QedeqTreeCtrl.java
001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
002  *
003  * Copyright 2000-2013,  Michael Meyling <mime@qedeq.org>.
004  *
005  * "Hilbert II" is free software; you can redistribute
006  * it and/or modify it under the terms of the GNU General Public
007  * License as published by the Free Software Foundation; either
008  * version 2 of the License, or (at your option) any later version.
009  *
010  * This program is distributed in the hope that it will be useful,
011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013  * GNU General Public License for more details.
014  */
015 
016 package org.qedeq.gui.se.tree;
017 
018 import java.awt.event.MouseAdapter;
019 import java.util.ArrayList;
020 import java.util.List;
021 
022 import javax.swing.SwingUtilities;
023 import javax.swing.event.TreeModelEvent;
024 import javax.swing.event.TreeModelListener;
025 import javax.swing.event.TreeSelectionEvent;
026 import javax.swing.event.TreeSelectionListener;
027 import javax.swing.tree.DefaultMutableTreeNode;
028 import javax.swing.tree.TreePath;
029 
030 import org.qedeq.base.trace.Trace;
031 import org.qedeq.gui.se.control.QedeqController;
032 import org.qedeq.gui.se.main.LowerTabbedView;
033 import org.qedeq.gui.se.main.UpperTabbedView;
034 import org.qedeq.kernel.bo.common.QedeqBo;
035 
036 
037 /**
038  * Controller for a certain JTree.
039  *
040  * A Controller, which represents the classes connecting the model and the view, and is used to
041  * communicate between classes in the model and view.
042  *
043  * TODO mime 20080126: rather work with listeners than referencing other views
044  *
045  @version $Revision: 1.8 $
046  @author  Michael Meyling
047  */
048 public final class QedeqTreeCtrl implements TreeModelListener {
049 
050     /** This class. */
051     private static final Class CLASS = QedeqTreeCtrl.class;
052 
053     /** Tree view. */
054     private final QedeqTreeView treeView;
055 
056     /** Tree model. */
057     private final QedeqTreeModel treeModel;
058 
059     /** Context menu. */
060     private final QedeqTreeContextMenu contextMenu;
061 
062     /** Reference to view. */
063     private final UpperTabbedView pane;
064 
065     /** Reference to view. */
066     private final LowerTabbedView lower;
067 
068 
069     /**
070      * Tree controller.
071      *
072      @param   treeView    View.
073      @param   treeModel   Model.
074      @param   pane        Dependent view.
075      @param   lowerView   Dependent view.
076      @param   controller  Main controller.
077      */
078     public QedeqTreeCtrl(final QedeqTreeView treeView, final QedeqTreeModel treeModel,
079             final UpperTabbedView pane, final LowerTabbedView lowerView,
080             final QedeqController controller) {
081 
082         this.treeView = treeView;
083         this.treeModel = treeModel;
084         this.treeModel.addTreeModelListener(this);
085         this.contextMenu = new QedeqTreeContextMenu(controller);
086         this.treeView.treeAddMouseListener(new QedeqMouseListener());
087         this.treeView.addTreeSelectionListener(new SelectionChangedCommand());
088         // LATER mime 20071024: inform others per listener about this event
089         this.pane = pane;
090         this.lower = lowerView;
091     }
092 
093     /**
094      * Get all selected QedeqBos.
095      *
096      @return  Selected QedeqBos.
097      @throws  NothingSelectedException    Nothing was selected.
098      */
099     public final QedeqBo[] getSelected() throws NothingSelectedException {
100         final String method = "getSelected";
101         Trace.begin(CLASS, this, method);
102         try {
103             TreePath[] selected = treeView.getSelectionPaths();
104             final List list = new ArrayList();
105             if (selected != null && selected.length > 0) {
106                 Trace.trace(CLASS, this, "actionPerformed",
107                     "selection=" + selected[selected.length - 1]);
108                 for (int i = 0; i < selected.length; i++) {
109                     final DefaultMutableTreeNode node = (DefaultMutableTreeNode)
110                              (selected[i].getLastPathComponent());
111                     if (node instanceof QedeqTreeNode) {
112                         final QedeqBo prop = (QedeqBonode.getUserObject();
113                         list.add(prop);
114                     }
115                 }
116             }
117             if (list.size() <= 0) {
118                 throw new NothingSelectedException();
119             }
120             return (QedeqBo[]) list.toArray(new QedeqBo[0]);
121         catch (RuntimeException ex) {
122             Trace.trace(CLASS, this, method, ex);
123             throw new NothingSelectedException();
124         finally {
125             Trace.end(CLASS, this, method);
126         }
127     }
128 
129     /**
130      * Get edited QEDEQ text.
131      *
132      @return  QEDEQ text
133      @throws  IllegalStateException   text is not editable
134      */
135     public final String getEditedQedeq() {
136         return this.pane.getEditedQedeq();
137     }
138 
139     public void treeNodesChanged(final TreeModelEvent e) {
140         Trace.param(CLASS, this, "treeNodesChanged""event", e);
141         final Runnable runLater = new Runnable() {
142             public void run() {
143                 try {
144                     pane.updateView();
145                 catch (RuntimeException ex) {
146                     Trace.fatal(CLASS, this, "treeNodesChanged""unexpected problem", ex);
147                 }
148             }
149         };
150         SwingUtilities.invokeLater(runLater);
151         Trace.end(CLASS, this, "treeNodesChanged");
152     }
153 
154     public void treeNodesInserted(final TreeModelEvent e) {
155         Trace.begin(CLASS, this, "treeNodesInserted");
156         // 20080501 mime: solution of an old problem:
157         // because the root is invisible its children are invisible too. to make them visible
158         // we must expand the the path of the root node. but this is possible only if there is
159         // already a child node there. so if the first node is added we must expand the
160         // path of root. when the model sends us this event we can not simply expand the path
161         // instantly because if we do so, the new node is added twice! This is done for example for
162         // VariableHeightLayoutCache.visibleNodes in the two methods
163         // setExpandedState(TreePath path, boolean isExpanded) (called by expandPath) and in
164         // (treeNodesInserted(TreeModelEvent e) (called by DefaultTreeModel.nodesWereInserted(
165         // TreeNode node, int[] childIndices)
166         // To solve this dilemma we simply generate a new event that is handled later on
167         final Runnable runLater = new Runnable() {
168             public void run() {
169                 try {
170                     if (((DefaultMutableTreeNodetreeModel.getRoot()).getChildCount() 0) {
171                         Trace.trace(CLASS, this, "treeNodesInserted""expandPath");
172                         treeView.expandPath(new TreePath(treeModel.getRoot()));
173                     }
174                 catch (RuntimeException ex) {
175                     Trace.fatal(CLASS, this, "treeNodesInserted""unexpected problem", ex);
176                 }
177             }
178         };
179         SwingUtilities.invokeLater(runLater);
180         Trace.end(CLASS, this, "treeNodesInserted");
181     }
182 
183 
184     public void treeNodesRemoved(final TreeModelEvent e) {
185         final DefaultMutableTreeNode node = (DefaultMutableTreeNode)
186             (e.getTreePath().getLastPathComponent());
187         Trace.trace(CLASS, this, "treeNodeRemoved", node.getUserObject());
188     }
189 
190 
191     public void treeStructureChanged(final TreeModelEvent e) {
192         final DefaultMutableTreeNode node = (DefaultMutableTreeNode)
193             (e.getTreePath().getLastPathComponent());
194         Trace.trace(CLASS, this, "treeStructureChanged", node.getUserObject());
195     }
196 
197 
198     /**
199      * Changes node view, depending from chosen object.
200      */
201     private class SelectionChangedCommand implements TreeSelectionListener {
202 
203         public SelectionChangedCommand()  {
204         }
205 
206         public void valueChanged(final TreeSelectionEvent event) {
207             // TODO mime 20071024: inform others per listener about this event
208             Trace.trace(CLASS, this, "valueChanged", event);
209             TreePath path = event.getPath();
210             QedeqTreeNode treeNode = (QedeqTreeNodepath.getLastPathComponent();
211             if (event.isAddedPath()
212                     && treeNode.getUserObject() instanceof QedeqBo) {
213                 QedeqBo prop = (QedeqBotreeNode.getUserObject();
214                 pane.setQedeqModel(prop);
215                 lower.setQedeqModel(prop);
216             else {
217                 pane.setQedeqModel(null);
218                 lower.setQedeqModel(null);
219             }
220         }
221     }
222 
223     /**
224      * Handle mouse events.
225      */
226     private class QedeqMouseListener extends MouseAdapter {
227 
228         public void mousePressed(final java.awt.event.MouseEvent evt) {
229 
230             if (SwingUtilities.isRightMouseButton(evt)) {
231                 try {
232                     getSelected();
233                 catch (NothingSelectedException e) {
234                     final TreePath path =  treeView.getPathForLocation(evt.getX(), evt.getY());
235                     if (path != null) {
236                         treeView.setSelectionPath(path);
237                     // TODO mime 20080126: other ContextMenu if no selection was done
238                 }
239                 contextMenu.show(evt.getComponent(),
240                     evt.getX(), evt.getY());
241             }
242 //            super.mousePressed(evt);
243         }
244 
245     }
246 
247 }