選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

Tree.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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.addons.ui_swing.framemanager.tree;
  23. import com.dmdirc.addons.ui_swing.EDTInvocation;
  24. import com.dmdirc.addons.ui_swing.UIUtilities;
  25. import com.dmdirc.addons.ui_swing.actions.CloseWindowAction;
  26. import com.dmdirc.addons.ui_swing.components.frames.TextFrame;
  27. import com.dmdirc.addons.ui_swing.events.SwingActiveWindowChangeRequestEvent;
  28. import com.dmdirc.addons.ui_swing.events.SwingEventBus;
  29. import com.dmdirc.config.ConfigBinding;
  30. import com.dmdirc.interfaces.config.AggregateConfigProvider;
  31. import java.awt.Rectangle;
  32. import java.awt.event.ActionEvent;
  33. import java.awt.event.ActionListener;
  34. import java.awt.event.MouseEvent;
  35. import java.awt.event.MouseListener;
  36. import java.awt.event.MouseMotionListener;
  37. import java.util.Optional;
  38. import javax.swing.BorderFactory;
  39. import javax.swing.JComponent;
  40. import javax.swing.JMenuItem;
  41. import javax.swing.JPopupMenu;
  42. import javax.swing.JTree;
  43. import javax.swing.UIManager;
  44. import javax.swing.tree.DefaultTreeModel;
  45. import javax.swing.tree.MutableTreeNode;
  46. import javax.swing.tree.TreeModel;
  47. import javax.swing.tree.TreePath;
  48. import javax.swing.tree.TreeSelectionModel;
  49. import net.miginfocom.layout.PlatformDefaults;
  50. /**
  51. * Specialised JTree for the frame manager.
  52. */
  53. public class Tree extends JTree implements MouseMotionListener, MouseListener, ActionListener {
  54. /** A version number for this class. */
  55. private static final long serialVersionUID = 1;
  56. /** Tree frame manager. */
  57. private final TreeFrameManager manager;
  58. /** The swing event bus to post events to. */
  59. private final SwingEventBus eventBus;
  60. /** Config manager. */
  61. private final AggregateConfigProvider config;
  62. /** Drag selection enabled? */
  63. private boolean dragSelect;
  64. /** Drag button 1? */
  65. private boolean dragButton;
  66. /** Show handles. */
  67. private boolean showHandles;
  68. /**
  69. * Specialised JTree for frame manager.
  70. *
  71. * @param manager Frame manager
  72. * @param model tree model.
  73. * @param eventBus The swing event bus to post events to.
  74. * @param globalConfig The config to read settings from.
  75. * @param domain The domain to read settings from.
  76. */
  77. public Tree(
  78. final TreeFrameManager manager,
  79. final TreeModel model,
  80. final SwingEventBus eventBus,
  81. final AggregateConfigProvider globalConfig,
  82. final String domain) {
  83. super(model);
  84. this.manager = manager;
  85. this.config = globalConfig;
  86. this.eventBus = eventBus;
  87. putClientProperty("JTree.lineStyle", "Angled");
  88. getInputMap().setParent(null);
  89. getInputMap(JComponent.WHEN_FOCUSED).clear();
  90. getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).clear();
  91. getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).clear();
  92. getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
  93. setRootVisible(false);
  94. setRowHeight(getFontMetrics(UIManager.getFont("Tree.font")).getHeight() + 2);
  95. setOpaque(true);
  96. setBorder(BorderFactory.createEmptyBorder(
  97. (int) PlatformDefaults.getUnitValueX("related").getValue(),
  98. (int) PlatformDefaults.getUnitValueX("related").getValue(),
  99. (int) PlatformDefaults.getUnitValueX("related").getValue(),
  100. (int) PlatformDefaults.getUnitValueX("related").getValue()));
  101. setFocusable(false);
  102. config.getBinder().withDefaultDomain(domain).bind(this, Tree.class);
  103. // TODO: Create a nicer lifecycle and unbind
  104. addMouseListener(this);
  105. addMouseMotionListener(this);
  106. }
  107. @Override
  108. public void scrollRectToVisible(final Rectangle aRect) {
  109. final Rectangle rect = new Rectangle(0, aRect.y, aRect.width, aRect.height);
  110. super.scrollRectToVisible(rect);
  111. }
  112. /**
  113. * Set path.
  114. *
  115. * @param path Path
  116. */
  117. public void setTreePath(final TreePath path) {
  118. UIUtilities.invokeLater(() -> setSelectionPath(path));
  119. }
  120. /**
  121. * Returns the node for the specified location, returning null if rollover is disabled or there
  122. * is no node at the specified location.
  123. *
  124. * @param x x coordiantes
  125. * @param y y coordiantes
  126. *
  127. * @return node or null
  128. */
  129. public TreeViewNode getNodeForLocation(final int x,
  130. final int y) {
  131. TreeViewNode node = null;
  132. final TreePath selectedPath = getPathForLocation(x, y);
  133. if (selectedPath != null) {
  134. node = (TreeViewNode) selectedPath.getLastPathComponent();
  135. }
  136. return node;
  137. }
  138. @ConfigBinding(key = "dragSelection", invocation = EDTInvocation.class)
  139. public void handleDragSelection(final String value) {
  140. dragSelect = config.getOptionBool("treeview", "dragSelection");
  141. }
  142. @ConfigBinding(key="showtreeexpands")
  143. public void handleTreeExpands(final String value) {
  144. showHandles = Boolean.valueOf(value);
  145. setShowsRootHandles(showHandles);
  146. putClientProperty("showHandles", showHandles);
  147. }
  148. @Override
  149. public void mouseDragged(final MouseEvent e) {
  150. if (dragSelect && dragButton) {
  151. final TreeViewNode node = getNodeForLocation(e.getX(), e.getY());
  152. if (node != null) {
  153. eventBus.publishAsync(new SwingActiveWindowChangeRequestEvent(
  154. Optional.ofNullable(((TreeViewNode) new TreePath(node.getPath())
  155. .getLastPathComponent()).getWindow())));
  156. }
  157. }
  158. manager.checkRollover(e);
  159. }
  160. @Override
  161. public void mouseMoved(final MouseEvent e) {
  162. manager.checkRollover(e);
  163. }
  164. @Override
  165. public void mouseClicked(final MouseEvent e) {
  166. processMouseEvents(e);
  167. }
  168. @Override
  169. public void mousePressed(final MouseEvent e) {
  170. if (e.getButton() == MouseEvent.BUTTON1) {
  171. dragButton = true;
  172. final TreePath selectedPath = getPathForLocation(e.getX(),
  173. e.getY());
  174. if (selectedPath != null) {
  175. eventBus.publishAsync(new SwingActiveWindowChangeRequestEvent(
  176. Optional.ofNullable(((TreeViewNode) selectedPath.getLastPathComponent())
  177. .getWindow())));
  178. }
  179. }
  180. processMouseEvents(e);
  181. }
  182. @Override
  183. public void mouseReleased(final MouseEvent e) {
  184. dragButton = false;
  185. processMouseEvents(e);
  186. }
  187. @Override
  188. public void mouseEntered(final MouseEvent e) {
  189. //Ignore
  190. }
  191. @Override
  192. public void mouseExited(final MouseEvent e) {
  193. manager.checkRollover(null);
  194. }
  195. /**
  196. * Processes every mouse button event to check for a popup trigger.
  197. *
  198. * @param e mouse event
  199. */
  200. void processMouseEvents(final MouseEvent e) {
  201. final TreePath localPath = getPathForLocation(e.getX(), e.getY());
  202. if (localPath != null && e.isPopupTrigger()) {
  203. final TextFrame frame = ((TreeViewNode) localPath.getLastPathComponent()).getWindow();
  204. if (frame == null) {
  205. return;
  206. }
  207. final JPopupMenu popupMenu = frame.getPopupMenu(null,
  208. new Object[][]{new Object[]{""}});
  209. frame.addCustomPopupItems(popupMenu);
  210. if (popupMenu.getComponentCount() > 0) {
  211. popupMenu.addSeparator();
  212. }
  213. final TreeViewNodeMenuItem popoutMenuItem;
  214. if (frame.getPopoutFrame() == null) {
  215. popoutMenuItem = new TreeViewNodeMenuItem("Pop Out", "popout",
  216. (TreeViewNode) localPath.getLastPathComponent());
  217. } else {
  218. popoutMenuItem = new TreeViewNodeMenuItem("Pop In", "popin",
  219. (TreeViewNode) localPath.getLastPathComponent());
  220. }
  221. popupMenu.add(popoutMenuItem);
  222. popupMenu.addSeparator();
  223. popoutMenuItem.addActionListener(this);
  224. final TreeViewNodeMenuItem moveUp = new TreeViewNodeMenuItem("Move Up", "Up",
  225. (TreeViewNode) localPath.getLastPathComponent());
  226. final TreeViewNodeMenuItem moveDown = new TreeViewNodeMenuItem("Move Down", "Down",
  227. (TreeViewNode) localPath.getLastPathComponent());
  228. moveUp.addActionListener(this);
  229. moveDown.addActionListener(this);
  230. popupMenu.add(moveUp);
  231. popupMenu.add(moveDown);
  232. popupMenu.add(new JMenuItem(new CloseWindowAction(frame.getContainer())));
  233. popupMenu.show(this, e.getX(), e.getY());
  234. }
  235. }
  236. @Override
  237. public void actionPerformed(final ActionEvent e) {
  238. final TreeViewNode node = ((TreeViewNodeMenuItem) e.getSource()).
  239. getTreeNode();
  240. int index = getModel().getIndexOfChild(node.getParent(), node);
  241. switch (e.getActionCommand()) {
  242. case "Up":
  243. if (index == 0) {
  244. index = node.getSiblingCount() - 1;
  245. } else {
  246. index--;
  247. }
  248. break;
  249. case "Down":
  250. if (index == node.getSiblingCount() - 1) {
  251. index = 0;
  252. } else {
  253. index++;
  254. }
  255. break;
  256. case "popout":
  257. node.getWindow().setPopout(true);
  258. break;
  259. case "popin":
  260. node.getWindow().setPopout(false);
  261. break;
  262. }
  263. final MutableTreeNode parentNode = (MutableTreeNode) node.getParent();
  264. final TreePath nodePath = new TreePath(node.getPath());
  265. final boolean isExpanded = isExpanded(nodePath);
  266. ((DefaultTreeModel) getModel()).removeNodeFromParent(node);
  267. ((DefaultTreeModel) getModel()).insertNodeInto(node, parentNode, index);
  268. setExpandedState(nodePath, isExpanded);
  269. }
  270. }