View Javadoc

1   /*
2      Copyright 2002-2006 Martin van den Bemt
3   
4      Licensed under the Apache License, Version 2.0 (the "License");
5      you may not use this file except in compliance with the License.
6      You may obtain a copy of the License at
7   
8          http://www.apache.org/licenses/LICENSE-2.0
9   
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15  */
16  package org.xulux.guilayer.swing.widgets;
17  
18  import java.util.ArrayList;
19  import java.util.Iterator;
20  import java.util.List;
21  
22  import javax.swing.JScrollPane;
23  import javax.swing.ToolTipManager;
24  import javax.swing.tree.DefaultTreeCellRenderer;
25  import javax.swing.tree.TreePath;
26  import javax.swing.tree.TreeSelectionModel;
27  
28  import org.xulux.api.core.IWidgetConfig;
29  import org.xulux.api.dataprovider.IField;
30  import org.xulux.api.dataprovider.IMapping;
31  import org.xulux.api.gui.IContentWidget;
32  import org.xulux.api.gui.IWidget;
33  import org.xulux.core.XuluxContext;
34  import org.xulux.dataprovider.contenthandlers.ContentView;
35  import org.xulux.dataprovider.contenthandlers.TreeContentHandler;
36  import org.xulux.gui.ContainerWidget;
37  import org.xulux.gui.WidgetFactory;
38  import org.xulux.guilayer.swing.extensions.XuluxJTree;
39  import org.xulux.guilayer.swing.listeners.ImmidiateTreeSelectionListener;
40  import org.xulux.guilayer.swing.listeners.MouseDoubleClickTreeListener;
41  import org.xulux.guilayer.swing.listeners.NewSelectionListener;
42  import org.xulux.guilayer.swing.listeners.PopupListener;
43  import org.xulux.guilayer.swing.listeners.UpdateButtonsListener;
44  import org.xulux.guilayer.swing.models.NyxTreeCellRenderer;
45  import org.xulux.guilayer.swing.models.SwingTreeModel;
46  import org.xulux.utils.BooleanUtils;
47  import org.xulux.utils.ClassLoaderUtils;
48  
49  /**
50   * @author <a href="mailto:martin@mvdb.net">Martin van den Bemt</a>
51   * @version $Id: Tree.java,v 1.1 2005/12/18 12:58:18 mvdb Exp $
52   */
53  public class Tree extends ContainerWidget implements IContentWidget {
54  
55      /**
56       * The native jtree
57       */
58      protected XuluxJTree jtree;
59      /**
60       * the native scrollpane
61       */
62      protected JScrollPane scrollPane;
63      /**
64       * the content
65       */
66      protected Object content;
67      /**
68       * the content has changed
69       */
70      protected boolean contentChanged;
71      /**
72       * the contenthandler
73       */
74      protected SwingTreeModel contentHandler;
75      /**
76       * has childpopups
77       */
78      protected boolean hasChildPopups;
79      /**
80       * the menu
81       */
82      protected IWidget menu;
83      /**
84       * the cell renderer
85       */
86      protected NyxTreeCellRenderer cellRenderer;
87      /**
88       * the selectionlistener
89       */
90      protected NewSelectionListener selectionListener;
91      /**
92       * the immidiatelistener
93       */
94      protected ImmidiateTreeSelectionListener immidiateListener;
95      
96      protected MouseDoubleClickTreeListener mdcListener;
97  
98      /**
99       * @param name the name of the tree
100      */
101     public Tree(String name) {
102         super(name);
103     }
104 
105     /**
106      * @see org.xulux.gui.XuluxWidget#destroy()
107      * @todo Destroy it better than now!
108      */
109     public void destroy() {
110         if (!initialized) {
111             return;
112         }
113         super.destroy();
114         cellRenderer = null;
115         if (mdcListener != null) {
116         	jtree.removeMouseListener(mdcListener);
117         	mdcListener = null;
118         }
119         if (selectionListener != null) {
120             jtree.removeTreeSelectionListener(selectionListener);
121             selectionListener = null;
122         }
123         if (immidiateListener != null) {
124             jtree.removeTreeSelectionListener(immidiateListener);
125             immidiateListener = null;
126         }
127         if (this.scrollPane != null) {
128             if (this.jtree != null) {
129                 this.scrollPane.remove(jtree);
130                 jtree = null;
131             }
132             this.scrollPane = null;
133         }
134     }
135 
136     /**
137      * @see org.xulux.nyx.gui.XuluxWidget#getNativeWidget()
138      */
139     public Object getNativeWidget() {
140         initialize();
141         return this.scrollPane;
142     }
143 
144     /**
145      * We need to set the model on initialization and replace the root
146      * node on refresh. A setmodel doesn't use the new model (???) Weird..
147      *
148      * @see org.xulux.nyx.gui.XuluxWidget#initialize()
149      */
150     public void initialize() {
151         if (initialized) {
152             return;
153         }
154         if (this.contentHandler == null) {
155             this.contentHandler = new SwingTreeModel(null);
156         }
157         jtree = new XuluxJTree(this.contentHandler);
158         ToolTipManager.sharedInstance().registerComponent(jtree);
159         if (selectionListener == null) {
160             selectionListener = new NewSelectionListener(this);
161         }
162         jtree.addTreeSelectionListener(selectionListener);
163         if (immidiateListener == null) {
164             immidiateListener = new ImmidiateTreeSelectionListener(this);
165         }
166         jtree.addTreeSelectionListener(immidiateListener);
167         cellRenderer = new NyxTreeCellRenderer(this);
168         // @todo add support for more than one node selection
169         jtree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
170         jtree.setCellRenderer(this.cellRenderer);
171         scrollPane = new JScrollPane(jtree);
172         initializeChildren();
173         initialized = true;
174         contentChanged();
175         refresh();
176     }
177 
178     /**
179      * @see org.xulux.nyx.gui.XuluxWidget#refresh()
180      */
181     public void refresh() {
182         if (isRefreshing()) {
183             return;
184         }
185         isRefreshing = true;
186         initialize();
187         /* Works only for Metal L&F */
188         String lineStyle = getProperty("linestyle");
189         if (lineStyle == null) {
190             // default to none..
191             lineStyle = "None";
192         }
193         // probably destroyed already..
194         if (jtree == null) {
195             return;
196         }
197         jtree.putClientProperty("JTree.lineStyle", lineStyle);
198         if (getChildWidgets() != null) {
199           // we need to set the cellrenderer..
200           IWidget child = (IWidget) getChildWidgets().get(0);
201         }
202         boolean showRoot = true;
203         if (getProperty("showRoot") != null) {
204             showRoot = BooleanUtils.toBoolean(getProperty("showRoot"));
205         }
206         jtree.setRootVisible(showRoot);
207 
208         if (contentChanged) {
209             if (getProperty("contentview") != null) {
210                 contentHandler.setView(ClassLoaderUtils.getClass((String)getProperty("contentview")));
211             }
212             contentHandler.refresh();
213             jtree.setModel(contentHandler);
214             contentChanged = false;
215         }
216         if (getProperty("collapse") != null) {
217             if (getContent() != null) {
218               setProperty("collapse", null);
219             }
220             // collapsetree..
221             //            System.err.println("Collapsing!!");
222             if (jtree != null && contentHandler != null) {
223                 // @todo really make this flexible!
224                 String collapseUntill = getProperty("collapse-untill");
225                 jtree.collapsePath(new TreePath(contentHandler.getRoot()));
226                 if (collapseUntill != null) {
227                     if (collapseUntill != null) {
228                         if (collapseUntill.toLowerCase().startsWith("level")) {
229                             int level = Integer.parseInt(collapseUntill.substring("level".length()));
230                             expandToLevel(null, level);
231                         }
232                     }
233                 }
234             }
235         }
236         if (getProperty("expand") != null) {
237             if (jtree != null && contentHandler != null) {
238                 String expandUntill = getProperty("expand-untill");
239                 expandTree(expandUntill);
240             }
241         }
242         jtree.setShowsRootHandles(true);
243         
244         // default is on, so we only process if the value is false.
245         if (!BooleanUtils.toBoolean(getProperty("showicons"))) {
246             if (jtree != null) {
247                 if (jtree.getCellRenderer() instanceof DefaultTreeCellRenderer) {
248                     DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) jtree.getCellRenderer();
249                     renderer.setOpenIcon(null);
250                     renderer.setClosedIcon(null);
251                     renderer.setLeafIcon(null);
252                 }
253             }
254         }
255         
256         if (mdcListener == null && getProperty("doubleclick") != null) {
257         	mdcListener = new MouseDoubleClickTreeListener(this);
258         	jtree.addMouseListener(mdcListener);
259         }
260         jtree.setEnabled(isEnabled());
261         jtree.setVisible(isVisible());
262         scrollPane.setPreferredSize(getRectangle().getRectangle().getSize());
263         initializePopupMenu();
264         isRefreshing = false;
265     }
266 
267     /**
268      * Expands the tree to the specified level.
269      * Level 0 is the root of the tree.
270      * If level is higher than the levels present, it will just collapse all.
271      *
272      * @param root the root level
273      * @param level the level
274      */
275     protected void expandToLevel(Object root, int level) {
276         if (level == 0) {
277             // the root level is always expanded..
278             return;
279         }
280         if (expandPath == null) {
281             expandPath = new ArrayList();
282         }
283         if (root == null) {
284             // asssume we need to set the root..
285             root = contentHandler.getRoot();
286         }
287         expandPath.add(root);
288         int childCount = contentHandler.getChildCount(root);
289         for (int i = 0; i < childCount; i++) {
290             Object ele = contentHandler.getChild(root, i);
291             if (expandPath.size() - 1 != level) {
292                 expandToLevel(ele, level);
293             } else {
294                 if (expandPath != null) {
295                     // collapse the current root..
296                     jtree.collapsePath(new TreePath(expandPath.toArray()));
297                     // remove the current root,else it will expand leafs..
298                     expandPath.remove(root);
299                     jtree.expandPath(new TreePath(expandPath.toArray()));
300                     break;
301                 }
302             }
303         }
304         expandPath.remove(root);
305     }
306 
307     /**
308      * Does not do anything atm.
309      * @param root the root object
310      */
311     protected void expandToLevel(Object root) {
312 
313     }
314 
315     /**
316      * Expands the tree.
317      * @param untill - a string representation untill wath kind of object
318      *         the tree should be expanded.
319      */
320     protected void expandTree(String untill) {
321         boolean expandDefault = false;
322         Object root = contentHandler.getRoot();
323         if (untill == null) {
324             expandDefault(root);
325             return;
326         }
327         if (untill.equalsIgnoreCase("leaf")) {
328             expand(root, true);
329             return;
330         }
331         if (untill.equalsIgnoreCase("all")) {
332             expand(root, false);
333             return;
334         }
335         Class untillClazz = ClassLoaderUtils.getClass(untill);
336         if (untillClazz == null) {
337             expandDefault(root);
338             return;
339         }
340         expandFrom(root, untillClazz);
341     }
342     /**
343      * The private expandPath array. Used in expandToLeaf..
344      */
345     private List expandPath;
346     /**
347      * @todo Document this... or move to treecontenthandler!
348      * @param root the root object
349      * @param leaf true or false. If true it will NOT show the leaf, otherwise it will
350      */
351     protected void expand(Object root, boolean leaf) {
352         if (expandPath == null) {
353             expandPath = new ArrayList();
354         }
355         expandPath.add(root);
356         int childCount = contentHandler.getChildCount(root);
357         for (int i = 0; i < childCount; i++) {
358             Object ele = contentHandler.getChild(root, i);
359             if (!contentHandler.isLeaf(ele)) {
360                 expand(ele, leaf);
361             } else {
362                 if (expandPath != null) {
363                     // collapse the current root..
364                     // remove the current root,else it will expand leafs..
365                     if (leaf) {
366                         expandPath.remove(root);
367                         jtree.collapsePath(new TreePath(expandPath.toArray()));
368                     }
369                     jtree.expandPath(new TreePath(expandPath.toArray()));
370                 }
371             }
372         }
373         expandPath.remove(root);
374     }
375 
376     /**
377      *
378      * @param root - the root to expand from
379      * @param untill - the class to expand untill.
380      */
381     protected void expandFrom(Object root, Class untill) {
382     }
383 
384     /**
385      * The default method to expand.
386      * @param root the root object
387      */
388     protected void expandDefault(Object root) {
389         jtree.expandPath(new TreePath(root));
390     }
391 
392     /**
393      * @see org.xulux.nyx.gui.XuluxWidget#getGuiValue()
394      */
395     public Object getGuiValue() {
396         return null;
397     }
398 
399     /**
400      * @see org.xulux.nyx.gui.XuluxWidget#focus()
401      */
402     public void focus() {
403 
404     }
405 
406     /**
407      * @see org.xulux.nyx.gui.XuluxWidget#isValueEmpty()
408      */
409     public boolean isValueEmpty() {
410         return false;
411     }
412 
413     /**
414      * @see org.xulux.nyx.gui.XuluxWidget#canContainValue()
415      */
416     public boolean canContainValue() {
417         return false;
418     }
419 
420     /**
421      * @see org.xulux.nyx.gui.IContentWidget#setContent(java.lang.Object)
422      */
423     public void setContent(Object object) {
424         //        System.err.println("setContent called");
425         //setProperty("content", content);
426         this.content = object;
427         if (object != null) {
428             String cHProp = getProperty("contenthandler");
429             TreeContentHandler handler = null;
430             if (cHProp != null) {
431               handler = (TreeContentHandler) ClassLoaderUtils.getObjectFromClassString(cHProp);
432             } else {
433                 IWidgetConfig config = XuluxContext.getGuiDefaults().getWidgetConfig(getWidgetType());
434                 handler = (TreeContentHandler) config.getContentHandler(object.getClass());
435             }
436 //            System.out.println("handler for tree : " + handler);
437             if (handler == null) {
438                 System.err.println("Handler for content " + object.getClass() + " not found");
439             } else {
440                 handler.setWidget(this);
441                 handler.setContent(object);
442 //                System.err.println("handler content "+handler.getContent());
443                 this.contentHandler = new SwingTreeModel(handler);
444                 this.contentHandler.setWidget(this);
445                 this.contentHandler.setContent(object);
446                 if (getProperty("contentview") != null) {
447                     Class contentView = ClassLoaderUtils.getClass(getProperty("contentview"));
448                     this.contentHandler.setView(contentView);
449                 }
450             }
451         }
452         contentChanged = true;
453         refresh();
454     }
455 
456     /**
457      * @see org.xulux.nyx.gui.IContentWidget#getContent()
458      */
459     public Object getContent() {
460         return this.content;
461     }
462 
463     /**
464      * @see org.xulux.nyx.gui.IContentWidget#contentChanged()
465      */
466     public void contentChanged() {
467         if (getProperty("content") != null) {
468             String c = getProperty("content");
469             String cType = getProperty("content.type");
470             if (cType == null || cType.equalsIgnoreCase("use")) {
471               IMapping mapping = XuluxContext.getDictionary().getDefaultProvider().getMapping(getPart().getBean());
472               IField field = mapping.getField(c);
473               setContent(field.getValue(getPart().getBean()));
474             }
475         } else if (getProperty("content.type") != null) {
476             if ("bean".equalsIgnoreCase(getProperty("content.type"))) {
477                 setContent(getPart().getBean());
478             }
479         }
480         if (contentHandler != null) {
481             if (getProperty("contentview") != null) {
482                 //contentHandler.get
483                 contentHandler.getInnerContentHandler().setView(ClassLoaderUtils.getClass((String)getProperty("contentview")));
484             }
485             contentHandler.refresh();
486         }
487     }
488 
489     /**
490      * @see org.xulux.nyx.gui.ContainerWidget#addToParent(org.xulux.nyx.gui.XuluxWidget)
491      */
492     public void addToParent(IWidget widget) {
493         if (widget instanceof PopupMenu || widget instanceof MenuItem) {
494             hasChildPopups = true;
495         } else {
496             cellRenderer.setChildWidget(widget);
497         }
498     }
499 
500     /**
501      * Initializes the popupmenus of the tree.
502      *
503      */
504     protected void initializePopupMenu() {
505         //        System.out.println("childPopupinit");
506         if (hasChildPopups) {
507             //            System.err.println("hasChildPopups !!!");
508             //            System.err.println("Childwidgets : "+getChildWidgets());
509             if (menu == null) {
510                 menu = WidgetFactory.getWidget("popupmenu", "PopupMenu:" + getName());
511                 menu.setPart(getPart());
512             }
513             if (getChildWidgets() != null) {
514                 for (Iterator it = getChildWidgets().iterator(); it.hasNext();) {
515                     IWidget cw = (IWidget) it.next();
516                     if (cw instanceof MenuItem) {
517                         cw.addXuluxListener(new UpdateButtonsListener(this, cw));
518                         cw.setParent(menu);
519                         cw.setPart(getPart());
520                         menu.addChildWidget(cw);
521                     }
522                 }
523             }
524         }
525         if (menu != null) {
526             menu.setParent(this);
527             menu.initialize();
528             jtree.addMouseListener(new PopupListener(this.menu, this));
529         }
530     }
531 
532     /**
533      * @see org.xulux.nyx.gui.XuluxWidget#getValue()
534      */
535     public Object getValue() {
536         if (jtree != null) {
537             if (jtree.getSelectionPath() != null) {
538                 Object obj = jtree.getSelectionPath().getLastPathComponent();
539                 if (obj instanceof ContentView) {
540                     obj = ((ContentView)obj).getSource();
541                 } 
542                 return obj;
543             }
544         }
545         return null;
546     }
547     
548     /**
549      * Set the currently selected value in the tree.
550      * @see org.xulux.gui.XuluxWidget#setValue(java.lang.Object)
551      */
552     public void setValue(Object value) {
553         System.out.println("Setting value : " + value);
554         new Exception().printStackTrace();
555         if (this.contentHandler == null) {
556             return;
557         }
558         if (jtree != null) {
559             TreePath tp = this.contentHandler.getTreePath(value);
560             if (tp == null) {
561                 System.out.println("Cannot find value "+value+" in the tree!");
562             } else {
563                 jtree.setSelectionPath(tp);
564                 jtree.scrollPathToVisible(tp);
565             }
566         }
567         super.setValue(value);
568     }
569     /**
570      *
571      * @return the tree model. It is a convenience method for internal use only.
572      */
573     public SwingTreeModel getSwingModel() {
574         return this.contentHandler;
575     }
576 
577 }