KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > rice > cs > drjava > ui > RegionsTreePanel


1 /*BEGIN_COPYRIGHT_BLOCK
2  *
3  * This file is part of DrJava. Download the current version of this project from http://www.drjava.org/
4  * or http://sourceforge.net/projects/drjava/
5  *
6  * DrJava Open Source License
7  *
8  * Copyright (C) 2001-2005 JavaPLT group at Rice University (javaplt@rice.edu). All rights reserved.
9  *
10  * Developed by: Java Programming Languages Team, Rice University, http://www.cs.rice.edu/~javaplt/
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
13  * documentation files (the "Software"), to deal with the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
15  * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
16  *
17  * - Redistributions of source code must retain the above copyright notice, this list of conditions and the
18  * following disclaimers.
19  * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
20  * following disclaimers in the documentation and/or other materials provided with the distribution.
21  * - Neither the names of DrJava, the JavaPLT, Rice University, nor the names of its contributors may be used to
22  * endorse or promote products derived from this Software without specific prior written permission.
23  * - Products derived from this software may not be called "DrJava" nor use the term "DrJava" as part of their
24  * names without prior written permission from the JavaPLT group. For permission, write to javaplt@rice.edu.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
27  * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28  * CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
29  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30  * WITH THE SOFTWARE.
31  *
32  *END_COPYRIGHT_BLOCK*/

33
34 package edu.rice.cs.drjava.ui;
35
36 import java.util.List JavaDoc;
37 import java.util.ArrayList JavaDoc;
38 import java.util.LinkedList JavaDoc;
39 import java.util.Enumeration JavaDoc;
40 import java.io.*;
41
42 import javax.swing.*;
43 import javax.swing.event.*;
44 import javax.swing.tree.*;
45 import javax.swing.table.*;
46 import javax.swing.text.*;
47 import javax.swing.plaf.*;
48 import javax.swing.plaf.basic.BasicToolTipUI JavaDoc;
49 import java.awt.event.*;
50 import java.awt.font.*;
51 import java.awt.*;
52
53 import edu.rice.cs.drjava.DrJava;
54 import edu.rice.cs.drjava.model.DocumentRegion;
55 import edu.rice.cs.drjava.model.SingleDisplayModel;
56 import edu.rice.cs.drjava.model.debug.*;
57 import edu.rice.cs.drjava.model.OpenDefinitionsDocument;
58 import edu.rice.cs.drjava.model.definitions.ClassNameNotFoundException;
59 import edu.rice.cs.drjava.config.*;
60 import edu.rice.cs.util.swing.Utilities;
61 import edu.rice.cs.util.UnexpectedException;
62 import edu.rice.cs.util.StringOps;
63 import edu.rice.cs.util.swing.RightClickMouseAdapter;
64
65 /** Panel for displaying regions in a tree sorted by class name and line number. This class is a swing class that
66   * should only be accessed from the event thread.
67   * @version $Id$
68   */

