KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > xml > text > navigator > NavigatorContent


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.xml.text.navigator;
21
22 import java.awt.BorderLayout JavaDoc;
23 import java.awt.Color JavaDoc;
24 import java.awt.Container JavaDoc;
25 import java.awt.Graphics JavaDoc;
26 import java.awt.Toolkit JavaDoc;
27 import java.awt.event.ActionEvent JavaDoc;
28 import java.awt.event.KeyEvent JavaDoc;
29 import java.awt.event.MouseAdapter JavaDoc;
30 import java.awt.event.MouseEvent JavaDoc;
31 import java.awt.event.MouseListener JavaDoc;
32 import java.beans.PropertyChangeEvent JavaDoc;
33 import java.beans.PropertyChangeListener JavaDoc;
34 import java.io.IOException JavaDoc;
35 import java.lang.ref.WeakReference JavaDoc;
36 import java.lang.reflect.InvocationTargetException JavaDoc;
37 import java.util.WeakHashMap JavaDoc;
38 import javax.swing.AbstractAction JavaDoc;
39 import javax.swing.BorderFactory JavaDoc;
40 import javax.swing.Icon JavaDoc;
41 import javax.swing.ImageIcon JavaDoc;
42 import javax.swing.JEditorPane JavaDoc;
43 import javax.swing.JLabel JavaDoc;
44 import javax.swing.JMenuItem JavaDoc;
45 import javax.swing.JPanel JavaDoc;
46 import javax.swing.JPopupMenu JavaDoc;
47 import javax.swing.JScrollPane JavaDoc;
48 import javax.swing.JTree JavaDoc;
49 import javax.swing.KeyStroke JavaDoc;
50 import javax.swing.SwingConstants JavaDoc;
51 import javax.swing.SwingUtilities JavaDoc;
52 import javax.swing.ToolTipManager JavaDoc;
53 import javax.swing.border.EmptyBorder JavaDoc;
54 import javax.swing.event.ChangeEvent JavaDoc;
55 import javax.swing.text.Document JavaDoc;
56 import javax.swing.text.JTextComponent JavaDoc;
57 import javax.swing.tree.DefaultTreeModel JavaDoc;
58 import javax.swing.tree.DefaultTreeSelectionModel JavaDoc;
59 import javax.swing.tree.TreeModel JavaDoc;
60 import javax.swing.tree.TreeNode JavaDoc;
61 import javax.swing.tree.TreePath JavaDoc;
62 import javax.swing.tree.TreeSelectionModel JavaDoc;
63 import org.netbeans.editor.BaseDocument;
64 import org.netbeans.editor.EditorUI;
65 import org.netbeans.editor.ext.ExtEditorUI;
66 import org.netbeans.modules.editor.NbEditorUtilities;
67 import org.netbeans.modules.editor.structure.api.DocumentElement;
68 import org.netbeans.modules.editor.structure.api.DocumentModel;
69 import org.netbeans.modules.editor.structure.api.DocumentModelException;
70 import org.openide.ErrorManager;
71 import org.openide.cookies.EditorCookie;
72 import org.openide.cookies.EditorCookie.Observable;
73 import org.openide.loaders.DataObject;
74 import org.openide.nodes.Node;
75 import org.openide.text.CloneableEditorSupport;
76 import org.openide.text.DataEditorSupport;
77 import org.openide.util.Lookup.Template;
78 import org.openide.util.NbBundle;
79 import org.openide.util.RequestProcessor;
80 import org.openide.util.UserQuestionException;
81 import org.openide.util.Utilities;
82 import org.openide.windows.TopComponent;
83
84
85 /** XML Navigator UI component containing a tree of XML elements.
86  *
87  * @author Marek Fukala
88  * @version 1.0
89  */

