Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

WindowMenuFrameManager.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. /*
  2. * Copyright (c) 2006-2011 Chris Smith, Shane Mc Cormack, Gregory Holmes
  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.addons.ui_swing.framemanager.windowmenu;
  23. import com.dmdirc.FrameContainer;
  24. import com.dmdirc.FrameContainerComparator;
  25. import com.dmdirc.addons.ui_swing.SwingController;
  26. import com.dmdirc.addons.ui_swing.SwingWindowListener;
  27. import com.dmdirc.addons.ui_swing.UIUtilities;
  28. import com.dmdirc.config.IdentityManager;
  29. import com.dmdirc.interfaces.SelectionListener;
  30. import com.dmdirc.ui.IconManager;
  31. import com.dmdirc.ui.interfaces.Window;
  32. import com.dmdirc.util.ReturnableThread;
  33. import java.awt.Component;
  34. import java.awt.event.ActionEvent;
  35. import java.awt.event.ActionListener;
  36. import java.util.ArrayList;
  37. import java.util.Collection;
  38. import java.util.Collections;
  39. import java.util.HashMap;
  40. import java.util.Map;
  41. import java.util.concurrent.atomic.AtomicBoolean;
  42. import javax.swing.AbstractButton;
  43. import javax.swing.JMenu;
  44. import javax.swing.JMenuItem;
  45. import javax.swing.JPopupMenu;
  46. import javax.swing.JSeparator;
  47. /**
  48. * Manages the window menu window list.
  49. */
  50. public final class WindowMenuFrameManager extends JMenu implements
  51. SwingWindowListener, ActionListener, SelectionListener {
  52. /**
  53. * A version number for this class. It should be changed whenever the class
  54. * structure is changed (or anything else that would prevent serialized
  55. * objects being unserialized with the new class).
  56. */
  57. private static final long serialVersionUID = 1;
  58. /** Comparator. */
  59. private final FrameContainerComparator comparator =
  60. new FrameContainerComparator();
  61. /** Non frame container menu count. */
  62. private final int itemCount;
  63. /** Menu items closing the active window. */
  64. private final JMenuItem closeMenuItem;
  65. /** Seperator. */
  66. private final JSeparator separator;
  67. /** Enabled menu items? */
  68. private final AtomicBoolean enabledMenuItems = new AtomicBoolean(false);
  69. /** Swing controller. */
  70. private final SwingController controller;
  71. /** Window -> menu map. */
  72. private final Map<FrameContainer, FrameContainerMenu> menus;
  73. private final Map<FrameContainer, FrameContainerMenuItem> items;
  74. private final Map<FrameContainer, FrameContainerMenuItem> menuItems;
  75. /**
  76. * Creates a new instance of WindowMenuFrameManager.
  77. *
  78. * @param controller Swing controller
  79. */
  80. public WindowMenuFrameManager(final SwingController controller) {
  81. super();
  82. this.controller = controller;
  83. menus = Collections.synchronizedMap(
  84. new HashMap<FrameContainer, FrameContainerMenu>());
  85. items = Collections.synchronizedMap(
  86. new HashMap<FrameContainer, FrameContainerMenuItem>());
  87. menuItems = Collections.synchronizedMap(
  88. new HashMap<FrameContainer, FrameContainerMenuItem>());
  89. setText("Window");
  90. setMnemonic('w');
  91. controller.getWindowFactory().addWindowListener(this);
  92. closeMenuItem = new JMenuItem(IconManager.getIconManager().getIcon(
  93. "close"));
  94. closeMenuItem.setMnemonic('c');
  95. closeMenuItem.setText("Close");
  96. closeMenuItem.setActionCommand("Close");
  97. closeMenuItem.addActionListener(this);
  98. add(closeMenuItem);
  99. separator = new JPopupMenu.Separator();
  100. add(separator);
  101. itemCount = getMenuComponentCount();
  102. new WindowMenuScroller(this, controller.getDomain(), itemCount);
  103. checkMenuItems();
  104. }
  105. /**
  106. * Checks the number of components in the menu and enables menus items
  107. * appropriately.
  108. */
  109. private void checkMenuItems() {
  110. enabledMenuItems.set((getMenuComponentCount() > itemCount));
  111. separator.setVisible(enabledMenuItems.get());
  112. closeMenuItem.setEnabled(enabledMenuItems.get());
  113. }
  114. /** {@inheritDoc} */
  115. @Override
  116. public void windowAdded(final Window parent, final Window window) {
  117. if (parent == null) {
  118. final FrameContainerMenuItem item = UIUtilities.invokeAndWait(
  119. new ReturnableThread<FrameContainerMenuItem>() {
  120. /** {@inheritDoc} */
  121. @Override
  122. public void run() {
  123. setObject(new FrameContainerMenuItem(window.
  124. getContainer(), WindowMenuFrameManager.this));
  125. }
  126. });
  127. items.put(window.getContainer(), item);
  128. final int index = getIndex(window.getContainer(), this);
  129. window.getContainer().addSelectionListener(this);
  130. UIUtilities.invokeLater(new Runnable() {
  131. /** {@inheritDoc} */
  132. @Override
  133. public void run() {
  134. add(item, index);
  135. }
  136. });
  137. } else {
  138. final FrameContainerMenuItem item = UIUtilities.invokeAndWait(
  139. new ReturnableThread<FrameContainerMenuItem>() {
  140. /** {@inheritDoc} */
  141. @Override
  142. public void run() {
  143. setObject(new FrameContainerMenuItem(window.
  144. getContainer(),
  145. WindowMenuFrameManager.this));
  146. }
  147. });
  148. final JMenu parentMenu;
  149. if (!menus.containsKey(parent.getContainer())) {
  150. final FrameContainerMenu replacement = UIUtilities.invokeAndWait(
  151. new ReturnableThread<FrameContainerMenu>() {
  152. /** {@inheritDoc} */
  153. @Override
  154. public void run() {
  155. setObject(new FrameContainerMenu(parent.
  156. getContainer(), controller));
  157. }
  158. });
  159. replaceItemWithMenu(getParentMenu(parent.getContainer()),
  160. items.get(parent.getContainer()), replacement);
  161. parentMenu = replacement;
  162. } else {
  163. parentMenu = menus.get(parent.getContainer());
  164. }
  165. items.put(window.getContainer(), item);
  166. window.getContainer().addSelectionListener(this);
  167. UIUtilities.invokeLater(new Runnable() {
  168. /** {@inheritDoc} */
  169. @Override
  170. public void run() {
  171. parentMenu.add(item, getIndex(window.getContainer(),
  172. parentMenu));
  173. }
  174. });
  175. }
  176. checkMenuItems();
  177. }
  178. /** {@inheritDoc} */
  179. @Override
  180. public void windowDeleted(final Window parent, final Window window) {
  181. if (parent == null) {
  182. final AbstractButton item;
  183. if (items.containsKey(window.getContainer())) {
  184. item = items.get(window.getContainer());
  185. items.remove(window.getContainer());
  186. } else if (menus.containsKey(window.getContainer())) {
  187. item = menus.get(window.getContainer());
  188. menus.remove(window.getContainer());
  189. } else {
  190. window.getContainer().removeSelectionListener(this);
  191. return;
  192. }
  193. UIUtilities.invokeLater(new Runnable() {
  194. /** {@inheritDoc} */
  195. @Override
  196. public void run() {
  197. remove(item);
  198. }
  199. });
  200. window.getContainer().removeSelectionListener(this);
  201. } else {
  202. if (items.containsKey(window.getContainer())) {
  203. final JMenu menu = getParentMenu(window.getContainer());
  204. final FrameContainerMenuItem item = items.get(window.
  205. getContainer());
  206. items.remove(window.getContainer());
  207. UIUtilities.invokeAndWait(new Runnable() {
  208. /** {@inheritDoc} */
  209. @Override
  210. public void run() {
  211. menu.remove(item);
  212. if (menu.getMenuComponentCount() == 1) {
  213. replaceMenuWithItem(getParentMenu(parent.
  214. getContainer()),
  215. menus.get(parent.getContainer()),
  216. new FrameContainerMenuItem(parent.
  217. getContainer(),
  218. WindowMenuFrameManager.this));
  219. }
  220. }
  221. });
  222. } else if (menus.containsKey(window.getContainer())) {
  223. menus.get(parent.getContainer()).remove(menus.get(window.
  224. getContainer()));
  225. menus.remove(window.getContainer());
  226. }
  227. window.getContainer().removeSelectionListener(this);
  228. }
  229. checkMenuItems();
  230. }
  231. private JMenu getParentMenu(final FrameContainer window) {
  232. FrameContainer parent = window.getParent();
  233. if (parent == null) {
  234. return this;
  235. } else {
  236. return menus.get(parent);
  237. }
  238. }
  239. private void replaceItemWithMenu(final JMenu parentMenu,
  240. final FrameContainerMenuItem item, final FrameContainerMenu menu) {
  241. UIUtilities.invokeAndWait(new Runnable() {
  242. @Override
  243. public void run() {
  244. parentMenu.remove(item);
  245. parentMenu.add(menu, getIndex(menu.getFrame(), parentMenu));
  246. menu.add(item, getIndex(item.getFrame(), menu));
  247. }
  248. });
  249. items.remove(item.getFrame());
  250. menus.put(menu.getFrame(), menu);
  251. menuItems.put(item.getFrame(), item);
  252. }
  253. private void replaceMenuWithItem(final JMenu parentMenu,
  254. final FrameContainerMenu menu, final FrameContainerMenuItem item) {
  255. parentMenu.remove(menu);
  256. parentMenu.add(item, getIndex(item.getFrame(), parentMenu));
  257. menus.remove(menu.getFrame());
  258. items.put(item.getFrame(), item);
  259. }
  260. /**
  261. * {@inheritDoc}
  262. *
  263. * @param e Action event
  264. */
  265. @Override
  266. public void actionPerformed(final ActionEvent e) {
  267. if (enabledMenuItems.get() && e.getActionCommand().equals("Close")) {
  268. controller.getMainFrame().getActiveFrame().close();
  269. }
  270. }
  271. /** {@inheritDoc} */
  272. @Override
  273. public void selectionChanged(final FrameContainer window) {
  274. final Collection<SelectionListener> values =
  275. new ArrayList<SelectionListener>();
  276. synchronized (menus) {
  277. synchronized (items) {
  278. synchronized (menuItems) {
  279. values.addAll(menus.values());
  280. values.addAll(items.values());
  281. values.addAll(menuItems.values());
  282. }
  283. }
  284. }
  285. UIUtilities.invokeLater(new Runnable() {
  286. @Override
  287. public void run() {
  288. for (SelectionListener menuItem : values) {
  289. menuItem.selectionChanged(window);
  290. }
  291. }
  292. });
  293. }
  294. /**
  295. * Tells a parent its child is selected.
  296. *
  297. * @param window parent to inform
  298. */
  299. protected void parentSelection(final FrameContainer window) {
  300. final FrameContainerMenu menuItem = menus.get(window);
  301. if (menuItem != null) {
  302. menuItem.childSelected();
  303. }
  304. }
  305. /**
  306. * Compares the new child with the existing children or parent to decide
  307. * where it needs to be inserted.
  308. *
  309. * @param newChild new node to be inserted.
  310. * @param menu Menu for the node to be inserted in
  311. *
  312. * @return index where new node is to be inserted.
  313. */
  314. private int getIndex(final FrameContainer newChild, final JMenu menu) {
  315. final int count = menu == this ? itemCount : 0;
  316. final int menuItemCount = UIUtilities.invokeAndWait(
  317. new ReturnableThread<Integer>() {
  318. /** {@inheritDoc} */
  319. @Override
  320. public void run() {
  321. setObject(menu.getMenuComponentCount());
  322. }
  323. });
  324. for (int i = count; i < menuItemCount; i++) {
  325. final int index = i;
  326. final Component component = UIUtilities.invokeAndWait(
  327. new ReturnableThread<Component>() {
  328. @Override
  329. public void run() {
  330. setObject(menu.getMenuComponent(index));
  331. }
  332. });
  333. if (!(component instanceof FrameContainerMenuInterface)) {
  334. continue;
  335. }
  336. final FrameContainer child = ((FrameContainerMenuInterface) component).
  337. getFrame();
  338. if (sortBefore(newChild, child)) {
  339. return i;
  340. } else if (!sortAfter(newChild, child) && IdentityManager.
  341. getGlobalConfig().getOptionBool("treeview",
  342. "sortwindows") && newChild.toString().compareToIgnoreCase(
  343. child.toString()) < 0) {
  344. return i;
  345. }
  346. }
  347. return UIUtilities.invokeAndWait(new ReturnableThread<Integer>() {
  348. /** {@inheritDoc} */
  349. @Override
  350. public void run() {
  351. setObject(menu.getMenuComponentCount());
  352. }
  353. });
  354. }
  355. /**
  356. * Compares the types of the specified nodes' objects to see if the new
  357. * node should be sorted before the other.
  358. *
  359. * @param newChild The new child to be tested
  360. * @param child The existing child that it's being tested against
  361. *
  362. * @return True iff newChild should be sorted before child
  363. */
  364. private boolean sortBefore(final FrameContainer newChild,
  365. final FrameContainer child) {
  366. return comparator.compare(newChild, child) <= -1;
  367. }
  368. /**
  369. * Compares the types of the specified nodes' objects to see if the new
  370. * node should be sorted after the other.
  371. *
  372. * @param newChild The new child to be tested
  373. * @param child The existing child that it's being tested against
  374. *
  375. * @return True iff newChild should be sorted before child
  376. */
  377. private boolean sortAfter(final FrameContainer newChild,
  378. final FrameContainer child) {
  379. return comparator.compare(newChild, child) >= 1;
  380. }
  381. }