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 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824
  1. /*
  2. * Copyright (c) 2006-2014 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.ClientLineAddedEvent;
  25. import com.dmdirc.interfaces.Connection;
  26. import com.dmdirc.interfaces.FrameCloseListener;
  27. import com.dmdirc.interfaces.FrameComponentChangeListener;
  28. import com.dmdirc.interfaces.FrameInfoListener;
  29. import com.dmdirc.interfaces.NotificationListener;
  30. import com.dmdirc.interfaces.config.AggregateConfigProvider;
  31. import com.dmdirc.interfaces.config.ConfigChangeListener;
  32. import com.dmdirc.messages.MessageSinkManager;
  33. import com.dmdirc.parser.common.CompositionState;
  34. import com.dmdirc.ui.Colour;
  35. import com.dmdirc.ui.IconManager;
  36. import com.dmdirc.ui.input.TabCompleter;
  37. import com.dmdirc.ui.messages.Formatter;
  38. import com.dmdirc.ui.messages.IRCDocument;
  39. import com.dmdirc.ui.messages.Styliser;
  40. import com.dmdirc.util.ChildEventBusManager;
  41. import com.dmdirc.util.URLBuilder;
  42. import com.dmdirc.util.collections.ListenerList;
  43. import com.google.common.base.Optional;
  44. import com.google.common.eventbus.EventBus;
  45. import java.util.ArrayList;
  46. import java.util.Collection;
  47. import java.util.Collections;
  48. import java.util.Date;
  49. import java.util.HashSet;
  50. import java.util.LinkedList;
  51. import java.util.List;
  52. import java.util.Set;
  53. import java.util.concurrent.CopyOnWriteArrayList;
  54. import javax.annotation.Nonnull;
  55. import javax.annotation.Nullable;
  56. import static com.google.common.base.Preconditions.checkState;
  57. /**
  58. * The frame container implements basic methods that should be present in all objects that handle a
  59. * frame.
  60. */
  61. public abstract class FrameContainer {
  62. /** Listeners not yet using ListenerSupport. */
  63. protected final ListenerList listeners = new ListenerList();
  64. /** The colour of our frame's notifications. */
  65. private Optional<Colour> notification = Optional.absent();
  66. /** The document used to store this container's content. */
  67. private IRCDocument document;
  68. /** The children of this frame. */
  69. private final Collection<FrameContainer> children = new CopyOnWriteArrayList<>();
  70. /** The parent of this frame. */
  71. private final Optional<FrameContainer> parent;
  72. /** The name of the icon being used for this container's frame. */
  73. private String icon;
  74. /** The name of this container. */
  75. private String name;
  76. /** The title of this container. */
  77. private String title;
  78. /** The config manager for this container. */
  79. private final AggregateConfigProvider configManager;
  80. /** The IconChanger for this container. */
  81. private final IconChanger changer = new IconChanger();
  82. /** The UI components that this frame requires. */
  83. private final Set<String> components;
  84. /** The styliser used by this container. */
  85. private Styliser styliser;
  86. /** Object used to synchronise styliser access. */
  87. private final Object styliserSync = new Object();
  88. /** Object used to synchronise styliser access. */
  89. private final Object documentSync = new Object();
  90. /** The manager to use to manage our event bus. */
  91. private final ChildEventBusManager eventBusManager;
  92. /** Event bus to dispatch events to. */
  93. private final EventBus eventBus;
  94. /** The icon manager to use for this container. */
  95. private final IconManager iconManager;
  96. /** Whether or not this container is writable. */
  97. private final boolean writable;
  98. /**
  99. * The command parser used for commands in this container.
  100. * <p>
  101. * Only defined if this container is {@link #writable}.
  102. */
  103. private final Optional<CommandParser> commandParser;
  104. /**
  105. * The manager to use to dispatch messages to sinks.
  106. * <p>
  107. * Only defined if this container is {@link #writable}.
  108. */
  109. private final Optional<MessageSinkManager> messageSinkManager;
  110. /**
  111. * The tab completer to use.
  112. * <p>
  113. * Only defined if this container is {@link #writable}.
  114. */
  115. private final Optional<TabCompleter> tabCompleter;
  116. /**
  117. * Instantiate new frame container.
  118. *
  119. * @param parent The parent of this frame container, if any.
  120. * @param icon The icon to use for this container
  121. * @param name The name of this container
  122. * @param title The title of this container
  123. * @param config The config manager for this container
  124. * @param urlBuilder The URL builder to use when finding icons.
  125. * @param eventBus The bus to dispatch events on.
  126. * @param components The UI components that this frame requires
  127. *
  128. * @since 0.6.4
  129. */
  130. protected FrameContainer(
  131. @Nullable final FrameContainer parent,
  132. final String icon,
  133. final String name,
  134. final String title,
  135. final AggregateConfigProvider config,
  136. final URLBuilder urlBuilder,
  137. final EventBus eventBus,
  138. final Collection<String> components) {
  139. this.parent = Optional.fromNullable(parent);
  140. this.configManager = config;
  141. this.name = name;
  142. this.title = title;
  143. this.components = new HashSet<>(components);
  144. this.iconManager = new IconManager(configManager, urlBuilder);
  145. this.writable = false;
  146. this.commandParser = Optional.absent();
  147. this.tabCompleter = Optional.absent();
  148. this.messageSinkManager = Optional.absent();
  149. this.eventBusManager = new ChildEventBusManager(eventBus);
  150. this.eventBusManager.connect();
  151. this.eventBus = eventBusManager.getChildBus();
  152. setIcon(icon);
  153. }
  154. /**
  155. * Instantiate new frame container that accepts user input.
  156. *
  157. * @param parent The parent of this frame container, if any.
  158. * @param icon The icon to use for this container
  159. * @param name The name of this container
  160. * @param title The title of this container
  161. * @param config The config manager for this container
  162. * @param urlBuilder The URL builder to use when finding icons.
  163. * @param commandParser The command parser to use for input.
  164. * @param tabCompleter The tab completer to use.
  165. * @param messageSinkManager The manager to use to dispatch notifications.
  166. * @param eventBus The bus to dispatch events on.
  167. * @param components The UI components that this frame requires
  168. *
  169. * @since 0.6.4
  170. */
  171. protected FrameContainer(
  172. @Nullable final FrameContainer parent,
  173. final String icon,
  174. final String name,
  175. final String title,
  176. final AggregateConfigProvider config,
  177. final URLBuilder urlBuilder,
  178. final CommandParser commandParser,
  179. final TabCompleter tabCompleter,
  180. final MessageSinkManager messageSinkManager,
  181. final EventBus eventBus,
  182. final Collection<String> components) {
  183. this.parent = Optional.fromNullable(parent);
  184. this.configManager = config;
  185. this.name = name;
  186. this.title = title;
  187. this.components = new HashSet<>(components);
  188. this.iconManager = new IconManager(configManager, urlBuilder);
  189. this.writable = true;
  190. this.commandParser = Optional.of(commandParser);
  191. this.tabCompleter = Optional.of(tabCompleter);
  192. this.messageSinkManager = Optional.of(messageSinkManager);
  193. commandParser.setOwner(this);
  194. this.eventBusManager = new ChildEventBusManager(eventBus);
  195. this.eventBusManager.connect();
  196. this.eventBus = eventBusManager.getChildBus();
  197. setIcon(icon);
  198. }
  199. public Optional<Colour> getNotification() {
  200. return notification;
  201. }
  202. public Optional<FrameContainer> getParent() {
  203. return parent;
  204. }
  205. public String getIcon() {
  206. return icon;
  207. }
  208. public String getName() {
  209. return name;
  210. }
  211. public String getTitle() {
  212. return title;
  213. }
  214. public AggregateConfigProvider getConfigManager() {
  215. return configManager;
  216. }
  217. public EventBus getEventBus() {
  218. return eventBus;
  219. }
  220. public boolean isWritable() {
  221. return writable;
  222. }
  223. /**
  224. * Returns a collection of direct children of this frame.
  225. *
  226. * @return This frame's children
  227. *
  228. * @since 0.6.4
  229. */
  230. public Collection<FrameContainer> getChildren() {
  231. return Collections.unmodifiableCollection(children);
  232. }
  233. /**
  234. * Adds a new child window to this frame.
  235. *
  236. * @param child The window to be added
  237. *
  238. * @since 0.6.4
  239. */
  240. public void addChild(final FrameContainer child) {
  241. children.add(child);
  242. }
  243. /**
  244. * Removes a child window from this frame.
  245. *
  246. * @param child The window to be removed
  247. *
  248. * @since 0.6.4
  249. */
  250. public void removeChild(final FrameContainer child) {
  251. children.remove(child);
  252. }
  253. /**
  254. * Gets an icon manager for this container.
  255. *
  256. * @return An icon manager for this container.
  257. */
  258. public IconManager getIconManager() {
  259. return iconManager;
  260. }
  261. /**
  262. * Retrieves the {@link IRCDocument} used to store this frame's content.
  263. *
  264. * @return This frame's document
  265. *
  266. * @since 0.6.4
  267. */
  268. public IRCDocument getDocument() {
  269. synchronized (documentSync) {
  270. if (document == null) {
  271. document = new IRCDocument(getConfigManager(), getStyliser());
  272. }
  273. return document;
  274. }
  275. }
  276. /**
  277. * Changes the name of this container, and notifies any {@link FrameInfoListener}s of the
  278. * change.
  279. *
  280. * @param name The new name for this frame.
  281. */
  282. protected void setName(final String name) {
  283. this.name = name;
  284. listeners.getCallable(FrameInfoListener.class).nameChanged(this, name);
  285. }
  286. /**
  287. * Changes the title of this container, and notifies any {@link FrameInfoListener}s of the
  288. * change.
  289. *
  290. * @param title The new title for this frame.
  291. */
  292. public void setTitle(final String title) {
  293. this.title = title;
  294. listeners.getCallable(FrameInfoListener.class).titleChanged(this, title);
  295. }
  296. /**
  297. * Returns the collection of UI component identifiers that this frame container requires for its
  298. * display.
  299. *
  300. * @since 0.6.6
  301. * @return Collection of UI component identifiers
  302. */
  303. public Set<String> getComponents() {
  304. return Collections.unmodifiableSet(components);
  305. }
  306. /**
  307. * Adds a new component to this container.
  308. *
  309. * @since 0.6.6
  310. * @param component The component to be added
  311. */
  312. public void addComponent(final String component) {
  313. components.add(component);
  314. for (FrameComponentChangeListener listener
  315. : listeners.get(FrameComponentChangeListener.class)) {
  316. listener.componentAdded(this, component);
  317. }
  318. }
  319. /**
  320. * Removes a component from this container.
  321. *
  322. * @since 0.6.6
  323. * @param component The component to be removed
  324. */
  325. public void removeComponent(final String component) {
  326. components.remove(component);
  327. for (FrameComponentChangeListener listener
  328. : listeners.get(FrameComponentChangeListener.class)) {
  329. listener.componentRemoved(this, component);
  330. }
  331. }
  332. /**
  333. * Closes this container (and its associated frame).
  334. */
  335. public void close() {
  336. eventBusManager.disconnect();
  337. for (FrameCloseListener listener : listeners.get(FrameCloseListener.class)) {
  338. listener.windowClosing(this);
  339. listeners.remove(FrameCloseListener.class, listener);
  340. }
  341. }
  342. /**
  343. * Returns the connection that this container is associated with.
  344. *
  345. * @return the associated connection, or {@code null}.
  346. */
  347. @Nonnull
  348. public abstract Connection getConnection();
  349. /**
  350. * Sets the icon to be used by this frame container.
  351. *
  352. * @param icon The new icon to be used
  353. */
  354. public final void setIcon(final String icon) {
  355. this.icon = icon;
  356. iconUpdated();
  357. configManager.removeListener(changer);
  358. configManager.addChangeListener("icon", icon, changer);
  359. }
  360. /**
  361. * Called when this container's icon is updated.
  362. */
  363. private void iconUpdated() {
  364. listeners.getCallable(FrameInfoListener.class).iconChanged(this, icon);
  365. }
  366. /**
  367. * Retrieves the styliser which should be used by this container.
  368. *
  369. * @return this container's styliser
  370. */
  371. public Styliser getStyliser() {
  372. synchronized (styliserSync) {
  373. if (styliser == null) {
  374. styliser = new Styliser(getConnection(), getConfigManager());
  375. }
  376. return styliser;
  377. }
  378. }
  379. /**
  380. * Clears any outstanding notifications this frame has set.
  381. */
  382. public void clearNotification() {
  383. notification = Optional.absent();
  384. listeners.getCallable(NotificationListener.class).notificationCleared(this);
  385. }
  386. /**
  387. * Sends a notification to the frame manager if this fame isn't active.
  388. *
  389. * @param colour The colour to use for the notification
  390. */
  391. public void sendNotification(final Colour colour) {
  392. if (!notification.isPresent() || !colour.equals(notification.get())) {
  393. notification = Optional.of(colour);
  394. listeners.getCallable(NotificationListener.class).notificationSet(this, colour);
  395. }
  396. }
  397. /**
  398. * Adds a line to this container's window. If the window is null for some reason, the line is
  399. * silently discarded.
  400. *
  401. * @param type The message type to use
  402. * @param timestamp The timestamp to use for this line
  403. * @param args The message's arguments
  404. *
  405. * @since 0.6.4
  406. */
  407. public void addLine(final String type, final Date timestamp,
  408. final Object... args) {
  409. if (type != null && !type.isEmpty()) {
  410. addLine(Formatter.formatMessage(getConfigManager(), type, args),
  411. timestamp);
  412. }
  413. }
  414. /**
  415. * Adds a line to this container's window. If the window is null for some reason, the line is
  416. * silently discarded.
  417. *
  418. * @param type The message type to use
  419. * @param args The message's arguments
  420. */
  421. public void addLine(final String type, final Object... args) {
  422. addLine(type, new Date(), args);
  423. }
  424. /**
  425. * Adds a line to this container's window. If the window is null for some reason, the line is
  426. * silently discarded.
  427. *
  428. * @param type The message type to use
  429. * @param timestamp The timestamp to use for this line
  430. * @param args The message's arguments
  431. *
  432. * @since 0.6.4
  433. */
  434. public void addLine(final StringBuffer type, final Date timestamp,
  435. final Object... args) {
  436. if (type != null) {
  437. addLine(type.toString(), timestamp, args);
  438. }
  439. }
  440. /**
  441. * Adds a line to this container's window. If the window is null for some reason, the line is
  442. * silently discarded.
  443. *
  444. * @param type The message type to use
  445. * @param args The message's arguments
  446. */
  447. public void addLine(final StringBuffer type, final Object... args) {
  448. addLine(type, new Date(), args);
  449. }
  450. /**
  451. * Adds the specified raw line to the window, without using a formatter.
  452. *
  453. * @param line The line to be added
  454. * @param timestamp Whether or not to display the timestamp for this line
  455. */
  456. public void addLine(final String line, final boolean timestamp) {
  457. addLine(line, timestamp ? new Date() : null);
  458. }
  459. /**
  460. * Adds the specified raw line to the window, without using a formatter, and using the specified
  461. * timestamp. If the timestamp is <code>null</code>, no timestamp is added.
  462. *
  463. * @param line The line to be added
  464. * @param timestamp The timestamp to use for the line
  465. *
  466. * @since 0.6.4
  467. */
  468. public void addLine(final String line, final Date timestamp) {
  469. final List<String[]> lines = new LinkedList<>();
  470. for (final String myLine : line.split("\n")) {
  471. if (timestamp != null) {
  472. lines.add(new String[]{
  473. Formatter.formatMessage(getConfigManager(), "timestamp",
  474. timestamp),
  475. myLine,});
  476. } else {
  477. lines.add(new String[]{myLine});
  478. }
  479. eventBus.post(new ClientLineAddedEvent(this, myLine));
  480. }
  481. getDocument().addText(lines);
  482. }
  483. /**
  484. * Adds a close listener for this frame container. Close listeners will only be called once,
  485. * even if the container is closed, re-added, and closed again.
  486. *
  487. * @param listener The listener to be added
  488. */
  489. public void addCloseListener(final FrameCloseListener listener) {
  490. listeners.add(FrameCloseListener.class, listener);
  491. }
  492. /**
  493. * Removes a close listener from this frame container.
  494. *
  495. * @since 0.6.5
  496. * @param listener The listener to be removed
  497. */
  498. public void removeCloseListener(final FrameCloseListener listener) {
  499. listeners.remove(FrameCloseListener.class, listener);
  500. }
  501. /**
  502. * Adds a component listener to this container.
  503. *
  504. * @since 0.6.6
  505. * @param listener The listener to be added
  506. */
  507. public void addComponentListener(final FrameComponentChangeListener listener) {
  508. listeners.add(FrameComponentChangeListener.class, listener);
  509. }
  510. /**
  511. * Removes a component listener from this container.
  512. *
  513. * @since 0.6.6
  514. * @param listener The listener to be removed
  515. */
  516. public void removeComponentListener(final FrameComponentChangeListener listener) {
  517. listeners.remove(FrameComponentChangeListener.class, listener);
  518. }
  519. /**
  520. * Adds a notification listener to this container.
  521. *
  522. * @param listener The listener to inform of notification events.
  523. */
  524. public void addNotificationListener(final NotificationListener listener) {
  525. listeners.add(NotificationListener.class, listener);
  526. }
  527. /**
  528. * Removes a notification listener from this container.
  529. *
  530. * @param listener The listener to be removed.
  531. */
  532. public void removeNotificationListener(final NotificationListener listener) {
  533. listeners.remove(NotificationListener.class, listener);
  534. }
  535. /**
  536. * Adds a frame info listener to this container.
  537. *
  538. * @param listener The listener to be informed of frame information changes.
  539. */
  540. public void addFrameInfoListener(final FrameInfoListener listener) {
  541. listeners.add(FrameInfoListener.class, listener);
  542. }
  543. /**
  544. * Removes a frame info listener from this container.
  545. *
  546. * @param listener The listener to be removed.
  547. */
  548. public void removeFrameInfoListener(final FrameInfoListener listener) {
  549. listeners.remove(FrameInfoListener.class, listener);
  550. }
  551. /**
  552. * Sends a line of text to this container's source.
  553. *
  554. * @param line The line to be sent
  555. */
  556. public void sendLine(final String line) {
  557. throw new UnsupportedOperationException("Container doesn't override sendLine");
  558. }
  559. /**
  560. * Retrieves the command parser to be used for this container.
  561. *
  562. * @return This container's command parser
  563. */
  564. public CommandParser getCommandParser() {
  565. checkState(writable);
  566. return commandParser.get();
  567. }
  568. /**
  569. * Retrieves the tab completer which should be used for this container.
  570. *
  571. * @return This container's tab completer
  572. */
  573. public TabCompleter getTabCompleter() {
  574. checkState(writable);
  575. return tabCompleter.get();
  576. }
  577. /**
  578. * Returns the maximum length that a line passed to sendLine() should be, in order to prevent it
  579. * being truncated or causing protocol violations.
  580. *
  581. * @return The maximum line length for this container
  582. */
  583. public int getMaxLineLength() {
  584. throw new UnsupportedOperationException("Container doesn't override getMaxLineLength");
  585. }
  586. /**
  587. * Splits the specified line into chunks that contain a number of bytes less than or equal to
  588. * the value returned by {@link #getMaxLineLength()}.
  589. *
  590. * @param line The line to be split
  591. *
  592. * @return An ordered list of chunks of the desired length
  593. */
  594. protected List<String> splitLine(final String line) {
  595. final List<String> result = new ArrayList<>();
  596. if (line.indexOf('\n') > -1) {
  597. for (String part : line.split("\n")) {
  598. result.addAll(splitLine(part));
  599. }
  600. } else {
  601. final StringBuilder remaining = new StringBuilder(line);
  602. while (getMaxLineLength() > -1 && remaining.toString().getBytes().length
  603. > getMaxLineLength()) {
  604. int number = Math.min(remaining.length(), getMaxLineLength());
  605. while (remaining.substring(0, number).getBytes().length > getMaxLineLength()) {
  606. number--;
  607. }
  608. result.add(remaining.substring(0, number));
  609. remaining.delete(0, number);
  610. }
  611. result.add(remaining.toString());
  612. }
  613. return result;
  614. }
  615. /**
  616. * Returns the number of lines that the specified string would be sent as.
  617. *
  618. * @param line The string to be split and sent
  619. *
  620. * @return The number of lines required to send the specified string
  621. */
  622. public final int getNumLines(final String line) {
  623. final String[] splitLines = line.split("(\n|\r\n|\r)", Integer.MAX_VALUE);
  624. int lines = 0;
  625. for (String splitLine : splitLines) {
  626. if (getMaxLineLength() <= 0) {
  627. lines++;
  628. } else {
  629. lines += (int) Math.ceil(splitLine.getBytes().length
  630. / (double) getMaxLineLength());
  631. }
  632. }
  633. return lines;
  634. }
  635. /**
  636. * Processes and displays a notification.
  637. *
  638. * @param messageType The name of the formatter to be used for the message
  639. * @param args The arguments for the message
  640. *
  641. * @return True if any further behaviour should be executed, false otherwise
  642. */
  643. public boolean doNotification(final String messageType, final Object... args) {
  644. return doNotification(new Date(), messageType, args);
  645. }
  646. /**
  647. * Processes and displays a notification.
  648. *
  649. * @param date The date/time at which the event occurred
  650. * @param messageType The name of the formatter to be used for the message
  651. * @param args The arguments for the message
  652. *
  653. * @return True if any further behaviour should be executed, false otherwise
  654. */
  655. public boolean doNotification(final Date date, final String messageType, final Object... args) {
  656. final List<Object> messageArgs = new ArrayList<>();
  657. final List<Object> actionArgs = new ArrayList<>();
  658. final StringBuffer buffer = new StringBuffer(messageType);
  659. actionArgs.add(this);
  660. for (Object arg : args) {
  661. actionArgs.add(arg);
  662. if (!processNotificationArg(arg, messageArgs)) {
  663. messageArgs.add(arg);
  664. }
  665. }
  666. modifyNotificationArgs(actionArgs, messageArgs);
  667. handleNotification(date, buffer.toString(), messageArgs.toArray());
  668. return true;
  669. }
  670. /**
  671. * Allows subclasses to modify the lists of arguments for notifications.
  672. *
  673. * @param actionArgs The list of arguments to be passed to the actions system
  674. * @param messageArgs The list of arguments to be passed to the formatter
  675. */
  676. protected void modifyNotificationArgs(final List<Object> actionArgs,
  677. final List<Object> messageArgs) {
  678. // Do nothing
  679. }
  680. /**
  681. * Allows subclasses to process specific types of notification arguments.
  682. *
  683. * @param arg The argument to be processed
  684. * @param args The list of arguments that any data should be appended to
  685. *
  686. * @return True if the arg has been processed, false otherwise
  687. */
  688. protected boolean processNotificationArg(final Object arg, final List<Object> args) {
  689. return false;
  690. }
  691. /**
  692. * Handles general server notifications (i.e., ones not tied to a specific window). The user can
  693. * select where the notifications should go in their config.
  694. *
  695. * @param messageType The type of message that is being sent
  696. * @param args The arguments for the message
  697. */
  698. public void handleNotification(final String messageType, final Object... args) {
  699. handleNotification(new Date(), messageType, args);
  700. }
  701. /**
  702. * Handles general server notifications (i.e., ones not tied to a specific window). The user can
  703. * select where the notifications should go in their config.
  704. *
  705. * @param date The date/time at which the event occurred
  706. * @param messageType The type of message that is being sent
  707. * @param args The arguments for the message
  708. */
  709. public void handleNotification(final Date date, final String messageType, final Object... args) {
  710. checkState(writable);
  711. messageSinkManager.get().dispatchMessage(this, date, messageType, args);
  712. }
  713. /**
  714. * Sets the composition state for the local user for this chat.
  715. *
  716. * @param state The new composition state
  717. */
  718. public void setCompositionState(final CompositionState state) {
  719. // Default implementation does nothing. Subclasses that support
  720. // composition should override this.
  721. }
  722. /**
  723. * Updates the icon of this frame if its config setting is changed.
  724. */
  725. private class IconChanger implements ConfigChangeListener {
  726. @Override
  727. public void configChanged(final String domain, final String key) {
  728. iconUpdated();
  729. }
  730. }
  731. }