90 public class NavigatorContent extends JPanel JavaDoc implements PropertyChangeListener JavaDoc {
91     
92     private static final boolean DEBUG = false;
93     private static NavigatorContent navigatorContentInstance = null;
94     
95     
96     public static synchronized NavigatorContent getDefault() {
97         if(navigatorContentInstance == null)
98             navigatorContentInstance = new NavigatorContent();
99         return navigatorContentInstance;
100     }
101     
102     //suppose we always have only one instance of the navigator panel at one time
103
//so using the static fields is OK. TheeNodeAdapter is reading these two
104
//fields and change it's look accordingly
105
static boolean showAttributes = true;
106     static boolean showContent = true;
107     
108     private JPanel JavaDoc active = null;
109     private final JPanel JavaDoc emptyPanel;
110     
111     private JLabel JavaDoc msgLabel;
112     
113     private DataObject peerDO = null;
114     
115     private WeakHashMap JavaDoc uiCache = new WeakHashMap JavaDoc();
116     
117     private boolean editorOpened = false;
118     
119     private Icon JavaDoc waitIcon;
120
121     private NavigatorContent() {
122         setLayout(new BorderLayout JavaDoc());
123         //init empty panel
124
setBackground(Color.WHITE);
125         emptyPanel = new JPanel JavaDoc();
126         emptyPanel.setBackground(Color.WHITE);
127         emptyPanel.setLayout(new BorderLayout JavaDoc());
128         msgLabel = new JLabel JavaDoc();
129         emptyPanel.add(msgLabel, BorderLayout.CENTER);
130     }
131     
132     public void navigate(DataObject d) {
133         if(peerDO != null && peerDO != d) {
134             //release the original document (see closeDocument() javadoc)
135
closeDocument(peerDO);
136         }
137         
138         EditorCookie ec = (EditorCookie)d.getCookie(EditorCookie.class);
139         if(ec == null) {
140             ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL, "The DataObject " + d.getName() + "(class=" + d.getClass().getName() + ") has no EditorCookie!?");
141         } else {
142             try {
143                 if(DEBUG) System.out.println("[xml navigator] navigating to DATAOBJECT " + d.hashCode());
144                 //test if the document is opened in editor
145
BaseDocument bdoc = (BaseDocument)ec.openDocument();
146                 //create & show UI
147
if(bdoc != null) {
148                     //there is something we can navigate in
149
navigate(d, bdoc);
150                     //remember the peer dataobject to be able the call EditorCookie.close() when closing navigator
151
this.peerDO = d;
152                     //check if the editor for the DO has an opened pane
153
editorOpened = ec.getOpenedPanes() != null && ec.getOpenedPanes().length > 0;
154                 }
155                 
156             }catch(UserQuestionException uqe) {
157                 //do not open a question dialog when the document is just loaded into the navigator
158
showDocumentTooLarge();
159             }catch(IOException JavaDoc e) {
160                 ErrorManager.getDefault().notify(e);
161             }
162         }
163     }
164     
165     public void navigate(final DataObject documentDO, final BaseDocument bdoc) {
166         if(DEBUG) System.out.println("[xml navigator] navigating to DOCUMENT " + bdoc.hashCode());
167         //called from AWT thread
168
showScanningPanel();
169         
170         //try to find the UI in the UIcache
171
final JPanel JavaDoc cachedPanel;
172         WeakReference JavaDoc panelWR = (WeakReference JavaDoc)uiCache.get(documentDO);
173         if(panelWR != null) {
174             NavigatorContentPanel cp = (NavigatorContentPanel)panelWR.get();
175             if(cp != null) {
176                 if(DEBUG) System.out.println("panel is cached");
177                 //test if the document associated with the panel is the same we got now
178
cachedPanel = bdoc == cp.getDocument() ? cp : null;
179                 if(cachedPanel == null) {
180                     if(DEBUG) System.out.println("but the document is different - creating a new UI...");
181                     if(DEBUG) System.out.println("the cached document : " + cp.getDocument());
182                     
183                     //remove the old mapping from the cache
184
uiCache.remove(documentDO);
185                 }
186             } else
187                 cachedPanel = null;
188         } else
189             cachedPanel = null;
190         
191         //get the model and create the new UI on background
192
RequestProcessor.getDefault().post(new Runnable JavaDoc() {
193             public void run() {
194                 //get document model for the file
195
try {
196                     final DocumentModel model;
197                     if(cachedPanel == null)
198                         model = DocumentModel.getDocumentModel(bdoc);
199                     else
200                         model = null; //if the panel is cached it holds a refs to the model - not need to init it again
201

202                     
203                     if(cachedPanel != null || model != null) {
204                         
205                         try {
206                             SwingUtilities.invokeAndWait(new Runnable JavaDoc() {
207                                 public void run() {
208                                     showWaitPanel();
209                                     JPanel JavaDoc panel = null;
210                                     if(cachedPanel == null) {
211                                         try {
212                                             //lock the model for modifications during the
213
//navigator tree creation
214
model.readLock();
215                                             
216                                             //cache the newly created panel
217
panel = new NavigatorContentPanel(model);
218                                             //use the document dataobject as a key since the document itself is very easily discarded and hence
219
//harly usable as a key of the WeakHashMap
220
uiCache.put(documentDO, new WeakReference JavaDoc(panel));
221                                             if(DEBUG) System.out.println("[xml navigator] panel created");
222                                             
223                                             //start to listen to the document property changes - we need to get know when the document is being closed
224
EditorCookie.Observable eco = (EditorCookie.Observable)documentDO.getCookie(EditorCookie.Observable.class);
225                                             if(eco != null) {
226                                                 eco.addPropertyChangeListener(NavigatorContent.this);
227                                             } else {
228                                                 ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL, "The DataObject " + documentDO.getName() + "(class=" + documentDO.getClass().getName() + ") has no EditorCookie.Observable!");
229                                             }
230                                         }finally{
231                                             //unlock the model
232
model.readUnlock();
233                                         }
234                                     } else {
235                                         panel = cachedPanel;
236                                         if(DEBUG) System.out.println("[xml navigator] panel gotten from cache");
237                                     }
238                                     
239                                     //paint the navigator UI
240
removeAll();
241                                     add(panel, BorderLayout.CENTER);
242                                     revalidate();
243                                     //panel.revalidate();
244
repaint();
245                                 }
246                             });
247                         }catch(InterruptedException JavaDoc ie) {
248                             ErrorManager.getDefault().notify(ErrorManager.WARNING, ie);
249                         }catch(InvocationTargetException JavaDoc ite) {
250                             ErrorManager.getDefault().notify(ErrorManager.ERROR, ite);
251                         }
252                     } else {
253                         //model is null => show message
254
showCannotNavigate();
255                     }
256                 }catch(DocumentModelException dme) {
257                     ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, dme);
258                 }
259             }
260         });
261     }
262     
263     public void release() {
264         removeAll();
265         repaint();
266         
267         closeDocument(peerDO);
268     }
269     
270     /** A hacky fix for XMLSyncSupport - I need to call EditorCookie.close when the navigator
271      * is deactivated and there is not view pane for the navigated document. Then a the synchronization
272      * support releases a strong reference to NbEditorDocument. */

