Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

FrameContainer.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  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.actions.ActionManager;
  24. import com.dmdirc.actions.CoreActionType;
  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.ui.Colour;
  33. import com.dmdirc.ui.IconManager;
  34. import com.dmdirc.ui.messages.Formatter;
  35. import com.dmdirc.ui.messages.IRCDocument;
  36. import com.dmdirc.ui.messages.Styliser;
  37. import com.dmdirc.util.URLBuilder;
  38. import com.dmdirc.util.collections.ListenerList;
  39. import java.util.Collection;
  40. import java.util.Collections;
  41. import java.util.Date;
  42. import java.util.HashSet;
  43. import java.util.LinkedList;
  44. import java.util.List;
  45. import java.util.Set;
  46. import java.util.concurrent.CopyOnWriteArrayList;
  47. /**
  48. * The frame container implements basic methods that should be present in all objects that handle a
  49. * frame.
  50. */
  51. public abstract class FrameContainer {
  52. /** Listeners not yet using ListenerSupport. */
  53. protected final ListenerList listeners = new ListenerList();
  54. /** The colour of our frame's notifications. */
  55. private Colour notification = Colour.BLACK;
  56. /** The document used to store this container's content. */
  57. private IRCDocument document;
  58. /** The children of this frame. */
  59. private final Collection<FrameContainer> children = new CopyOnWriteArrayList<>();
  60. /** The parent of this frame. */
  61. private FrameContainer parent;
  62. /** The name of the icon being used for this container's frame. */
  63. private String icon;
  64. /** The name of this container. */
  65. private String name;
  66. /** The title of this container. */
  67. private String title;
  68. /** The config manager for this container. */
  69. private final AggregateConfigProvider configManager;
  70. /** The IconChanger for this container. */
  71. private final IconChanger changer = new IconChanger();
  72. /** The UI components that this frame requires. */
  73. private final Set<String> components;
  74. /** The styliser used by this container. */
  75. private Styliser styliser;
  76. /** Object used to synchronise styliser access. */
  77. private final Object styliserSync = new Object();
  78. /** Object used to synchronise styliser access. */
  79. private final Object documentSync = new Object();
  80. /** The icon manager to use for this container. */
  81. private final IconManager iconManager;
  82. /**
  83. * Instantiate new frame container.
  84. *
  85. * @param icon The icon to use for this container
  86. * @param name The name of this container
  87. * @param title The title of this container
  88. * @param config The config manager for this container
  89. * @param urlBuilder The URL builder to use when finding icons.
  90. * @param components The UI components that this frame requires
  91. *
  92. * @since 0.6.4
  93. */
  94. protected FrameContainer(
  95. final String icon,
  96. final String name,
  97. final String title,
  98. final AggregateConfigProvider config,
  99. final URLBuilder urlBuilder,
  100. final Collection<String> components) {
  101. this.configManager = config;
  102. this.name = name;
  103. this.title = title;
  104. this.components = new HashSet<>(components);
  105. this.iconManager = new IconManager(configManager, urlBuilder);
  106. setIcon(icon);
  107. }
  108. public Colour getNotification() {
  109. return notification;
  110. }
  111. public FrameContainer getParent() {
  112. return parent;
  113. }
  114. public String getIcon() {
  115. return icon;
  116. }
  117. public String getName() {
  118. return name;
  119. }
  120. public String getTitle() {
  121. return title;
  122. }
  123. public AggregateConfigProvider getConfigManager() {
  124. return configManager;
  125. }
  126. /**
  127. * Returns a collection of direct children of this frame.
  128. *
  129. * @return This frame's children
  130. *
  131. * @since 0.6.4
  132. */
  133. public Collection<FrameContainer> getChildren() {
  134. return Collections.unmodifiableCollection(children);
  135. }
  136. /**
  137. * Determines whether the specified target is a child of this container. Children may be
  138. * indirect (i.e., a child of another child).
  139. *
  140. * @param target The window to be tested
  141. *
  142. * @return True if the specified container is a child of this one
  143. */
  144. public boolean isChild(final FrameContainer target) {
  145. if (children.contains(target)) {
  146. return true;
  147. }
  148. for (FrameContainer child : children) {
  149. if (child.isChild(target)) {
  150. return true;
  151. }
  152. }
  153. return false;
  154. }
  155. /**
  156. * Adds a new child window to this frame.
  157. *
  158. * @param child The window to be added
  159. *
  160. * @since 0.6.4
  161. */
  162. public void addChild(final FrameContainer child) {
  163. children.add(child);
  164. child.setParent(this);
  165. }
  166. /**
  167. * Removes a child window from this frame.
  168. *
  169. * @param child The window to be removed
  170. *
  171. * @since 0.6.4
  172. */
  173. public void removeChild(final FrameContainer child) {
  174. children.remove(child);
  175. }
  176. /**
  177. * Sets the parent of this container to the one specified. If this container already had a
  178. * parent, it will deregister itself with the old parent.
  179. *
  180. * @param parent The new parent for this container
  181. *
  182. * @since 0.6.4
  183. */
  184. public synchronized void setParent(final FrameContainer parent) {
  185. if (this.parent != null && (parent == null || !parent.equals(this.parent))) {
  186. this.parent.removeChild(this);
  187. }
  188. this.parent = parent;
  189. }
  190. /**
  191. * Gets an icon manager for this container.
  192. *
  193. * @return An icon manager for this container.
  194. */
  195. public IconManager getIconManager() {
  196. return iconManager;
  197. }
  198. /**
  199. * Gets an icon manager for this container.
  200. *
  201. * @param urlBuilder The builder to use to construct icon URLs.
  202. *
  203. * @return An icon manager for this container.
  204. *
  205. * @deprecated Don't bother passing in a URL builder. That was a bad idea.
  206. */
  207. @Deprecated
  208. public IconManager getIconManager(final URLBuilder urlBuilder) {
  209. return iconManager;
  210. }
  211. /**
  212. * Retrieves the {@link IRCDocument} used to store this frame's content.
  213. *
  214. * @return This frame's document
  215. *
  216. * @since 0.6.4
  217. */
  218. public IRCDocument getDocument() {
  219. synchronized (documentSync) {
  220. if (document == null) {
  221. document = new IRCDocument(getConfigManager(), getStyliser());
  222. }
  223. return document;
  224. }
  225. }
  226. /**
  227. * Changes the name of this container, and notifies any {@link FrameInfoListener}s of the
  228. * change.
  229. *
  230. * @param name The new name for this frame.
  231. */
  232. protected void setName(final String name) {
  233. this.name = name;
  234. listeners.getCallable(FrameInfoListener.class).nameChanged(this, name);
  235. }
  236. /**
  237. * Changes the title of this container, and notifies any {@link FrameInfoListener}s of the
  238. * change.
  239. *
  240. * @param title The new title for this frame.
  241. */
  242. public void setTitle(final String title) {
  243. this.title = title;
  244. listeners.getCallable(FrameInfoListener.class).titleChanged(this, title);
  245. }
  246. /**
  247. * Returns the collection of UI component identifiers that this frame container requires for its
  248. * display.
  249. *
  250. * @since 0.6.6
  251. * @return Collection of UI component identifiers
  252. */
  253. public Set<String> getComponents() {
  254. return Collections.unmodifiableSet(components);
  255. }
  256. /**
  257. * Adds a new component to this container.
  258. *
  259. * @since 0.6.6
  260. * @param component The component to be added
  261. */
  262. public void addComponent(final String component) {
  263. components.add(component);
  264. for (FrameComponentChangeListener listener
  265. : listeners.get(FrameComponentChangeListener.class)) {
  266. listener.componentAdded(this, component);
  267. }
  268. }
  269. /**
  270. * Removes a component from this container.
  271. *
  272. * @since 0.6.6
  273. * @param component The component to be removed
  274. */
  275. public void removeComponent(final String component) {
  276. components.remove(component);
  277. for (FrameComponentChangeListener listener
  278. : listeners.get(FrameComponentChangeListener.class)) {
  279. listener.componentRemoved(this, component);
  280. }
  281. }
  282. /**
  283. * Closes this container (and it's associated frame).
  284. */
  285. public void close() {
  286. for (FrameCloseListener listener : listeners.get(FrameCloseListener.class)) {
  287. listener.windowClosing(this);
  288. }
  289. }
  290. /**
  291. * Returns the connection that this container is associated with.
  292. *
  293. * @return the associated connection, or {@code null}.
  294. */
  295. public abstract Connection getConnection();
  296. /**
  297. * Sets the icon to be used by this frame container.
  298. *
  299. * @param icon The new icon to be used
  300. */
  301. public final void setIcon(final String icon) {
  302. this.icon = icon;
  303. iconUpdated();
  304. configManager.removeListener(changer);
  305. configManager.addChangeListener("icon", icon, changer);
  306. }
  307. /**
  308. * Called when this container's icon is updated.
  309. */
  310. private void iconUpdated() {
  311. listeners.getCallable(FrameInfoListener.class).iconChanged(this, icon);
  312. }
  313. /**
  314. * Retrieves the styliser which should be used by this container.
  315. *
  316. * @return this container's styliser
  317. */
  318. public Styliser getStyliser() {
  319. synchronized (styliserSync) {
  320. if (styliser == null) {
  321. styliser = new Styliser(getConnection(), getConfigManager());
  322. }
  323. return styliser;
  324. }
  325. }
  326. /**
  327. * Clears any outstanding notifications this frame has set.
  328. */
  329. public void clearNotification() {
  330. // TODO: This should default ot something colour independent
  331. notification = Colour.BLACK;
  332. listeners.getCallable(NotificationListener.class).notificationCleared(this);
  333. }
  334. /**
  335. * Sends a notification to the frame manager if this fame isn't active.
  336. *
  337. * @param colour The colour to use for the notification
  338. */
  339. public void sendNotification(final Colour colour) {
  340. if (!colour.equals(notification)) {
  341. notification = colour;
  342. listeners.getCallable(NotificationListener.class).notificationSet(this, colour);
  343. }
  344. }
  345. /**
  346. * Adds a line to this container's window. If the window is null for some reason, the line is
  347. * silently discarded.
  348. *
  349. * @param type The message type to use
  350. * @param timestamp The timestamp to use for this line
  351. * @param args The message's arguments
  352. *
  353. * @since 0.6.4
  354. */
  355. public void addLine(final String type, final Date timestamp,
  356. final Object... args) {
  357. if (type != null && !type.isEmpty()) {
  358. addLine(Formatter.formatMessage(getConfigManager(), type, args),
  359. timestamp);
  360. }
  361. }
  362. /**
  363. * Adds a line to this container's window. If the window is null for some reason, the line is
  364. * silently discarded.
  365. *
  366. * @param type The message type to use
  367. * @param args The message's arguments
  368. */
  369. public void addLine(final String type, final Object... args) {
  370. addLine(type, new Date(), args);
  371. }
  372. /**
  373. * Adds a line to this container's window. If the window is null for some reason, the line is
  374. * silently discarded.
  375. *
  376. * @param type The message type to use
  377. * @param timestamp The timestamp to use for this line
  378. * @param args The message's arguments
  379. *
  380. * @since 0.6.4
  381. */
  382. public void addLine(final StringBuffer type, final Date timestamp,
  383. final Object... args) {
  384. if (type != null) {
  385. addLine(type.toString(), timestamp, args);
  386. }
  387. }
  388. /**
  389. * Adds a line to this container's window. If the window is null for some reason, the line is
  390. * silently discarded.
  391. *
  392. * @param type The message type to use
  393. * @param args The message's arguments
  394. */
  395. public void addLine(final StringBuffer type, final Object... args) {
  396. addLine(type, new Date(), args);
  397. }
  398. /**
  399. * Adds the specified raw line to the window, without using a formatter.
  400. *
  401. * @param line The line to be added
  402. * @param timestamp Whether or not to display the timestamp for this line
  403. */
  404. public void addLine(final String line, final boolean timestamp) {
  405. addLine(line, timestamp ? new Date() : null);
  406. }
  407. /**
  408. * Adds the specified raw line to the window, without using a formatter, and using the specified
  409. * timestamp. If the timestamp is
  410. * <code>null</code>, no timestamp is added.
  411. *
  412. * @param line The line to be added
  413. * @param timestamp The timestamp to use for the line
  414. *
  415. * @since 0.6.4
  416. */
  417. public void addLine(final String line, final Date timestamp) {
  418. final List<String[]> lines = new LinkedList<>();
  419. for (final String myLine : line.split("\n")) {
  420. if (timestamp != null) {
  421. lines.add(new String[]{
  422. Formatter.formatMessage(getConfigManager(), "timestamp",
  423. timestamp),
  424. myLine,});
  425. } else {
  426. lines.add(new String[]{
  427. myLine,});
  428. }
  429. ActionManager.getActionManager().triggerEvent(
  430. CoreActionType.CLIENT_LINE_ADDED, null, this, myLine);
  431. }
  432. getDocument().addText(lines);
  433. }
  434. /**
  435. * Adds a close listener for this frame container.
  436. *
  437. * @since 0.6.5
  438. * @param listener The listener to be added
  439. */
  440. public void addCloseListener(final FrameCloseListener listener) {
  441. listeners.add(FrameCloseListener.class, listener);
  442. }
  443. /**
  444. * Removes a close listener from this frame container.
  445. *
  446. * @since 0.6.5
  447. * @param listener The listener to be removed
  448. */
  449. public void removeCloseListener(final FrameCloseListener listener) {
  450. listeners.remove(FrameCloseListener.class, listener);
  451. }
  452. /**
  453. * Adds a component listener to this container.
  454. *
  455. * @since 0.6.6
  456. * @param listener The listener to be added
  457. */
  458. public void addComponentListener(final FrameComponentChangeListener listener) {
  459. listeners.add(FrameComponentChangeListener.class, listener);
  460. }
  461. /**
  462. * Removes a component listener from this container.
  463. *
  464. * @since 0.6.6
  465. * @param listener The listener to be removed
  466. */
  467. public void removeComponentListener(final FrameComponentChangeListener listener) {
  468. listeners.remove(FrameComponentChangeListener.class, listener);
  469. }
  470. /**
  471. * Adds a notification listener to this container.
  472. *
  473. * @param listener The listener to inform of notification events.
  474. */
  475. public void addNotificationListener(final NotificationListener listener) {
  476. listeners.add(NotificationListener.class, listener);
  477. }
  478. /**
  479. * Removes a notification listener from this container.
  480. *
  481. * @param listener The listener to be removed.
  482. */
  483. public void removeNotificationListener(final NotificationListener listener) {
  484. listeners.remove(NotificationListener.class, listener);
  485. }
  486. /**
  487. * Adds a frame info listener to this container.
  488. *
  489. * @param listener The listener to be informed of frame information changes.
  490. */
  491. public void addFrameInfoListener(final FrameInfoListener listener) {
  492. listeners.add(FrameInfoListener.class, listener);
  493. }
  494. /**
  495. * Removes a frame info listener from this container.
  496. *
  497. * @param listener The listener to be removed.
  498. */
  499. public void removeFrameInfoListener(final FrameInfoListener listener) {
  500. listeners.remove(FrameInfoListener.class, listener);
  501. }
  502. /**
  503. * Updates the icon of this frame if its config setting is changed.
  504. */
  505. private class IconChanger implements ConfigChangeListener {
  506. /** {@inheritDoc} */
  507. @Override
  508. public void configChanged(final String domain, final String key) {
  509. iconUpdated();
  510. }
  511. }
  512. }