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  @author  Michael Meyling
046  */
047 public final class QedeqTreeCtrl implements TreeModelListener {
048 
049     /** This class. */
050     private static final Class CLASS = QedeqTreeCtrl.class;
051 
052     /** Tree view. */
053     private final QedeqTreeView treeView;
054 
055     /** Tree model. */
056     private final QedeqTreeModel treeModel;
057 
058     /** Context menu. */
059     private final QedeqTreeContextMenu contextMenu;
060 
061     /** Reference to view. */
062     private final UpperTabbedView pane;
063 
064     /** Reference to view. */
065     private final LowerTabbedView lower;
066 
067 
068     /**
069      * Tree controller.
070      *
071      @param   treeView    View.
072      @param   treeModel   Model.
073      @param   pane        Dependent view.
074      @param   lowerView   Dependent view.
075      @param   controller  Main controller.
076      */
077     public QedeqTreeCtrl(final QedeqTreeView treeView, final QedeqTreeModel treeModel,
078             final UpperTabbedView pane, final LowerTabbedView lowerView,
079             final QedeqController controller) {
080 
081         this.treeView = treeView;
082         this.treeModel = treeModel;
083         this.treeModel.addTreeModelListener(this);
084         this.contextMenu = new QedeqTreeContextMenu(controller);
085         this.treeView.treeAddMouseListener(new QedeqMouseListener());
086         this.treeView.addTreeSelectionListener(new SelectionChangedCommand());
087         // LATER mime 20071024: inform others per listener about this event
088         this.pane = pane;
089         this.lower = lowerView;
090     }
091 
092     /**
093      * Get all selected QedeqBos.
094      *
095      @return  Selected QedeqBos.
096      @throws  NothingSelectedException    Nothing was selected.
097      */
098     public final QedeqBo[] getSelected() throws NothingSelectedException {
099         final String method = "getSelected";
100         Trace.begin(CLASS, this, method);
101         try {
102             TreePath[] selected = treeView.getSelectionPaths();
103             final List list = new ArrayList();
104             if (selected != null && selected.length > 0) {
105                 Trace.trace(CLASS, this, "actionPerformed",
106                     "selection=" + selected[selected.length - 1]);
107                 for (int i = 0; i < selected.length; i++) {
108                     final DefaultMutableTreeNode node = (DefaultMutableTreeNode)
109                              (selected[i].getLastPathComponent());
110                     if (node instanceof QedeqTreeNode) {
111                         final QedeqBo prop = (QedeqBonode.getUserObject();
112                         list.add(prop);
113                     }
114                 }
115             }
116             if (list.size() <= 0) {
117                 throw new NothingSelectedException();
118             }
119             return (QedeqBo[]) list.toArray(new QedeqBo[0]);
120         catch (RuntimeException ex) {
121             Trace.trace(CLASS, this, method, ex);
122             throw new NothingSelectedException();
123         finally {
124             Trace.end(CLASS, this, method);
125         }
126     }
127 
128     /**
129      * Get edited QEDEQ text.
130      *
131      @return  QEDEQ text
132      @throws  IllegalStateException   text is not editable
133      */
134     public final String getEditedQedeq() {
135         return this.pane.getEditedQedeq();
136     }
137 
138     public void treeNodesChanged(final TreeModelEvent e) {
139         Trace.param(CLASS, this, "treeNodesChanged""event", e);
140         final Runnable runLater = new Runnable() {
141             public void run() {
142                 try {
143                     pane.updateView();
144                 catch (RuntimeException ex) {
145                     Trace.fatal(CLASS, this, "treeNodesChanged""unexpected problem", ex);
146                 }
147             }
148         };
149         SwingUtilities.invokeLater(runLater);
150         Trace.end(CLASS, this, "treeNodesChanged");
151     }
152 
153     public void treeNodesInserted(final TreeModelEvent e) {
154         Trace.begin(CLASS, this, "treeNodesInserted");
155         // 20080501 mime: solution of an old problem:
156         // because the root is invisible its children are invisible too. to make them visible
157         // we must expand the the path of the root node. but this is possible only if there is
158         // already a child node there. so if the first node is added we must expand the
159         // path of root. when the model sends us this event we can not simply expand the path
160         // instantly because if we do so, the new node is added twice! This is done for example for
161         // VariableHeightLayoutCache.visibleNodes in the two methods
162         // setExpandedState(TreePath path, boolean isExpanded) (called by expandPath) and in
163         // (treeNodesInserted(TreeModelEvent e) (called by DefaultTreeModel.nodesWereInserted(
164         // TreeNode node, int[] childIndices)
165         // To solve this dilemma we simply generate a new event that is handled later on
166         final Runnable runLater = new Runnable() {
167             public void run() {
168                 try {
169                     if (((DefaultMutableTreeNodetreeModel.getRoot()).getChildCount() 0) {
170                         Trace.trace(CLASS, this, "treeNodesInserted""expandPath");
171                         treeView.expandPath(new TreePath(treeModel.getRoot()));
172                     }
173                 catch (RuntimeException ex) {
174                     Trace.fatal(CLASS, this, "treeNodesInserted""unexpected problem", ex);
175                 }
176             }
177         };
178         SwingUtilities.invokeLater(runLater);
179         Trace.end(CLASS, this, "treeNodesInserted");
180     }
181 
182 
183     public void treeNodesRemoved(final TreeModelEvent e) {
184         final DefaultMutableTreeNode node = (DefaultMutableTreeNode)
185             (e.getTreePath().getLastPathComponent());
186         Trace.trace(CLASS, this, "treeNodeRemoved", node.getUserObject());
187     }
188 
189 
190     public void treeStructureChanged(final TreeModelEvent e) {
191         final DefaultMutableTreeNode node = (DefaultMutableTreeNode)
192             (e.getTreePath().getLastPathComponent());
193         Trace.trace(CLASS, this, "treeStructureChanged", node.getUserObject());
194     }
195 
196 
197     /**
198      * Changes node view, depending from chosen object.
199      */
200     private class SelectionChangedCommand implements TreeSelectionListener {
201 
202         public SelectionChangedCommand()  {
203             // nothing to do
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 }