You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FrameContainer.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. /*
  2. * Copyright (c) 2006-2015 DMDirc Developers
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc;
  23. import com.dmdirc.commandparser.parsers.CommandParser;
  24. import com.dmdirc.events.FrameClosingEvent;
  25. import com.dmdirc.events.FrameComponentAddedEvent;
  26. import com.dmdirc.events.FrameComponentRemovedEvent;
  27. import com.dmdirc.events.FrameIconChangedEvent;
  28. import com.dmdirc.events.FrameNameChangedEvent;
  29. import com.dmdirc.events.FrameTitleChangedEvent;
  30. import com.dmdirc.interfaces.InputModel;
  31. import com.dmdirc.interfaces.WindowModel;
  32. import com.dmdirc.interfaces.config.AggregateConfigProvider;
  33. import com.dmdirc.interfaces.config.ConfigChangeListener;
  34. import com.dmdirc.parser.common.CompositionState;
  35. import com.dmdirc.ui.input.TabCompleter;
  36. import com.dmdirc.ui.messages.BackBuffer;
  37. import com.dmdirc.ui.messages.BackBufferFactory;
  38. import com.dmdirc.ui.messages.UnreadStatusManager;
  39. import com.dmdirc.util.ChildEventBusManager;
  40. import com.dmdirc.util.collections.ListenerList;
  41. import java.util.ArrayList;
  42. import java.util.Collection;
  43. import java.util.Collections;
  44. import java.util.HashSet;
  45. import java.util.List;
  46. import java.util.Optional;
  47. import java.util.Set;
  48. import static com.google.common.base.Preconditions.checkState;
  49. /**
  50. * The frame container implements basic methods that should be present in all objects that handle a
  51. * frame.
  52. */
  53. public abstract class FrameContainer implements WindowModel, InputModel {
  54. /** Listeners not yet using ListenerSupport. */
  55. protected final ListenerList listeners = new ListenerList();
  56. /** The name of the icon being used for this container's frame. */
  57. private String icon;
  58. /** The name of this container. */
  59. private String name;
  60. /** The title of this container. */
  61. private String title;
  62. /** The config manager for this container. */
  63. private final AggregateConfigProvider configManager;
  64. /** The IconChanger for this container. */
  65. private final ConfigChangeListener changer = (d, k) -> iconUpdated();
  66. /** The UI components that this frame requires. */
  67. private final Set<String> components;
  68. /** The manager to use to manage our event bus. */
  69. private final ChildEventBusManager eventBusManager;
  70. /** Event bus to dispatch events to. */
  71. private final DMDircMBassador eventBus;
  72. /** The manager handling this frame's unread status. */
  73. private final UnreadStatusManager unreadStatusManager;
  74. /** Whether or not this container is writable. */
  75. private final boolean writable;
  76. /** The back buffer factory. */
  77. private final BackBufferFactory backBufferFactory;
  78. /** The back buffer for this container. */
  79. private BackBuffer backBuffer;
  80. /**
  81. * The tab completer to use.
  82. * <p>
  83. * Only defined if this container is {@link #writable}.
  84. */
  85. private final Optional<TabCompleter> tabCompleter;
  86. /**
  87. * The command parser used for commands in this container.
  88. * <p>
  89. * Only defined if this container is {@link #writable}.
  90. */
  91. private Optional<CommandParser> commandParser = Optional.empty();
  92. /**
  93. * Instantiate new frame container.
  94. */
  95. protected FrameContainer(
  96. final String icon,
  97. final String name,
  98. final String title,
  99. final AggregateConfigProvider config,
  100. final BackBufferFactory backBufferFactory,
  101. final DMDircMBassador eventBus,
  102. final Collection<String> components) {
  103. this.configManager = config;
  104. this.name = name;
  105. this.title = title;
  106. this.components = new HashSet<>(components);
  107. this.writable = false;
  108. this.tabCompleter = Optional.empty();
  109. this.backBufferFactory = backBufferFactory;
  110. eventBusManager = new ChildEventBusManager(eventBus);
  111. eventBusManager.connect();
  112. this.eventBus = eventBusManager.getChildBus();
  113. this.unreadStatusManager = new UnreadStatusManager(this);
  114. this.eventBus.subscribe(unreadStatusManager);
  115. configManager.getBinder().bind(unreadStatusManager, UnreadStatusManager.class);
  116. setIcon(icon);
  117. }
  118. /**
  119. * Instantiate new frame container that accepts user input.
  120. */
  121. protected FrameContainer(
  122. final String icon,
  123. final String name,
  124. final String title,
  125. final AggregateConfigProvider config,
  126. final BackBufferFactory backBufferFactory,
  127. final TabCompleter tabCompleter,
  128. final DMDircMBassador eventBus,
  129. final Collection<String> components) {
  130. this.configManager = config;
  131. this.name = name;
  132. this.title = title;
  133. this.components = new HashSet<>(components);
  134. this.writable = true;
  135. this.tabCompleter = Optional.of(tabCompleter);
  136. this.backBufferFactory = backBufferFactory;
  137. eventBusManager = new ChildEventBusManager(eventBus);
  138. eventBusManager.connect();
  139. this.eventBus = eventBusManager.getChildBus();
  140. this.unreadStatusManager = new UnreadStatusManager(this);
  141. this.eventBus.subscribe(unreadStatusManager);
  142. configManager.getBinder().bind(unreadStatusManager, UnreadStatusManager.class);
  143. setIcon(icon);
  144. }
  145. protected void initBackBuffer() {
  146. backBuffer = backBufferFactory.getBackBuffer(this);
  147. backBuffer.startAddingEvents();
  148. }
  149. public void setCommandParser(final CommandParser commandParser) {
  150. this.commandParser = Optional.ofNullable(commandParser);
  151. }
  152. @Override
  153. public String getIcon() {
  154. return icon;
  155. }
  156. @Override
  157. public String getName() {
  158. return name;
  159. }
  160. @Override
  161. public String getTitle() {
  162. return title;
  163. }
  164. @Override
  165. public AggregateConfigProvider getConfigManager() {
  166. return configManager;
  167. }
  168. @Override
  169. public DMDircMBassador getEventBus() {
  170. return eventBus;
  171. }
  172. /**
  173. * Changes the name of this container, and fires a {@link FrameNameChangedEvent}.
  174. *
  175. * @param name The new name for this frame.
  176. */
  177. protected void setName(final String name) {
  178. this.name = name;
  179. eventBus.publishAsync(new FrameNameChangedEvent(this, name));
  180. }
  181. @Override
  182. public void setTitle(final String title) {
  183. this.title = title;
  184. eventBus.publishAsync(new FrameTitleChangedEvent(this, title));
  185. }
  186. @Override
  187. public Set<String> getComponents() {
  188. return Collections.unmodifiableSet(components);
  189. }
  190. @Override
  191. public void addComponent(final String component) {
  192. components.add(component);
  193. eventBus.publishAsync(new FrameComponentAddedEvent(this, component));
  194. }
  195. @Override
  196. public void removeComponent(final String component) {
  197. components.remove(component);
  198. eventBus.publishAsync(new FrameComponentRemovedEvent(this, component));
  199. }
  200. @Override
  201. public void close() {
  202. eventBus.unsubscribe(unreadStatusManager);
  203. configManager.getBinder().unbind(unreadStatusManager);
  204. eventBus.publish(new FrameClosingEvent(this));
  205. eventBusManager.disconnect();
  206. getBackBuffer().stopAddingEvents();
  207. }
  208. @Override
  209. public final void setIcon(final String icon) {
  210. this.icon = icon;
  211. iconUpdated();
  212. configManager.removeListener(changer);
  213. configManager.addChangeListener("icon", icon, changer);
  214. }
  215. /**
  216. * Called when this container's icon is updated.
  217. */
  218. private void iconUpdated() {
  219. eventBus.publish(new FrameIconChangedEvent(this, icon));
  220. }
  221. @Override
  222. public BackBuffer getBackBuffer() {
  223. return backBuffer;
  224. }
  225. @Override
  226. public void sendLine(final String line) {
  227. throw new UnsupportedOperationException("Container doesn't override sendLine");
  228. }
  229. @Override
  230. public CommandParser getCommandParser() {
  231. checkState(writable);
  232. return commandParser.get();
  233. }
  234. @Override
  235. public TabCompleter getTabCompleter() {
  236. checkState(writable);
  237. return tabCompleter.get();
  238. }
  239. @Override
  240. public int getMaxLineLength() {
  241. throw new UnsupportedOperationException("Container doesn't override getMaxLineLength");
  242. }
  243. /**
  244. * Splits the specified line into chunks that contain a number of bytes less than or equal to
  245. * the value returned by {@link #getMaxLineLength()}.
  246. *
  247. * @param line The line to be split
  248. *
  249. * @return An ordered list of chunks of the desired length
  250. */
  251. protected List<String> splitLine(final String line) {
  252. final List<String> result = new ArrayList<>();
  253. if (line.indexOf('\n') > -1) {
  254. for (String part : line.split("\n")) {
  255. result.addAll(splitLine(part));
  256. }
  257. } else {
  258. final StringBuilder remaining = new StringBuilder(line);
  259. while (getMaxLineLength() > -1 && remaining.toString().getBytes().length
  260. > getMaxLineLength()) {
  261. int number = Math.min(remaining.length(), getMaxLineLength());
  262. while (remaining.substring(0, number).getBytes().length > getMaxLineLength()) {
  263. number--;
  264. }
  265. result.add(remaining.substring(0, number));
  266. remaining.delete(0, number);
  267. }
  268. result.add(remaining.toString());
  269. }
  270. return result;
  271. }
  272. @Override
  273. public final int getNumLines(final String line) {
  274. final String[] splitLines = line.split("(\n|\r\n|\r)", Integer.MAX_VALUE);
  275. int lines = 0;
  276. for (String splitLine : splitLines) {
  277. if (getMaxLineLength() <= 0) {
  278. lines++;
  279. } else {
  280. lines += (int) Math.ceil(splitLine.getBytes().length
  281. / (double) getMaxLineLength());
  282. }
  283. }
  284. return lines;
  285. }
  286. /**
  287. * Sets the composition state for the local user for this chat.
  288. *
  289. * @param state The new composition state
  290. */
  291. public void setCompositionState(final CompositionState state) {
  292. // Default implementation does nothing. Subclasses that support
  293. // composition should override this.
  294. }
  295. @Override
  296. public Optional<InputModel> getInputModel() {
  297. if (writable) {
  298. return Optional.of(this);
  299. } else {
  300. return Optional.empty();
  301. }
  302. }
  303. @Override
  304. public UnreadStatusManager getUnreadStatusManager() {
  305. return unreadStatusManager;
  306. }
  307. }