69 public abstract class RegionsTreePanel<R extends DocumentRegion> extends TabbedPanel {
70   protected JPanel _leftPane;
71   
72   protected DefaultMutableTreeNode _regionRootNode;
73   protected DefaultTreeModel _regTreeModel;
74   protected JTree _regTree;
75   protected String JavaDoc _title;
76   
77   protected JPopupMenu _regionPopupMenu;
78   
79   protected final SingleDisplayModel _model;
80   protected final MainFrame _frame;
81   
82   protected JPanel _buttonPanel;
83   
84   protected DefaultTreeCellRenderer dtcr;
85   
86   /** Constructs a new panel to display regions in a tree. This is swing view class and hence should only be accessed
87     * from the event thread.
88     * @param frame the MainFrame
89     * @param title title of the pane
90     */

91   public RegionsTreePanel(MainFrame frame, String JavaDoc title) {
92     super(frame, title);
93     _title = title;
94     this.setLayout(new BorderLayout());
95     
96     _frame = frame;
97     _model = frame.getModel();
98     
99     this.removeAll(); // override the behavior of TabbedPanel
100

101     // remake closePanel
102
_closePanel = new JPanel(new BorderLayout());
103     _closePanel.add(_closeButton, BorderLayout.NORTH);
104     
105     _leftPane = new JPanel(new BorderLayout());
106     _setupRegionTree();
107     
108     this.add(_leftPane, BorderLayout.CENTER);
109     
110     _buttonPanel = new JPanel(new BorderLayout());
111     _setupButtonPanel();
112     this.add(_buttonPanel, BorderLayout.EAST);
113     updateButtons();
114     
115     // Setup the color listeners.
116
_setColors(_regTree);
117   }
118   
119   /** Quick helper for setting up color listeners. */
120   private static void _setColors(Component c) {
121     new ForegroundColorListener(c);
122     new BackgroundColorListener(c);
123   }
124   
125   /** Close the pane. */
126   protected void _close() {
127     super._close();
128     updateButtons();
129   }
130   
131   /**
132    * Update the tree.
133    */

134   public boolean requestFocusInWindow() {
135     // Only change GUI from event-dispatching thread
136
Runnable JavaDoc doCommand = new Runnable JavaDoc() {
137       public void run() {
138         // Update all tree nodes
139
Enumeration JavaDoc documents = _regionRootNode.children();
140         boolean found = false;
141         while ((!found) && (documents.hasMoreElements())) {
142           DefaultMutableTreeNode doc = (DefaultMutableTreeNode)documents.nextElement();
143           // Find the correct start offset node for this region
144
Enumeration JavaDoc existingRegions = doc.children();
145           while (existingRegions.hasMoreElements()) {
146             DefaultMutableTreeNode existing = (DefaultMutableTreeNode)existingRegions.nextElement();
147             _regTreeModel.nodeChanged(existing);
148           }
149           _regTreeModel.nodeChanged(doc);
150         }
151         updateButtons();
152       }
153     };
154     Utilities.invokeLater(doCommand);
155     return super.requestFocusInWindow();
156   }
157   
158   /** Creates the region tree. */
159   private void _setupRegionTree() {
160     _regionRootNode = new DefaultMutableTreeNode(_title);
161     _regTreeModel = new DefaultTreeModel(_regionRootNode);
162     _regTree = new RegionTree(_regTreeModel);
163     _regTree.setEditable(false);
164     _regTree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
165     _regTree.setShowsRootHandles(true);
166     _regTree.setRootVisible(false);
167     _regTree.putClientProperty("JTree.lineStyle", "Angled");
168     _regTree.setScrollsOnExpand(true);
169     _regTree.addTreeSelectionListener(new TreeSelectionListener() {
170       public void valueChanged(TreeSelectionEvent e) {
171         updateButtons();
172       }
173     });
174     _regTree.addKeyListener(new KeyAdapter() {
175       public void keyPressed(KeyEvent e) {
176         if (e.getKeyCode() == KeyEvent.VK_ENTER) {
177           performDefaultAction();
178         }
179       }
180     });
181     
182     // Region tree cell renderer
183
dtcr = new RegionRenderer();
184     dtcr.setOpaque(false);
185     _setColors(dtcr);
186     _regTree.setCellRenderer(dtcr);
187
188     _leftPane.add(new JScrollPane(_regTree));
189
190     _initPopup();
191     
192     ToolTipManager.sharedInstance().registerComponent(_regTree);
193   }
194   
195   /** Update button state and text. Should be overridden if additional buttons are added besides "Go To", "Remove" and "Remove All". */
196   protected void updateButtons() {
197   }
198   
199   /** Adds config color support to DefaultTreeCellEditor. */
200   class RegionRenderer extends DefaultTreeCellRenderer {
201     
202     public void setBackground(Color c) {
203       this.setBackgroundNonSelectionColor(c);
204     }
205     
206     public void setForeground(Color c) {
207       this.setTextNonSelectionColor(c);
208     }
209     
210     private RegionRenderer() {
211       this.setTextSelectionColor(Color.black);
212       setLeafIcon(null);
213       setOpenIcon(null);
214       setClosedIcon(null);
215     }
216
217     /**
218      * Overrides the default renderer component to use proper coloring.
219      */

220     public Component getTreeCellRendererComponent(JTree tree, Object JavaDoc value, boolean isSelected, boolean isExpanded,
221                                                    boolean leaf, int row, boolean hasFocus) {
222       Component renderer = super.getTreeCellRendererComponent(tree, value, isSelected, isExpanded, leaf, row, hasFocus);
223       
224       if (renderer instanceof JComponent) { ((JComponent) renderer).setOpaque(false); }
225       
226       _setColors(renderer);
227       
228       // set tooltip
229
String JavaDoc tooltip = null;
230       if (DrJava.getConfig().getSetting(OptionConstants.SHOW_CODE_PREVIEW_POPUPS).booleanValue()) {
231         if (leaf) {
232           DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
233           if (node.getUserObject() instanceof RegionTreeUserObj) {
234             @SuppressWarnings JavaDoc("unchecked") R r = ((RegionTreeUserObj<R>)(node.getUserObject())).region();
235             
236             OpenDefinitionsDocument doc = r.getDocument();
237             doc.acquireReadLock();
238             try {
239               int lnr = doc.getLineOfOffset(r.getStartOffset())+1;
240               int startOffset = doc.getOffset(lnr-3);
241               if (startOffset<0) { startOffset = 0; }
242               int endOffset = doc.getOffset(lnr+3);
243               if (endOffset<0) { endOffset = doc.getLength()-1; }
244               
245               // convert to HTML (i.e. < to &lt; and > to &gt; and newlines to <br>)
246
String JavaDoc s = doc.getText(startOffset, endOffset-startOffset);
247               
248               // this highlights the actual region in red
249
int rStart = r.getStartOffset()-startOffset;
250               if (rStart<0) { rStart = 0; }
251               int rEnd = r.getEndOffset()-startOffset;
252               if (rEnd>s.length()) { rEnd = s.length(); }
253               if ((rStart<=s.length()) && (rEnd>=rStart)) {
254                 String JavaDoc t1 = StringOps.encodeHTML(s.substring(0,rStart));
255                 String JavaDoc t2 = StringOps.encodeHTML(s.substring(rStart,rEnd));
256                 String JavaDoc t3 = StringOps.encodeHTML(s.substring(rEnd));
257                 s = t1 + "<font color=#ff0000>" + t2 + "</font>" + t3;
258               }
259               else {
260                 s = StringOps.encodeHTML(s);
261               }
262               tooltip = "<html><pre>"+s+"</pre></html>";
263             }
264             catch(javax.swing.text.BadLocationException JavaDoc ble) { tooltip = null; /* just don't give a tool tip */ }
265             finally { doc.releaseReadLock(); }
266             setText(node.getUserObject().toString());
267             renderer = this;
268           }
269         }
270       }
271       setToolTipText(tooltip);
272       return renderer;
273     }
274   }
275   
276   /** Action performed when the Enter key is pressed. Should be overridden. */
277   protected void performDefaultAction() {
278   }
279   
280   /** Creates the buttons for controlling the regions. Should be overridden. */
281   protected JComponent[] makeButtons() {
282     return new JComponent[0];
283   }
284   
285   /** Creates the buttons for controlling the regions. */
286   private void _setupButtonPanel() {
287     JPanel mainButtons = new JPanel();
288     JPanel emptyPanel = new JPanel();
289     JPanel closeButtonPanel = new JPanel(new BorderLayout());
290     GridBagLayout gbLayout = new GridBagLayout();
291     GridBagConstraints c = new GridBagConstraints();
292     mainButtons.setLayout(gbLayout);
293     
294     JComponent[] buts = makeButtons();
295
296     closeButtonPanel.add(_closeButton, BorderLayout.NORTH);
297     for (JComponent b: buts) { mainButtons.add(b); }
298     mainButtons.add(emptyPanel);
299     
300     c.fill = GridBagConstraints.HORIZONTAL;
301     c.anchor = GridBagConstraints.NORTH;
302     c.gridwidth = GridBagConstraints.REMAINDER;
303     c.weightx = 1.0;
304
305     for (JComponent b: buts) { gbLayout.setConstraints(b, c); }
306     
307     c.fill = GridBagConstraints.BOTH;
308     c.anchor = GridBagConstraints.SOUTH;
309     c.gridheight = GridBagConstraints.REMAINDER;
310     c.weighty = 1.0;
311     
312     gbLayout.setConstraints(emptyPanel, c);
313     
314     _buttonPanel.add(mainButtons, BorderLayout.CENTER);
315     _buttonPanel.add(closeButtonPanel, BorderLayout.EAST);
316   }
317   
318   /** Makes the popup menu actions. Should be overridden. */
319   protected AbstractAction[] makePopupMenuActions() {
320     return null;
321   }
322   
323   /** Initializes the pop-up menu. */
324   private void _initPopup() {
325     _regionPopupMenu = new JPopupMenu(_title);
326     AbstractAction[] acts = makePopupMenuActions();
327     if (acts!=null) {
328       for (AbstractAction a: acts) {
329         _regionPopupMenu.add(a);
330       }
331       _regTree.addMouseListener(new RegionMouseAdapter());
332     }
333   }
334   
335   /** Gets the currently selected regions in the region tree, or an empty array if no regions are selected.
336    * @return list of selected regions in the tree
337    */

338   protected ArrayList JavaDoc<R> getSelectedRegions() {
339     ArrayList JavaDoc<R> regs = new ArrayList JavaDoc<R>();
340     TreePath[] paths = _regTree.getSelectionPaths();
341     if (paths!=null) {
342       for (TreePath path: paths) {
343         if (path != null && path.getPathCount() == 3) {
344           DefaultMutableTreeNode lineNode = (DefaultMutableTreeNode)path.getLastPathComponent();
345           @SuppressWarnings JavaDoc("unchecked") R r = ((RegionTreeUserObj<R>) lineNode.getUserObject()).region();
346           regs.add(r);
347         }
348       }
349     }
350     return regs;
351   }
352   
353   /** Go to region. */
354   protected void goToRegion() {
355     ArrayList JavaDoc<R> r = getSelectedRegions();
356     if (r.size() == 1) _frame.scrollToDocumentAndOffset(r.get(0).getDocument(), r.get(0).getStartOffset(), false);
357   }
358     
359   /** Add a region to the tree. Must be executed in event thread.
360     * @param r the region
361     */

362   public void addRegion(final R r) {
363     String JavaDoc name = "";
364     try { name = r.getDocument().getQualifiedClassName(); }
365     catch (ClassNameNotFoundException cnnfe) { name = r.getDocument().toString(); }
366
367     DefaultMutableTreeNode regDocNode = new DefaultMutableTreeNode(name);
368     
369     // Look for matching document node
370
// Raw type here due to Swing's use of raw types.
371
Enumeration JavaDoc documents = _regionRootNode.children();
372     boolean done = false;
373     while (!done && (documents.hasMoreElements())) {
374       DefaultMutableTreeNode doc = (DefaultMutableTreeNode)documents.nextElement();
375       if (doc.getUserObject().equals(regDocNode.getUserObject())) {
376         
377         // Create a new region in this node
378
// Sort regions by start offset.
379
// Raw type here due to Swing's use of raw types.
380
Enumeration JavaDoc existingRegions = doc.children();
381         while (existingRegions.hasMoreElements()) {
382           DefaultMutableTreeNode existing = (DefaultMutableTreeNode)existingRegions.nextElement();
383           
384           // if start offset of indexed regions is less than new region, continue
385
int ofs = r.getStartOffset();
386           if (((RegionTreeUserObj)existing.getUserObject()).region().getStartOffset() == ofs) {
387             // don't add, already there
388
// just make sure this node is visible
389
_regTree.scrollPathToVisible(new TreePath(existing));
390             done = true;
391             break;
392           }
393           else if (((RegionTreeUserObj)existing.getUserObject()).region().getStartOffset() > ofs) {
394             
395             // else, add to the list
396
DefaultMutableTreeNode newRegion = new DefaultMutableTreeNode(makeRegionTreeUserObj(r));
397             _regTreeModel.insertNodeInto(newRegion, doc, doc.getIndex(existing));
398             
399             // Make sure this node is visible
400
_regTree.scrollPathToVisible(new TreePath(newRegion.getPath()));
401             done = true;
402             break;
403           }
404         }
405         if (done) { break; }
406         
407         // if none are greater, add at the end
408
DefaultMutableTreeNode newRegion = new DefaultMutableTreeNode(makeRegionTreeUserObj(r));
409         _regTreeModel.insertNodeInto(newRegion, doc, doc.getChildCount());
410         
411         // Make sure this node is visible
412
_regTree.scrollPathToVisible(new TreePath(newRegion.getPath()));
413         done = true;
414         break;
415       }
416     }
417     
418     if (!done) {
419       // No matching document node was found, so create one
420
_regTreeModel.insertNodeInto(regDocNode, _regionRootNode, _regionRootNode.getChildCount());
421       DefaultMutableTreeNode newRegion = new DefaultMutableTreeNode(makeRegionTreeUserObj(r));
422       _regTreeModel.insertNodeInto(newRegion, regDocNode, regDocNode.getChildCount());
423       
424       // Make visible
425
TreePath pathToNewRegion = new TreePath(newRegion.getPath());
426       _regTree.scrollPathToVisible(pathToNewRegion);
427     }
428     
429     updateButtons();
430   }
431   
432   /** Remove a region from the tree. Must be executed in event thread.
433    * @param r the region
434    */

435   public void removeRegion(final R r) {
436     // Only change GUI from event-dispatching thread
437
Runnable JavaDoc doCommand = new Runnable JavaDoc() {
438       public void run() {
439         String JavaDoc name = "";
440         try {
441           name = r.getDocument().getQualifiedClassName();
442         }
443         catch (ClassNameNotFoundException cnnfe) {
444           name = r.getDocument().toString();
445         }
446         
447         DefaultMutableTreeNode regDocNode = new DefaultMutableTreeNode(name);
448         
449         // Find the document node for this region
450
Enumeration JavaDoc documents = _regionRootNode.children();
451         boolean found = false;
452         while ((!found) && (documents.hasMoreElements())) {
453           DefaultMutableTreeNode doc = (DefaultMutableTreeNode)documents.nextElement();
454           if (doc.getUserObject().equals(regDocNode.getUserObject())) {
455             // Find the correct start offset node for this region
456
Enumeration JavaDoc existingRegions = doc.children();
457             while (existingRegions.hasMoreElements()) {
458               DefaultMutableTreeNode existing = (DefaultMutableTreeNode)existingRegions.nextElement();
459               if (((RegionTreeUserObj)existing.getUserObject()).region().getStartOffset()==r.getStartOffset()) {
460                 _regTreeModel.removeNodeFromParent(existing);
461                 // notify
462
if (doc.getChildCount() == 0) {
463                   // this document has no more regions, remove it
464
_regTreeModel.removeNodeFromParent(doc);
465                 }
466                 found = true;
467                 break;
468               }
469             }
470           }
471         }
472         updateButtons();
473       }
474     };
475     Utilities.invokeLater(doCommand);
476   }
477   
478   /** Remove all regions for this document from the tree. Must be executed in event thread.
479    */

480   public void removeRegions(final OpenDefinitionsDocument odd) {
481     // Only change GUI from event-dispatching thread
482
Runnable JavaDoc doCommand = new Runnable JavaDoc() {
483       public void run() {
484         String JavaDoc name = "";
485         try {
486           name = odd.getQualifiedClassName();
487         }
488         catch (ClassNameNotFoundException cnnfe) {
489           name = odd.toString();
490         }
491         
492         DefaultMutableTreeNode regDocNode = new DefaultMutableTreeNode(name);
493         
494         // Find the document node for this region
495
Enumeration JavaDoc documents = _regionRootNode.children();
496         while (documents.hasMoreElements()) {
497           DefaultMutableTreeNode doc = (DefaultMutableTreeNode)documents.nextElement();
498           if (doc.getUserObject().equals(regDocNode.getUserObject())) {
499             while(doc.getChildCount()>0) {
500               DefaultMutableTreeNode existing = (DefaultMutableTreeNode)doc.getFirstChild();
501               @SuppressWarnings JavaDoc("unchecked") R r = ((RegionTreeUserObj<R>)existing.getUserObject()).region();
502               _regTreeModel.removeNodeFromParent(existing);
503             }
504             _regTreeModel.removeNodeFromParent(doc);
505           }
506         }
507         updateButtons();
508       }
509     };
510     Utilities.invokeLater(doCommand);
511   }
512   
513   /**
514    * Mouse adapter for the region tree.
515    */

516   protected class RegionMouseAdapter extends RightClickMouseAdapter {
517     protected void _popupAction(MouseEvent e) {
518       int x = e.getX();
519       int y = e.getY();
520       TreePath path = _regTree.getPathForLocation(x, y);
521       if (path != null && path.getPathCount() == 3) {
522         _regTree.setSelectionRow(_regTree.getRowForLocation(x, y));
523         _regionPopupMenu.show(e.getComponent(), x, y);
524       }
525     }
526     
527     public void mousePressed(MouseEvent e) {
528       super.mousePressed(e);
529       if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2) {
530         performDefaultAction();
531       }
532     }
533   }
534   
535   /** Factory method to create user objects put in the tree.
536    * If subclasses extend RegionTreeUserObj, they need to override this method. */