273     private void closeDocument(DataObject dobj) {
274         if(dobj != null) {
275             EditorCookie ec = (EditorCookie)peerDO.getCookie(EditorCookie.class);
276             if(ec != null) {
277                 JEditorPane JavaDoc panes[] = ec.getOpenedPanes();
278                 //call EC.close() if there isn't any pane and the editor was opened
279
if((panes == null || panes.length == 0)) {
280                     ((EditorCookie.Observable)ec).removePropertyChangeListener(this);
281                     
282                     if(editorOpened) {
283                         ec.close();
284                         if(DEBUG) System.out.println("document instance for dataobject " + dobj.hashCode() + " closed.");
285                     }
286                 }
287                 editorOpened = false;
288             }
289         }
290     }
291     
292     public void showDocumentTooLarge() {
293         removeAll();
294         msgLabel.setForeground(Color.GRAY);
295         msgLabel.setText(NbBundle.getMessage(NavigatorContent.class, "LBL_TooLarge"));
296         msgLabel.setHorizontalAlignment(SwingConstants.CENTER);
297         add(emptyPanel, BorderLayout.CENTER);
298         repaint();
299     }
300     
301     public void showCannotNavigate() {
302         removeAll();
303         msgLabel.setIcon(null);
304         msgLabel.setForeground(Color.GRAY);
305         msgLabel.setText(NbBundle.getMessage(NavigatorContent.class, "LBL_CannotNavigate"));
306         msgLabel.setHorizontalAlignment(SwingConstants.CENTER);
307         add(emptyPanel, BorderLayout.CENTER);
308         repaint();
309     }
310     
311     private void showScanningPanel() {
312         removeAll();
313         if (waitIcon == null) {
314             waitIcon = new ImageIcon JavaDoc( Utilities.loadImage(
315             "org/netbeans/modules/xml/text/navigator/resources/wait.gif" ) ); //NOI18N
316
}
317         msgLabel.setIcon(waitIcon);
318         msgLabel.setHorizontalAlignment(SwingConstants.LEFT);
319         msgLabel.setForeground(Color.BLACK);
320         msgLabel.setText(NbBundle.getMessage(NavigatorContent.class, "LBL_Scan"));
321         add(emptyPanel, BorderLayout.NORTH);
322         repaint();
323     }
324     
325     
326     private void showWaitPanel() {
327         removeAll();
328         msgLabel.setIcon(null);
329         msgLabel.setForeground(Color.GRAY);
330         msgLabel.setHorizontalAlignment(SwingConstants.LEFT);
331         msgLabel.setText(NbBundle.getMessage(NavigatorContent.class, "LBL_Wait"));
332         add(emptyPanel, BorderLayout.NORTH);
333         repaint();
334     }
335     
336     public void propertyChange(PropertyChangeEvent JavaDoc evt) {
337         if(evt.getPropertyName() == EditorCookie.Observable.PROP_DOCUMENT) {
338             if(evt.getNewValue() == null) {
339                 final DataObject dobj = ((DataEditorSupport)evt.getSource()).getDataObject();
340                 if(dobj != null) {
341                     editorOpened = false;
342                     //document is being closed
343
if(DEBUG) System.out.println("document has been closed for DO: " + dobj.hashCode());
344                     
345                     //remove the property change listener from the DataObject's EditorSupport
346
EditorCookie ec = (EditorCookie)dobj.getCookie(EditorCookie.class);
347                     if(ec != null)
348                         ((EditorCookie.Observable)ec).removePropertyChangeListener(this);
349                     
350                     //and navigate the document again (must be called asynchronously
351
//otherwise the ClonableEditorSupport locks itself (new call to CES from CES.propertyChange))
352
SwingUtilities.invokeLater(new Runnable JavaDoc() {
353                         public void run() {
354                             if(dobj.isValid()) navigate(dobj);
355                         }
356                     });
357                 }
358             } else {
359                 //a new pane created
360
editorOpened = true;
361             }
362         }
363     }
364     
365     private class NavigatorContentPanel extends JPanel JavaDoc implements FiltersManager.FilterChangeListener {
366         
367         private JTree JavaDoc tree;
368         private FiltersManager filters;
369         private Document JavaDoc doc;
370         
371         public NavigatorContentPanel(DocumentModel dm) {
372             this.doc = dm.getDocument();
373             
374             setLayout(new BorderLayout JavaDoc());
375             //create the JTree pane
376
tree = new PatchedJTree();
377             TreeModel JavaDoc model = createTreeModel(dm);
378             tree.setModel(model);
379             //tree.setLargeModel(true);
380
tree.setShowsRootHandles(true);
381             tree.setRootVisible(false);
382             tree.setCellRenderer(new NavigatorTreeCellRenderer());
383             tree.putClientProperty("JTree.lineStyle", "Angled");
384             ToolTipManager.sharedInstance().registerComponent(tree);
385             
386             MouseListener JavaDoc ml = new MouseAdapter JavaDoc() {
387                 public void mousePressed(MouseEvent JavaDoc e) {
388                     int selRow = tree.getRowForLocation(e.getX(), e.getY());
389                     if(selRow != -1) {
390                         TreePath JavaDoc selPath = tree.getPathForLocation(e.getX(), e.getY());
391                         TreeNodeAdapter tna = (TreeNodeAdapter)selPath.getLastPathComponent();
392                         if(e.getClickCount() == 2)
393                             openAndFocusElement(tna, false);
394                         
395                         if(e.getClickCount() == 1)
396                             openAndFocusElement(tna, true); //select active line only
397

398                     }
399                 }
400             };
401             tree.addMouseListener(ml);
402             
403             final TreeSelectionModel JavaDoc selectionModel = new DefaultTreeSelectionModel JavaDoc();
404             selectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
405             tree.setSelectionModel(selectionModel);
406             tree.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "open"); // NOI18N
407
tree.getActionMap().put("open", new AbstractAction JavaDoc() { // NOI18N
408
public void actionPerformed(ActionEvent JavaDoc e) {
409                     TreePath JavaDoc selPath = selectionModel.getLeadSelectionPath();
410                     TreeNodeAdapter tna = (TreeNodeAdapter)selPath.getLastPathComponent();
411                     openAndFocusElement(tna, false);
412                 }
413             });
414             
415             JScrollPane JavaDoc treeView = new JScrollPane JavaDoc(tree);
416             treeView.setBorder(BorderFactory.createEmptyBorder());
417             treeView.setViewportBorder(BorderFactory.createEmptyBorder());
418             
419             add(treeView, BorderLayout.CENTER);
420             
421             //create the TapPanel
422
TapPanel filtersPanel = new TapPanel();
423             JLabel JavaDoc filtersLbl = new JLabel JavaDoc(NbBundle.getMessage(NavigatorContent.class, "LBL_Filter")); //NOI18N
424
filtersLbl.setBorder(new EmptyBorder JavaDoc(0, 5, 5, 0));
425             filtersPanel.add(filtersLbl);
426             filtersPanel.setOrientation(TapPanel.DOWN);
427             // tooltip
428
KeyStroke JavaDoc toggleKey = KeyStroke.getKeyStroke(KeyEvent.VK_T,
429                     Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
430             String JavaDoc keyText = org.openide.util.Utilities.keyToString(toggleKey);
431             filtersPanel.setToolTipText(NbBundle.getMessage(NavigatorContent.class, "TIP_TapPanel", keyText));
432             
433             //create FiltersManager
434
filters = createFilters();
435             //listen to filters changes
436
filters.hookChangeListener(this);
437             
438             filtersPanel.add(filters.getComponent());
439             
440             add(filtersPanel, BorderLayout.SOUTH);
441             
442             //add popup menu mouse listener
443
MouseListener JavaDoc pmml = new MouseAdapter JavaDoc() {
444                 public void mousePressed(MouseEvent JavaDoc e) {
445                     if(e.getClickCount() == 1 && e.getModifiers() == MouseEvent.BUTTON3_MASK) {
446                         //show popup
447
JPopupMenu JavaDoc pm = new JPopupMenu JavaDoc();
448                         JMenuItem JavaDoc[] items = new FilterActions(filters).createMenuItems();
449                         //add filter actions
450
for(int i = 0; i < items.length; i++) pm.add(items[i]);
451                         pm.pack();
452                         pm.show(tree, e.getX(), e.getY());
453                     }
454                 }
455             };
456             tree.addMouseListener(pmml);
457             
458             //expand all root elements which are tags
459
TreeNode JavaDoc rootNode = (TreeNode JavaDoc)model.getRoot();
460             for(int i = 0; i < rootNode.getChildCount(); i++) {
461                 TreeNode JavaDoc node = rootNode.getChildAt(i);
462                 if(node.getChildCount() > 0)
463                     tree.expandPath(new TreePath JavaDoc(new TreeNode JavaDoc[]{rootNode, node}));
464             }
465         }
466         
467         public Document JavaDoc getDocument() {
468             return this.doc;
469         }
470         
471         private void openAndFocusElement(final TreeNodeAdapter selected, final boolean selectLineOnly) {
472             BaseDocument bdoc = (BaseDocument)selected.getDocumentElement().getDocument();
473             DataObject dobj = NbEditorUtilities.getDataObject(bdoc);
474             if(dobj == null) return ;
475             
476             final EditorCookie.Observable ec = (EditorCookie.Observable)dobj.getCookie(EditorCookie.Observable.class);
477             if(ec == null) return ;
478             
479             SwingUtilities.invokeLater(new Runnable JavaDoc() {
480                 public void run() {
481                     JEditorPane JavaDoc[] panes = ec.getOpenedPanes();
482                     if (panes != null && panes.length > 0) {
483                         // editor already opened, so just select
484
selectElementInPane(panes[0], selected, !selectLineOnly);
485                     } else if(!selectLineOnly) {
486                         // editor not opened yet
487
ec.open();
488                         try {
489                             ec.openDocument(); //wait to editor to open
490
panes = ec.getOpenedPanes();
491                             if (panes != null && panes.length > 0) {
492                                 selectElementInPane(panes[0], selected, true);
493                             }
494                         }catch(IOException JavaDoc ioe) {
495                             ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ioe);
496                         }
497                     }
498                 }
499             });
500         }
501         
502         private void selectElementInPane(final JEditorPane JavaDoc pane, final TreeNodeAdapter tna, final boolean focus) {
503             RequestProcessor.getDefault().post(new Runnable JavaDoc() {
504                 public void run() {
505                     pane.setCaretPosition(tna.getDocumentElement().getStartOffset());
506                 }
507             });
508             if(focus) {
509                 // try to activate outer TopComponent
510
Container JavaDoc temp = pane;
511                 while (!(temp instanceof TopComponent)) {
512                     temp = temp.getParent();
513                 }
514                 ((TopComponent) temp).requestActive();
515             }
516         }
517         
518         private TreeModel JavaDoc createTreeModel(DocumentModel dm) {
519             DocumentElement rootElement = dm.getRootElement();
520             DefaultTreeModel JavaDoc dtm = new DefaultTreeModel JavaDoc(null);
521             TreeNodeAdapter rootTna = new TreeNodeAdapter(rootElement, dtm, tree, null);
522             dtm.setRoot(rootTna);
523             
524             return dtm;
525         }
526         
527         /** Creates filter descriptions and filters itself */
528         private FiltersManager createFilters() {
529             FiltersDescription desc = new FiltersDescription();
530             
531             desc.addFilter(ATTRIBUTES_FILTER,
532                     NbBundle.getMessage(NavigatorContent.class, "LBL_ShowAttributes"), //NOI18N
533
NbBundle.getMessage(NavigatorContent.class, "LBL_ShowAttributesTip"), //NOI18N
534
showAttributes,
535                     new ImageIcon JavaDoc(org.openide.util.Utilities.loadImage("org/netbeans/modules/xml/text/navigator/resources/a.png")), //NOI18N
536
null
537                     );
538             desc.addFilter(CONTENT_FILTER,
539                     NbBundle.getMessage(NavigatorContent.class, "LBL_ShowContent"), //NOI18N
540
NbBundle.getMessage(NavigatorContent.class, "LBL_ShowContentTip"), //NOI18N
541
showContent,
542                     new ImageIcon JavaDoc(org.openide.util.Utilities.loadImage("org/netbeans/modules/xml/text/navigator/resources/content.png")), //NOI18N
543
null
544                     );
545             
546             return FiltersDescription.createManager(desc);
547         }
548         
549         
550         public void filterStateChanged(ChangeEvent JavaDoc e) {
551             showAttributes = filters.isSelected(ATTRIBUTES_FILTER);
552             showContent = filters.isSelected(CONTENT_FILTER);
553             
554             tree.repaint();
555         }
556         
557         private class PatchedJTree extends JTree JavaDoc {
558             
559             private boolean firstPaint;
560             
561             public PatchedJTree() {
562                 super();
563                 firstPaint = true;
564             }
565             
566             /** Overriden to calculate correct row height before first paint */
567             public void paint(Graphics JavaDoc g) {
568                 if (firstPaint) {
569                     int height = g.getFontMetrics(getFont()).getHeight();
570                     setRowHeight(height + 2);
571                     firstPaint = false;
572                 }
573                 super.paint(g);
574             }
575             
576         }
577         
578         public static final String JavaDoc ATTRIBUTES_FILTER = "attrs";
579         public static final String JavaDoc CONTENT_FILTER = "content";
580         
581     }
582     
583 }
584
585
Popular Tags