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.

WindowManager.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  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.ui;
  23. import com.dmdirc.CustomWindow;
  24. import com.dmdirc.FrameContainer;
  25. import com.dmdirc.Precondition;
  26. import com.dmdirc.interfaces.FrameCloseListener;
  27. import com.dmdirc.interfaces.ui.FrameListener;
  28. import com.dmdirc.util.collections.ListenerList;
  29. import com.google.common.base.Optional;
  30. import java.util.Collection;
  31. import java.util.Collections;
  32. import java.util.List;
  33. import java.util.concurrent.CopyOnWriteArrayList;
  34. import javax.inject.Inject;
  35. import javax.inject.Singleton;
  36. import static com.google.common.base.Preconditions.checkArgument;
  37. import static com.google.common.base.Preconditions.checkNotNull;
  38. import static com.google.common.base.Preconditions.checkState;
  39. /**
  40. * The WindowManager maintains a list of all open windows, and their parent/child relations.
  41. */
  42. @Singleton
  43. public class WindowManager {
  44. /** A list of root windows. */
  45. private final List<FrameContainer> rootWindows = new CopyOnWriteArrayList<>();
  46. /** A list of frame listeners. */
  47. private final ListenerList listeners = new ListenerList();
  48. /** Listener for frame close events. */
  49. private final FrameCloseListener closeListener = new CloseListener();
  50. /**
  51. * Creates a new instance of {@link WindowManager}.
  52. */
  53. @Inject
  54. public WindowManager() {
  55. }
  56. /**
  57. * Registers a FrameListener with the WindowManager.
  58. *
  59. * @param frameListener The frame listener to be registered
  60. */
  61. @Precondition("The specified FrameListener is not null")
  62. public void addListener(final FrameListener frameListener) {
  63. checkNotNull(frameListener);
  64. listeners.add(FrameListener.class, frameListener);
  65. }
  66. /**
  67. * Registers a FrameListener with the WindowManager, and then calls the relevant methods on it
  68. * for all existing windows.
  69. *
  70. * @param frameListener The frame listener to be registered
  71. *
  72. * @since 0.6.6
  73. */
  74. @Precondition("The specified FrameListener is not null")
  75. public void addListenerAndSync(final FrameListener frameListener) {
  76. addListener(frameListener);
  77. for (FrameContainer root : rootWindows) {
  78. frameListener.addWindow(root, true);
  79. for (FrameContainer child : root.getChildren()) {
  80. fireAddWindow(frameListener, root, child);
  81. }
  82. }
  83. }
  84. /**
  85. * Recursively fires the addWindow callback for the specified windows and listener.
  86. *
  87. * @param listener The listener to be fired
  88. * @param parent The parent window
  89. * @param child The new child window that was added
  90. *
  91. */
  92. private void fireAddWindow(final FrameListener listener,
  93. final FrameContainer parent, final FrameContainer child) {
  94. listener.addWindow(parent, child, true);
  95. for (FrameContainer grandchild : child.getChildren()) {
  96. fireAddWindow(listener, child, grandchild);
  97. }
  98. }
  99. /**
  100. * Unregisters a FrameListener with the WindowManager.
  101. *
  102. * @param frameListener The frame listener to be removed
  103. */
  104. public void removeListener(final FrameListener frameListener) {
  105. listeners.remove(FrameListener.class, frameListener);
  106. }
  107. /**
  108. * Adds a new root window to the Window Manager.
  109. *
  110. * @param window The window to be added
  111. *
  112. * @since 0.6.4
  113. */
  114. @Precondition({
  115. "The specified Window is not null",
  116. "The specified Window has not already been added"
  117. })
  118. public void addWindow(final FrameContainer window) {
  119. addWindow(window, true);
  120. }
  121. /**
  122. * Adds a new root window to the Window Manager.
  123. *
  124. * @param window The window to be added
  125. * @param focus Should this window become focused
  126. *
  127. * @since 0.6.4
  128. */
  129. @Precondition({
  130. "The specified Window is not null",
  131. "The specified Window has not already been added"
  132. })
  133. public void addWindow(final FrameContainer window, final boolean focus) {
  134. checkNotNull(window);
  135. checkArgument(!rootWindows.contains(window));
  136. rootWindows.add(window);
  137. fireAddWindow(window, focus);
  138. window.addCloseListener(closeListener);
  139. }
  140. /**
  141. * Adds a new child window to the Window Manager.
  142. *
  143. * @param parent The parent window
  144. * @param child The child window to be added
  145. *
  146. * @since 0.6.4
  147. */
  148. @Precondition("The specified Windows are not null")
  149. public void addWindow(final FrameContainer parent,
  150. final FrameContainer child) {
  151. addWindow(parent, child, true);
  152. }
  153. /**
  154. * Adds a new child window to the Window Manager.
  155. *
  156. * @param parent The parent window
  157. * @param child The child window to be added
  158. * @param focus Should this window become focused
  159. *
  160. * @since 0.6.4
  161. */
  162. @Precondition({
  163. "The specified containers are not null",
  164. "The specified parent is in the window hierarchy already",
  165. "The specified child is NOT in the window hierarchy already"
  166. })
  167. public void addWindow(final FrameContainer parent,
  168. final FrameContainer child, final boolean focus) {
  169. checkNotNull(parent);
  170. checkArgument(isInHierarchy(parent));
  171. checkNotNull(child);
  172. parent.addChild(child);
  173. fireAddWindow(parent, child, focus);
  174. child.addCloseListener(closeListener);
  175. }
  176. /**
  177. * Recursively determines if the specified target is in the known hierarchy of containers. That
  178. * is, whether or not the specified target or any of its parents are root windows.
  179. *
  180. * @since 0.6.4
  181. * @param target The container to be tested
  182. *
  183. * @return True if the target is in the hierarchy, false otherwise
  184. */
  185. protected boolean isInHierarchy(final FrameContainer target) {
  186. if (rootWindows.contains(target)) {
  187. return true;
  188. }
  189. final Optional<FrameContainer> parent = target.getParent();
  190. return parent.isPresent() && isInHierarchy(parent.get());
  191. }
  192. /**
  193. * Removes a window from the Window Manager. If the specified window has child windows, they are
  194. * recursively removed before the target window. If the window hasn't previously been added, the
  195. * request to remove it is ignored.
  196. *
  197. * @param window The window to be removed
  198. *
  199. * @since 0.6.4
  200. */
  201. @Precondition({
  202. "The specified window is not null",
  203. "The specified window is in the window hierarchy"
  204. })
  205. public void removeWindow(final FrameContainer window) {
  206. checkNotNull(window != null);
  207. checkArgument(isInHierarchy(window));
  208. for (FrameContainer child : window.getChildren()) {
  209. child.close();
  210. }
  211. if (rootWindows.contains(window)) {
  212. fireDeleteWindow(window);
  213. rootWindows.remove(window);
  214. } else {
  215. final Optional<FrameContainer> parent = window.getParent();
  216. checkState(parent.isPresent());
  217. fireDeleteWindow(parent.get(), window);
  218. parent.get().removeChild(window);
  219. }
  220. }
  221. /**
  222. * Finds and returns a global custom window with the specified name. If a custom window with the
  223. * specified name isn't found, null is returned.
  224. *
  225. * @param name The name of the custom window to search for
  226. *
  227. * @return The specified custom window, or null
  228. */
  229. @Precondition("The specified window name is not null")
  230. public FrameContainer findCustomWindow(final String name) {
  231. checkNotNull(name);
  232. return findCustomWindow(rootWindows, name);
  233. }
  234. /**
  235. * Finds and returns a non-global custom window with the specified name. If a custom window with
  236. * the specified name isn't found, null is returned.
  237. *
  238. * @param parent The parent whose children should be searched
  239. * @param name The name of the custom window to search for
  240. *
  241. * @return The specified custom window, or null
  242. */
  243. @Precondition({
  244. "The specified window name is not null",
  245. "The specified parent window is not null",
  246. "The specified parent window has been added to the Window Manager"
  247. })
  248. public FrameContainer findCustomWindow(final FrameContainer parent, final String name) {
  249. checkNotNull(parent);
  250. checkNotNull(name);
  251. return findCustomWindow(parent.getChildren(), name);
  252. }
  253. /**
  254. * Finds a custom window with the specified name among the specified list of windows. If the
  255. * custom window is not found, returns null.
  256. *
  257. * @param windows The list of windows to search
  258. * @param name The name of the custom window to search for
  259. *
  260. * @return The custom window if found, or null otherwise
  261. */
  262. private FrameContainer findCustomWindow(final Collection<FrameContainer> windows,
  263. final String name) {
  264. for (FrameContainer window : windows) {
  265. if (window instanceof CustomWindow
  266. && window.getName().equals(name)) {
  267. return window;
  268. }
  269. }
  270. return null;
  271. }
  272. /**
  273. * Retrieves all known root (parent-less) windows.
  274. *
  275. * @since 0.6.4
  276. * @return A collection of all known root windows.
  277. */
  278. public Collection<FrameContainer> getRootWindows() {
  279. return Collections.unmodifiableCollection(rootWindows);
  280. }
  281. /**
  282. * Fires the addWindow(Window) callback.
  283. *
  284. * @param window The window that was added
  285. * @param focus Should this window become focused
  286. */
  287. private void fireAddWindow(final FrameContainer window, final boolean focus) {
  288. for (FrameListener listener : listeners.get(FrameListener.class)) {
  289. listener.addWindow(window, focus);
  290. }
  291. }
  292. /**
  293. * Fires the addWindow(Window, Window) callback.
  294. *
  295. * @param parent The parent window
  296. * @param child The new child window that was added
  297. * @param focus Should this window become focused
  298. *
  299. */
  300. private void fireAddWindow(final FrameContainer parent,
  301. final FrameContainer child, final boolean focus) {
  302. for (FrameListener listener : listeners.get(FrameListener.class)) {
  303. listener.addWindow(parent, child, focus);
  304. }
  305. }
  306. /**
  307. * Fires the delWindow(Window) callback.
  308. *
  309. * @param window The window that was removed
  310. */
  311. private void fireDeleteWindow(final FrameContainer window) {
  312. for (FrameListener listener : listeners.get(FrameListener.class)) {
  313. listener.delWindow(window);
  314. }
  315. }
  316. /**
  317. * Fires the delWindow(Window, Window) callback.
  318. *
  319. * @param parent The parent window
  320. * @param child The child window that was removed
  321. */
  322. private void fireDeleteWindow(final FrameContainer parent,
  323. final FrameContainer child) {
  324. for (FrameListener listener : listeners.get(FrameListener.class)) {
  325. listener.delWindow(parent, child);
  326. }
  327. }
  328. /**
  329. * Frame close listener that removes the window from this manager.
  330. */
  331. private class CloseListener implements FrameCloseListener {
  332. /** {@inheritDoc} */
  333. @Override
  334. public void windowClosing(final FrameContainer window) {
  335. removeWindow(window);
  336. }
  337. }
  338. }