537   protected RegionTreeUserObj<R> makeRegionTreeUserObj(R r) {
538     return new RegionTreeUserObj<R>(r);
539   }
540   
541   protected class RegionTree extends JTree {
542     public RegionTree(DefaultTreeModel s) {
543       super(s);
544     }
545     
546     public void setForeground(Color c) {
547       super.setForeground(c);
548       if (dtcr != null) dtcr.setTextNonSelectionColor(c);
549     }
550     
551     public void setBackground(Color c) {
552       super.setBackground(c);
553       if (RegionsTreePanel.this != null && dtcr != null) dtcr.setBackgroundNonSelectionColor(c);
554     }
555   }
556   
557   /** Class that gets put into the tree. The toString() method determines what's displayed in the three. */
558   protected static class RegionTreeUserObj<R extends DocumentRegion> {
559     protected R _region;
560     public int lineNumber() { return _region.getDocument().getLineOfOffset(_region.getStartOffset())+1; }
561     public R region() { return _region; }
562     public RegionTreeUserObj(R r) { _region = r; }
563     public String JavaDoc toString() {
564       final StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
565       _region.getDocument().acquireReadLock();
566       try {
567         sb.append(lineNumber());
568         try {
569           sb.append(": ");
570           int length = Math.min(120, _region.getEndOffset()-_region.getStartOffset());
571           sb.append(_region.getDocument().getText(_region.getStartOffset(), length).trim());
572         } catch(BadLocationException bpe) { /* ignore, just don't display line */ }
573       } finally { _region.getDocument().releaseReadLock(); }
574       return sb.toString();
575     }
576     public boolean equals(Object JavaDoc other) {
577       @SuppressWarnings JavaDoc("unchecked") RegionTreeUserObj<R> o = (RegionTreeUserObj<R>)other;
578       return (o.region().getDocument().equals(region().getDocument())) &&
579         (o.region().getStartOffset()==region().getStartOffset()) &&
580         (o.region().getEndOffset()==region().getEndOffset());
581     }
582   }
583 }
584
Popular Tags