KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > explorer > propertysheet > SheetTable


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.openide.explorer.propertysheet;
21
22 import java.awt.Color JavaDoc;
23 import java.awt.Component JavaDoc;
24 import java.awt.Container JavaDoc;
25 import java.awt.ContainerOrderFocusTraversalPolicy JavaDoc;
26 import java.awt.Dimension JavaDoc;
27 import java.awt.Graphics JavaDoc;
28 import java.awt.Insets JavaDoc;
29 import java.awt.KeyboardFocusManager JavaDoc;
30 import java.awt.Point JavaDoc;
31 import java.awt.Rectangle JavaDoc;
32 import java.awt.Toolkit JavaDoc;
33 import java.awt.datatransfer.DataFlavor JavaDoc;
34 import java.awt.datatransfer.Transferable JavaDoc;
35 import java.awt.datatransfer.UnsupportedFlavorException JavaDoc;
36 import java.awt.event.ActionEvent JavaDoc;
37 import java.awt.event.FocusEvent JavaDoc;
38 import java.awt.event.KeyEvent JavaDoc;
39 import java.awt.event.MouseEvent JavaDoc;
40 import java.beans.FeatureDescriptor JavaDoc;
41 import java.beans.PropertyEditor JavaDoc;
42 import java.io.IOException JavaDoc;
43 import java.io.InputStream JavaDoc;
44 import java.io.Reader JavaDoc;
45 import java.io.StringBufferInputStream JavaDoc;
46 import java.io.StringReader JavaDoc;
47 import java.util.EventObject JavaDoc;
48 import java.util.logging.Level JavaDoc;
49 import java.util.logging.Logger JavaDoc;
50 import javax.swing.AbstractAction JavaDoc;
51 import javax.swing.Action JavaDoc;
52 import javax.swing.ActionMap JavaDoc;
53 import javax.swing.BorderFactory JavaDoc;
54 import javax.swing.DefaultListSelectionModel JavaDoc;
55 import javax.swing.InputMap JavaDoc;
56 import javax.swing.JComponent JavaDoc;
57 import javax.swing.JDialog JavaDoc;
58 import javax.swing.JFrame JavaDoc;
59 import javax.swing.JTable JavaDoc;
60 import javax.swing.KeyStroke JavaDoc;
61 import javax.swing.ListSelectionModel JavaDoc;
62 import javax.swing.SwingUtilities JavaDoc;
63 import javax.swing.TransferHandler JavaDoc;
64 import javax.swing.UIManager JavaDoc;
65 import javax.swing.event.ChangeEvent JavaDoc;
66 import javax.swing.event.TableModelEvent JavaDoc;
67 import javax.swing.table.JTableHeader JavaDoc;
68 import javax.swing.table.TableCellEditor JavaDoc;
69 import javax.swing.table.TableCellRenderer JavaDoc;
70 import javax.swing.table.TableColumnModel JavaDoc;
71 import javax.swing.table.TableModel JavaDoc;
72 import org.openide.awt.HtmlRenderer;
73 import org.openide.nodes.Node;
74 import org.openide.nodes.Node.Property;
75 import org.openide.nodes.Node.PropertySet;
76 import org.openide.util.Exceptions;
77 import org.openide.util.NbBundle;
78
79 /**
80  * A JTable subclass that displays node properties. To set the properties,
81  * call <code>getPropertySetModel().setPropertySets()</code>. This class
82  * uses instance counts to track shared resources. Do NOT un-final this class.
83  * <p>
84  * This class implements only property-specific functionality; the row-selection
85  * painting logic, etc, are in the superclass BaseTable.
86  *
87  * @author Tim Boudreau
88  */

89 final class SheetTable extends BaseTable implements PropertySetModelListener, CustomEditorAction.Invoker {
90     /** Action key for right-arrow expansion of property sets */
91     private static final String JavaDoc ACTION_EXPAND = "expandSet"; //NOI18N
92

93     /** Action key for left-arrow closing of property sets */
94     private static final String JavaDoc ACTION_COLLAPSE = "collapseSet"; //NOI18N
95

96     /** Action key for invoking the custom editor */
97     private static final String JavaDoc ACTION_CUSTOM_EDITOR = "invokeCustomEditor"; //NOI18N
98

99     /** Action key for action to log the curent property editor class*/
100     private static final String JavaDoc ACTION_EDCLASS = "edclass"; //NOI18N
101

102     /** A reference count so the finalizer can release the shared editor and
103      * renderer instances (which hold onto some fairly heavy GUI components
104      * when no more instances are active */

105     private static int instanceCount = 0;
106
107     /** Flag to block calls to setModel, etc., after initialization */
108     private transient boolean initialized = false;
109
110     /** Field to hold last edited feature descriptor if state was stored */
111     private FeatureDescriptor JavaDoc storedFd = null;
112
113     /** Field to hold last editing state if state was stored */
114     private boolean wasEditing = false;
115
116     /** Field to hold partial user input if state was stored while editing */
117     private Object JavaDoc partialValue = null;
118
119     /** Fallback field storing the last selected row, in the case that the
120      * table changes and selection should be restored */

121     private int lastSelectedRow = -1;
122
123     /** Static sheetCellRenderer which will be shared by all instances of
124      * SheetTable */

125     private SheetCellRenderer renderer = null;
126
127     /** Static sheetCellEditor which will be shared by all instances of
128      * SheetTable */

129     private SheetCellEditor cellEditor = null;
130
131     /** Custom editor action used to invoke the custom editor from keyboard
132      * or button */

133     private Action JavaDoc customEditorAction = null;
134
135     /** Display name of the current node, for passing to the custom editor
136      * dialog for setting the title */

137     /** Action to collapse or expand a set when the user presses the left or right arrow */
138     private Action JavaDoc expandAction;
139
140     /** Action to collapse or expand a set when the user presses the left or right arrow */
141     private Action JavaDoc collapseAction;
142
143     /** For debugging, an action that prints the current selection's property editor class
144      * to the standard out */

145     private Action JavaDoc edClassAction;
146
147     /** Name used for the custom editor dialog, set by PropertySheet */
148     private String JavaDoc beanName;
149
150     /** Flag set when a custom editor is opening until it closes. This is
151      * used to shut off tooltips to avoid a Windows bug that when a tooltip
152      * appears, the window containing it will be fronted, moving the modal
153      * custom editor behind it. */

154     private boolean customEditorIsOpen = false;
155     private ReusablePropertyEnv reusableEnv = new ReusablePropertyEnv();
156     private ReusablePropertyModel reusableModel = new ReusablePropertyModel(reusableEnv);
157     boolean lastIncludeMargin = false;
158     private HtmlRenderer.Renderer htmlrenderer = null;
159
160     //***************Implementation of issue 9691 - restore editing state after failed edit (dlg shown) *********
161

162     /** field to keep a count of focus events - there will be two following a
163      * failed edit. EditingStopped will set this value to 2. FocusGained will
164      * decrement it. Doing almost anything else that touches the property sheet
165      * will reset it to -1. If it is 0 when a focusGained event occurs, editing
166      * will be restarted. This is less than ideal, but the code that shows the
167      * dialog will not start blocking the AWT queue until after focus has
168      * returned and the editor has been removed. We get one focusGained event as a
169      * result of the inplace editor being removed; the second is the user closing
170      * the dialog. */

171     int countDown = -1;
172     boolean lastFailed = false;
173
174     /** Creates a new instance of SheetTable */
175     public SheetTable() {
176         super(new SheetTableModel(), new SheetColumnModel(), new DefaultListSelectionModel JavaDoc());
177         setPropertySetModel(new PropertySetModelImpl());
178
179         //Set a default row height
180
setRowHeight(16);
181
182         //Show grid lines if no alternating color defined
183
setShowGrid(PropUtils.noAltBg());
184         setShowVerticalLines(PropUtils.noAltBg());
185         setShowHorizontalLines(PropUtils.noAltBg());
186         setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
187
188         if (!PropUtils.noAltBg()) {
189             setIntercellSpacing(new Dimension JavaDoc(0, 0));
190         }
191
192         setGridColor(PropUtils.getSetRendererColor());
193
194         Color JavaDoc c = UIManager.getColor("PropSheet.selectionBackground"); //NOI18N
195

196         if (c != null) {
197             setSelectionBackground(c);
198         }
199
200         c = UIManager.getColor("PropSheet.selectionForeground"); //NOI18N
201

202         if (c != null) {
203             setSelectionForeground(c);
204         }
205
206         getAccessibleContext().setAccessibleName(NbBundle.getMessage(SheetTable.class, "ACSN_SHEET_TABLE")); //NOI18N
207

208         getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(SheetTable.class, "ACSD_SHEET_TABLE")); //NOI18N
209

210         setTransferHandler(new SheetTableTransferHandler());
211
212         Color JavaDoc col = UIManager.getColor("netbeans.ps.background"); //NOI18N
213

214         if (col != null) {
215             setBackground(col);
216         }
217
218         setFocusTraversalPolicy(new STPolicy());
219         instanceCount++;
220     }
221
222     //************Shared infrastructure*****************************
223
protected void finalize() {
224         instanceCount--;
225
226         if (instanceCount == 0) {
227             renderer = null;
228             cellEditor = null;
229             cleanup();
230         }
231     }
232
233     /** Fetch the static render instance shared among tables */
234     SheetCellRenderer getRenderer() {
235         if (renderer == null) {
236             renderer = new SheetCellRenderer(true, reusableEnv, reusableModel);
237         }
238
239         return renderer;
240     }
241
242     /** Fetch the static editor instance shared among tables */
243     SheetCellEditor getEditor() {
244         if (cellEditor == null) {
245             cellEditor = new SheetCellEditor(getReusablePropertyEnv());
246         }
247
248         return cellEditor;
249     }
250
251     /****************Bean getters/setters*****************************************
252
253         /** Implement's Rochelle's suggestion of including the display name
254          * of the edited bean in the custom editor dlg title. SheetTable doesn't
255          * know what node it's displaying, so property sheet code sets this
256          * when it changes */

257     void setBeanName(String JavaDoc name) {
258         this.beanName = name;
259     }
260
261     /** Fetch the name of the currently displayed JavaBean */
262     public String JavaDoc getBeanName() {
263         return beanName;
264     }
265
266     /** Returns a reference to the static editor shared among all instances
267      * of SheetTable */

268     public TableCellEditor JavaDoc getCellEditor(int row, int column) {
269         return getEditor();
270     }
271
272     /** Returns a reference to the static renderer shared among all instances
273      * of SheetTable */

274     public TableCellRenderer JavaDoc getCellRenderer(int row, int column) {
275         return getRenderer();
276     }
277
278     //**********Overrides of model setters to disable changes that would break the impl******
279

280     /** Throws an UnsupportedOperationException when called by user code. Replacing
281      * the data model of property sheets is unsupported. You can change the model
282      * that determines what properties are shown - see <code>setPropertySetModel()</code>. */

283     public void setModel(TableModel JavaDoc model) {
284         if (initialized) {
285             throw new UnsupportedOperationException JavaDoc(
286                 "Changing the model of a property sheet table is not supported. If you want to change the set of properties, ordering or other characteristings, see setPropertySetModel()."
287             ); //NOI18N
288
}
289
290         super.setModel(model);
291     }
292
293     /** Throws an UnsupportedOperationException when called by user code. Replacing
294      * the column model of property sheets is unsupported.*/

295     public void setColumnModel(TableColumnModel JavaDoc model) {
296         if (initialized) {
297             throw new UnsupportedOperationException JavaDoc(
298                 "Changing the column model of a property sheet table is not supported. If you want to change the set of properties, ordering or other characteristings, see setPropertySetModel()."
299             ); //NOI18N
300
}
301
302         super.setColumnModel(model);
303     }
304
305     /** Throws an UnsupportedOperationException when called by user code. Replacing
306      * the selection model of property sheets not supported.*/

307     public void setSelectionModel(ListSelectionModel JavaDoc model) {
308         if (initialized) {
309             throw new UnsupportedOperationException JavaDoc(
310                 "Changing the selection model of a property sheet table is not supported. If you want to change the set of properties, ordering or other characteristings, see setPropertySetModel()."
311             ); //NOI18N
312
}
313
314         super.setSelectionModel(model);
315     }
316
317     /** Set the model which determines the ordering of properties and expansion
318      * state of embedded property sets. */

319     public void setPropertySetModel(PropertySetModel psm) {
320         PropertySetModel old = getSheetModel().getPropertySetModel();
321
322         if (old == psm) {
323             return;
324         }
325
326         if (old != null) {
327             old.removePropertySetModelListener(this);
328         }
329
330         getSheetModel().setPropertySetModel(psm);
331         psm.addPropertySetModelListener(this);
332     }
333
334     /** Convenience getter for the property set model. Delegates to the SheetModel. */
335     PropertySetModel getPropertySetModel() {
336         return getSheetModel().getPropertySetModel();
337     }
338
339     /** Convenience getter for the model as an instance of SheetTableModel. */
340     SheetTableModel getSheetModel() {
341         return (SheetTableModel) this.getModel();
342     }
343
344     /** Overridden to return null - some look and feels will want to create
345      * an empty header, and we don't want them to do that */

346     public JTableHeader JavaDoc getTableHeader() {
347         return null;
348     }
349
350     //******************Keyboard/mouse mgmt***********************************
351
protected void initKeysAndActions() {
352         super.initKeysAndActions();
353         unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0));
354         unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0));
355
356         expandAction = new ExpandAction();
357         collapseAction = new CollapseAction();
358         edClassAction = new EditorClassAction();
359
360         InputMap JavaDoc imp = getInputMap();
361         InputMap JavaDoc impAncestor = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
362         ActionMap JavaDoc am = getActionMap();
363         KeyStroke JavaDoc ks = KeyStroke.getKeyStroke(KeyEvent.VK_C, KeyEvent.CTRL_MASK);
364         imp.put(ks, null);
365
366         imp.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), ACTION_EXPAND);
367
368         imp.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), ACTION_COLLAPSE);
369
370         imp.put(
371             KeyStroke.getKeyStroke(
372                 KeyEvent.VK_HOME, KeyEvent.SHIFT_DOWN_MASK | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()
373             ), ACTION_EDCLASS
374         );
375
376         imp.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), ACTION_NEXT);
377
378         imp.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK), ACTION_PREV);
379
380         impAncestor.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, KeyEvent.CTRL_DOWN_MASK), ACTION_CUSTOM_EDITOR);
381
382         impAncestor.remove(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0));
383         impAncestor.remove(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0));
384
385         am.put(ACTION_EXPAND, expandAction);
386         am.put(ACTION_COLLAPSE, collapseAction);
387
388         am.put(ACTION_CUSTOM_EDITOR, getCustomEditorAction());
389         am.put(ACTION_EDCLASS, edClassAction);
390     }
391
392     Action JavaDoc getCustomEditorAction() {
393         if (customEditorAction == null) {
394             customEditorAction = new CustomEditorAction(this);
395         }
396
397         return customEditorAction;
398     }
399
400     /** Overridden to cast value to FeatureDescriptor and return true if the
401      * text matches its display name. The popup search field uses this method
402      * to check matches. */

403     protected boolean matchText(Object JavaDoc value, String JavaDoc text) {
404         if (value instanceof FeatureDescriptor JavaDoc) {
405             return ((FeatureDescriptor JavaDoc) value).getDisplayName().toUpperCase().startsWith(text.toUpperCase());
406         } else {
407             return false;
408         }
409     }
410
411     //******************Painting logic **********************************
412

413     /** Paint the table. After the super.paint() call, calls paintMargin() to fill
414      * in the left edge with the appropriate color, and then calls paintExpandableSets()
415      * to paint the property sets, which are not painted by the default painting
416      * methods because they need to be painted across two rows. */

417     public void paintComponent(Graphics JavaDoc g) {
418         boolean includeMargin = PropUtils.shouldDrawMargin(getPropertySetModel());
419
420         getRenderer().setIncludeMargin(includeMargin);
421         super.paintComponent(g);
422
423         if (!PropUtils.noAltBg()) {
424             paintCenterLine(g);
425         }
426
427         if (includeMargin) {
428             paintMargin(g);
429         }
430
431         paintExpandableSets(g);
432
433         lastIncludeMargin = includeMargin;
434     }
435
436     /** Workaround for excessive paints by SwingUtilities.paintComponent() */
437     private void paintComponent(Graphics JavaDoc g, Component JavaDoc c, int x, int y, int w, int h) {
438         c.setBounds(x, y, w, h);
439         g.translate(x, y);
440         c.paint(g);
441         g.translate(-x, -y);
442         c.setBounds(-w, -h, 0, 0);
443     }
444
445     /** Paints the center line in the property sheet if an alternate
446      * color has been specified, so the divider is visible */

447     private void paintCenterLine(Graphics JavaDoc g) {
448         Color JavaDoc c = PropUtils.getAltBg();
449         g.setColor(c);
450
451         int xpos = getColumn(SheetColumnModel.NAMES_IDENTIFIER).getWidth() - 1;
452         g.drawLine(xpos, 0, xpos, getHeight());
453     }
454
455     /** We only use a single listener on the selected node, PropertySheet.SheetPCListener,
456      * to centralize things. It will call this method if a property change is detected
457      * so that it can be repainted. */

458     void repaintProperty(String JavaDoc name) {
459         if (!isShowing()) {
460             return;
461         }
462
463         if (PropUtils.isLoggable(SheetTable.class)) {
464             PropUtils.log(SheetTable.class, "RepaintProperty: " + name);
465         }
466
467         PropertySetModel psm = getPropertySetModel();
468         int min = getFirstVisibleRow();
469
470         if (min == -1) {
471             return;
472         }
473
474         int max = min + getVisibleRowCount();
475
476         for (int i = min; i < max; i++) {
477             FeatureDescriptor JavaDoc fd = psm.getFeatureDescriptor(i);
478
479             if (fd.getName().equals(name)) {
480                 Rectangle JavaDoc r = getCellRect(i, 1, true);
481
482                 if (PropUtils.isLoggable(SheetTable.class)) {
483                     PropUtils.log(SheetTable.class, "Repainting " + r + " for property " + name);
484                 }
485
486                 repaint(r.x, r.y, r.width, r.height);
487
488                 return;
489             }
490         }
491
492         if (PropUtils.isLoggable(SheetTable.class)) {
493             PropUtils.log(SheetTable.class, "Property is either scrolled offscreen or property name is bogus: " + name);
494         }
495     }
496
497     /** Paint the outside margin where the spinners for expandable
498      * sets are. This should be derived from the standard control
499      * color. This method will overpaint the grid lines in this
500      * area. */

501     private void paintMargin(Graphics JavaDoc g) {
502         //Don't paint the margin for sorted modes
503
//fill the outer column with the set renderer color, per UI spec
504
g.setColor(PropUtils.getSetRendererColor());
505
506         int w = PropUtils.getMarginWidth();
507         int h = getHeight();
508
509         if (g.hitClip(0, 0, w, h)) {
510             g.fillRect(0, 0, w, h);
511         }
512     }
513
514     public Component JavaDoc prepareRenderer(TableCellRenderer JavaDoc renderer, int row, int col) {
515         Component JavaDoc result = super.prepareRenderer(renderer, row, col);
516
517         if ((row < 0) || (row >= getRowCount())) {
518             return result;
519         }
520
521         Object JavaDoc value = getValueAt(row, col);
522
523         if ((result != null) && value instanceof Property && (col == 1)) {
524             result.setEnabled(((Property) value).canWrite());
525         }
526
527         return result;
528     }
529
530     /** Paint the expandable sets. These are painted double width,
531      * across the entire width of the table. */

532     private void paintExpandableSets(Graphics JavaDoc g) {
533         int start = 0;
534         int end = getRowCount();
535
536         Insets JavaDoc ins = getInsets();
537
538         boolean canBeSelected = isKnownComponent(
539                 KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner()
540             );
541
542         for (int i = 0; i < end; i++) {
543             int idx = start + i;
544             Object JavaDoc value = getValueAt(idx, 0);
545
546             if (value instanceof PropertySet) {
547                 Rectangle JavaDoc r = getCellRect(idx, 0, false);
548                 r.x = ins.left;
549                 r.width = getWidth() - (ins.left + ins.right);
550
551                 if (g.hitClip(r.x, r.y, r.width, r.height)) {
552                     PropertySet ps = (PropertySet) value;
553
554                     String JavaDoc txt = ps.getHtmlDisplayName();
555                     boolean isHtml = txt != null;
556
557                     if (!isHtml) {
558                         txt = ps.getDisplayName();
559                     }
560
561                     if (htmlrenderer == null) {
562                         htmlrenderer = HtmlRenderer.createRenderer();
563                     }
564
565                     JComponent JavaDoc painter = (JComponent JavaDoc) htmlrenderer.getTableCellRendererComponent(
566                             this, txt, false, false, idx, 0
567                         );
568
569                     htmlrenderer.setHtml(isHtml);
570                     htmlrenderer.setParentFocused(true);
571
572                     htmlrenderer.setIconTextGap(2);
573
574                     htmlrenderer.setIcon(
575                         getPropertySetModel().isExpanded(ps) ? PropUtils.getExpandedIcon() : PropUtils.getCollapsedIcon()
576                     );
577
578                     boolean selected = canBeSelected && (getSelectedRow() == idx);
579
580                     if (!selected) {
581                         painter.setBackground(PropUtils.getSetRendererColor());
582                         painter.setForeground(PropUtils.getSetForegroundColor());
583                     } else {
584                         painter.setBackground(PropUtils.getSelectedSetRendererColor());
585                         painter.setForeground(PropUtils.getSelectedSetForegroundColor());
586                     }
587
588                     painter.setOpaque(true);
589
590                     paintComponent(g, painter, r.x, r.y, r.width, r.height);
591                 }
592             }
593         }
594     }
595
596     /** Overridden to check if the edit failed, and if so, set a focus event
597      * countdown for re-initiating editing */

598     public void editingStopped(ChangeEvent JavaDoc e) {
599         super.editingStopped(e);
600
601         //Po Ting's request for Rave - if commit on focus loss is on, all
602
//edits look like failures and trigger a new call to editCellAt()
603
if (!PropUtils.psCommitOnFocusLoss && !getEditor().isLastUpdateSuccessful()) {
604             //The last update failed, we're two focus events away from really
605
//having focus again - we'll get one, then the error dialog will
606
//steal focus. On the second one we've got focus back.
607
countDown = 2;
608         }
609     }
610
611     /** Initiate editing automatically - triggered by the focus event countdown */
612     private void autoEdit() {
613         editCellAt(getSelectedRow(), getSelectedColumn(), null);
614
615         if (editorComp != null) {
616             editorComp.requestFocus();
617         }
618
619         countDown = -1;
620     }
621
622     /** Overridden to clear the focus event countdown */
623     public void changeSelection(int row, int col, boolean a, boolean b) {
624         countDown = -1;
625         super.changeSelection(row, col, a, b);
626     }
627
628     /** Overridden to check the focus event countdown and initiate editing on
629      * the second focus event following a failed edit (dialog was shown) */

630     public void processFocusEvent(FocusEvent JavaDoc fe) {
631         super.processFocusEvent(fe);
632
633         if (fe.getID() == fe.FOCUS_GAINED) {
634             countDown--;
635
636             if (countDown == 0) {
637                 autoEdit();
638             }
639         }
640
641         if (
642             (fe.getID() == fe.FOCUS_GAINED) ||
643                 ((fe.getOppositeComponent() != null) && (fe.getID() == fe.FOCUS_LOST) &&
644                 !isAncestorOf(fe.getOppositeComponent()))
645         ) {
646             //Ensure the description goes back to the node description if
647
//we lose focus
648
fireChange();
649         }
650     }
651
652     protected void focusLostCancel() {
653         if (PropUtils.psCommitOnFocusLoss && isEditing()) {
654             getEditor().stopCellEditing();
655         } else {
656             super.focusLostCancel();
657         }
658     }
659
660     //**********************Miscellaneous**************************
661

662     /** Overridden to catch a mouse pressed event over the custom editor
663      * button and invoke the custom editor even if we do not have focus;
664      * otherwise, for example, in the options dialog, clicking from the
665      * tree to the table over the custom editor button will just set focus
666      * to the table, but will not initiate the custom editor dialog */

667     public void processMouseEvent(MouseEvent JavaDoc me) {
668         if (me.getID() == me.MOUSE_PRESSED && SwingUtilities.isLeftMouseButton(me) &&
669                 onCustomEditorButton(me) && !hasFocus()) {
670             if (PropUtils.psCommitOnFocusLoss && isEditing()) {
671                 getEditor().stopCellEditing();
672                 
673                 // #54211: it can happen that PropertySheet window is closed
674
// when previous property editing is finished (e.g. Form
675
// event properties) If this is the case don't try to edit
676
// newly selected property.
677
if (isGoingToBeClosed()) {
678                     return;
679                 }
680             }
681             
682             int row = rowAtPoint(me.getPoint());
683             int col = columnAtPoint(me.getPoint());
684             
685             if ((row != -1) && (col != -1)) {
686                 changeSelection(row, col, false, false);
687                 getCustomEditorAction().actionPerformed(
688                         new ActionEvent JavaDoc(this, ActionEvent.ACTION_PERFORMED, ACTION_CUSTOM_EDITOR)
689                         );
690                 me.consume();
691                 
692                 return;
693             }
694         }
695         
696         super.processMouseEvent(me);
697     }
698
699     /** Overridden to do nothing, the editor will take care of updating
700      * the value */

701     public void setValueAt(Object JavaDoc o, int row, int column) {
702         //do nothing
703
}
704
705     /** See if a component is one we know about or one the current editor
706      * knows about. This affects whether we paint as if focused or not, and
707      * is used to determine what kind of focus changes mean we should stop
708      * editing, and what kind are ok */

709     protected boolean isKnownComponent(Component JavaDoc c) {
710         boolean result = super.isKnownComponent(c);
711
712         if (result) {
713             return result;
714         }
715
716         if (c == null) {
717             return false;
718         }
719
720         if (c instanceof ButtonPanel) {
721             return true;
722         }
723
724         InplaceEditor ie = getEditor().getInplaceEditor();
725
726         if (ie != null) {
727             JComponent JavaDoc comp = ie.getComponent();
728
729             if (comp == c) {
730                 return true;
731             }
732
733             if (comp.isAncestorOf(c)) {
734                 return true;
735             }
736         }
737
738         if (c.getParent() instanceof ButtonPanel) {
739             return true;
740         }
741
742         if ((getParent() != null) && (getParent().isAncestorOf(c))) {
743             return true;
744         }
745
746         Container JavaDoc par = getParent();
747
748         if ((par != null) && par.isAncestorOf(c)) {
749             return true;
750         }
751
752         if (c instanceof InplaceEditor) {
753             return true;
754         }
755
756         InplaceEditor ine = getEditor().getInplaceEditor();
757
758         if (ine != null) {
759             return ine.isKnownComponent(c);
760         }
761
762         return false;
763     }
764
765     /** Returns true if a mouse event occured over the custom editor button.
766      * This is used to supply button specific tooltips and launch the custom
767      * editor without needing to instantiate a real button */

768     private boolean onCustomEditorButton(MouseEvent JavaDoc e) {
769         //see if we're in the approximate bounds of the custom editor button
770
Point JavaDoc pt = e.getPoint();
771         int row = rowAtPoint(pt);
772         int col = columnAtPoint(pt);
773         FeatureDescriptor JavaDoc fd = getSheetModel().getPropertySetModel().getFeatureDescriptor(row);
774         if( null == fd ) {
775             //prevent NPE when the activated Node has been destroyed and a new one hasn't been set yet
776
return false;
777         }
778
779         //see if the event happened over the custom editor button
780
boolean success;
781
782         if (PropUtils.noCustomButtons) {
783             //#41412 - impossible to invoke custom editor on props w/ no inline
784
//edit mode if the no custom buttons switch is set
785
success = false;
786         } else {
787             success = e.getX() > (getWidth() - PropUtils.getCustomButtonWidth());
788         }
789
790         //if it's a mouse button event, then we're not showing a tooltip, we're
791
//deciding if we should display a custom editor. For read-only props that
792
//support one, we should return true, since clicking the non-editable cell
793
//is not terribly useful.
794
if (
795             (e.getID() == MouseEvent.MOUSE_PRESSED) || (e.getID() == MouseEvent.MOUSE_RELEASED) ||
796                 (e.getID() == MouseEvent.MOUSE_CLICKED)
797         ) {
798             //We will show the custom editor for any click on the text value
799
//of a property that looks editable but sets canEditAsText to false -
800
//the click means the user is trying to edit something, so to just
801
//swallow the gesture is confusing
802
success |= Boolean.FALSE.equals(fd.getValue("canEditAsText"));
803
804             if (!success && fd instanceof Property) {
805                 PropertyEditor JavaDoc pe = PropUtils.getPropertyEditor((Property) fd);
806
807                 if ((pe != null) && pe.supportsCustomEditor()) {
808                     //Undocumented but used in Studio - in NB 3.5 and earlier, returning null from getAsText()
809
//was a way to make a property non-editable
810
success |= (pe.isPaintable() && (pe.getAsText() == null) && (pe.getTags() == null));
811                 }
812             }
813         }
814
815         try {
816             if (success) { //NOI18N
817

818                 if (fd instanceof Property && (col == 1)) {
819                     boolean supp = PropUtils.getPropertyEditor((Property) fd).supportsCustomEditor();
820
821                     return (supp);
822                 }
823             }
824         } catch (IllegalStateException JavaDoc ise) {
825             //See bugtraq 4941073 - if a property accessed via Reflection throws
826
//an unexpected exception (try customize bean on a vanilla GenericServlet
827
//to produce this) when the getter is accessed, then we are already
828
//displaying "Error fetching property value" in the value area of
829
//the propertysheet. No point in distracting the user with a
830
//stack trace - it's not our bug.
831
Logger.getLogger(SheetTable.class.getName()).log(Level.WARNING, null, ise);
832         }
833
834         return false;
835     }
836
837     /** Overridden to supply different tooltips depending on mouse position (name,
838      * value, custom editor button). Will HTML-ize long tooltips*/

839     public String JavaDoc getToolTipText(MouseEvent JavaDoc e) {
840         if (customEditorIsOpen) {
841             return null;
842         }
843
844         String JavaDoc result;
845         Point JavaDoc pt = e.getPoint();
846         int row = rowAtPoint(pt);
847         int col = columnAtPoint(pt);
848
849         if ((col == 1) && onCustomEditorButton(e)) {
850             result = NbBundle.getMessage(SheetTable.class, "CTL_EDBUTTON_TIP"); // NOI18N
851
} else {
852             result = getSheetModel().getDescriptionFor(row, col);
853
854             if ((col == 1) && (result != null) && (result.length() > 100)) {
855                 //e.g. Jesse's new file list property gives massive
856
//tooltips; break them up
857
result = PropUtils.createHtmlTooltip(
858                         getPropertySetModel().getFeatureDescriptor(row).getDisplayName(), result
859                     );
860             }
861         }
862
863         if ((result != null) && "".equals(result.trim())) {
864             result = null; // prevents 2x2 dot as a tooltip
865
}
866
867         return result;
868     }
869
870     /** Convenience method to get the currently selected property. Equivalent to calling
871      * <code>getSheetModel().getPropertySetModel().getFeatureDescriptor(getSelectedRow())
872      * </code>. This method will return null if the table does not have focus or editing
873      * is not in progress. */

874     public final FeatureDescriptor JavaDoc getSelection() {
875         return _getSelection();
876     }
877
878     /** Internal implementation of getSelection() which returns the selected feature
879      * descriptor whether or not the component has focus. */

880     public final FeatureDescriptor JavaDoc _getSelection() {
881         int i = getSelectedRow();
882         FeatureDescriptor JavaDoc result;
883
884         //Check bounds - a change can be fired after the model has been changed, but
885
//before the table has received the event and updated itself, in which case
886
//you get an AIOOBE
887
if (i < getPropertySetModel().getCount()) {
888             result = getSheetModel().getPropertySetModel().getFeatureDescriptor(getSelectedRow());
889         } else {
890             result = null;
891         }
892
893         return result;
894     }
895
896     //*********Implementation of editing*************************************
897

898     /**
899      * Overridden to do a bunch of property related things: cancel editing
900      * if the name cell was clicked; ignore duplicate requests; ignore edit
901      * requests if the user is currently dragging the center line; launch
902      * the custom editor dialog without entering edit mode if a click is
903      * over the custom editor button; expand/close property sets; directly
904      * toggle boolean values rather than rapidly instantiate and hide a
905      * checkbox editor
906      */

907     public boolean editCellAt(int row, int column, EventObject JavaDoc e) {
908         assert SwingUtilities.isEventDispatchThread();
909         enterEditRequest();
910
911         if( (editingRow == row) && isEditing() ) {
912             if( 0 == column ) {
913                 //click on name cell should stop editing
914
getEditor().stopCellEditing();
915                 removeEditor();
916             }
917             //discard edit requests if we're already editing that cell
918
exitEditRequest();
919
920             return false;
921         }
922
923         //issue 37584, there are some requests for commit on focus loss,
924
//so we'll try experimental support for this. Not sure it's a great
925
//idea, but might as well keep an open mind
926
if (PropUtils.psCommitOnFocusLoss && isEditing()) {
927             getEditor().stopCellEditing();
928
929             // #53870: it can happen that PropertySheet window is closed when
930
// previous property editing is finished (e.g. Form event properties)
931
// If this is the case don't try to edit newly selected property.
932
if (isGoingToBeClosed()) {
933                 return false;
934             }
935         }
936
937         if ((e instanceof MouseEvent JavaDoc) && (onCenterLine((MouseEvent JavaDoc) e))) {
938             //If it's a drag request, other code will handle it
939
exitEditRequest();
940
941             return false;
942         }
943
944         if ((e instanceof MouseEvent JavaDoc) && (onCustomEditorButton((MouseEvent JavaDoc) e))) {
945             if (PropUtils.isLoggable(SheetTable.class)) {
946                 PropUtils.log(SheetTable.class, "Got a mouse click on the " + "custom editor button"); //NOI18N
947
}
948
949             if (isEditing() && (editingRow != row)) {
950                 removeEditor();
951             }
952
953             //If it's a click on the custom editor button, just display the
954
//dialog, don't open an inplace editor
955
int prevSel = getSelectedRow();
956             changeSelection(row, column, false, false);
957
958             if (prevSel != -1) {
959                 paintRow(prevSel);
960             }
961
962             paintSelectionRow();
963             getCustomEditorAction().actionPerformed(new ActionEvent JavaDoc(this, 0, null));
964             exitEditRequest();
965
966             return false;
967         }
968
969         //Get the selected item
970
FeatureDescriptor JavaDoc fd = getPropertySetModel().getFeatureDescriptor(row);
971
972         //See if we got an edit trigger for a property set - if so,
973
//toggle its expanded state
974
if (fd instanceof PropertySet) {
975             //It was a legitimate click, so do stop editing and set the
976
//selection (otherwise selection will change but editor will remain)
977
if (isEditing()) {
978                 removeEditor();
979                 changeSelection(row, column, false, false);
980             }
981
982             maybeToggleExpanded(row, e);
983             exitEditRequest();
984
985             return false;
986         }
987
988         //Set the flag indicating we're starting to edit - affects paint
989
//and focus requests
990

991         /* boolean useRadioButtons = PropUtils.forceRadioButtons ||
992                     (fd.getValue ("stringValues") != null);
993          */

994         boolean useRadioButtons = (e instanceof MouseEvent JavaDoc && PropUtils.forceRadioButtons) ||
995             ((fd != null) && (fd.getValue("stringValues") != null));
996
997         //Special handling for boolean if checkbox - no need to create an
998
//editor that will be removed immediately, just toggles the value
999
//programmatically
1000
if (!useRadioButtons && (((column == 1) || e instanceof KeyEvent JavaDoc) && checkEditBoolean(row))) {
1001            //if checkEditBoolean returned true, then the value was toggled -
1002
//set the flag off and return
1003
exitEditRequest();
1004
1005            return false;
1006        }
1007
1008        boolean result = false;
1009
1010        try {
1011            //Try to start an actual edit
1012
result = super.editCellAt(row, column, e);
1013        } finally {
1014            exitEditRequest();
1015        }
1016
1017        return result;
1018    }
1019
1020    public void removeEditor() {
1021        enterEditorRemoveRequest();
1022
1023        try {
1024            // synchronized(getTreeLock()) {
1025
super.removeEditor();
1026
1027            //Make the editor detach its listeners and clear values in the
1028
//inplace editor since we're done with it
1029
getEditor().setInplaceEditor(null);
1030
1031            // }
1032
//Order of removal can cause the custom editor button to get focus even
1033
//though it's no longer onscreen, when the custom editor is removed
1034
// Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
1035
} finally {
1036            exitEditorRemoveRequest();
1037        }
1038    }
1039
1040    /**Overridden to do the assorted black magic by which one determines if
1041     * a property is editable */

1042    public boolean isCellEditable(int row, int column) {
1043        if (column == 0) {
1044            return false;
1045        }
1046
1047        FeatureDescriptor JavaDoc fd = getPropertySetModel().getFeatureDescriptor(row);
1048        boolean result;
1049
1050        if (fd instanceof PropertySet) {
1051            result = false;
1052        } else {
1053            Property p = (Property) fd;
1054            result = p.canWrite();
1055
1056            if (result) {
1057                Object JavaDoc val = p.getValue("canEditAsText"); //NOI18N
1058

1059                if (val != null) {
1060                    result &= Boolean.TRUE.equals(val);
1061                }
1062            }
1063        }
1064
1065        return result;
1066    }
1067
1068    /** Toggle the expanded state of a property set if either the event
1069     * was a double click in the title area, a single click in the spinner
1070     * area, or a keyboard event. */

1071    private void maybeToggleExpanded(int row, EventObject JavaDoc e) {
1072        boolean doExpand = true;
1073
1074        //If it's a mouse event, we need to check if it's a double click.
1075
if (e instanceof MouseEvent JavaDoc) {
1076            MouseEvent JavaDoc me = (MouseEvent JavaDoc) e;
1077            doExpand = me.getClickCount() > 1;
1078
1079            //If not a double click, allow single click in the spinner margin
1080
if (!doExpand) {
1081                //marginWidth will definitely be initialized, you can't
1082
//click something that isn't on the screen
1083
doExpand = me.getPoint().x <= PropUtils.getMarginWidth();
1084            }
1085        }
1086
1087        if (doExpand) {
1088            toggleExpanded(row);
1089        }
1090    }
1091
1092    /** Toggle the expanded state of a property set. If editing, the edit is
1093     * cancelled. */

1094    private void toggleExpanded(int index) {
1095        if (isEditing()) {
1096            getEditor().cancelCellEditing();
1097        }
1098
1099        PropertySetModel psm = getSheetModel().getPropertySetModel();
1100        psm.toggleExpanded(index);
1101    }
1102
1103    /** In the case that an edit request is made on a boolean checkbox property, an
1104     * edit request should simply toggle its state without instantiating a custom
1105     * editor component. Returns true if the state was toggled, in which case the
1106     * editor instantiation portion of editCellAt() should be aborted */

1107    boolean checkEditBoolean(int row) {
1108        FeatureDescriptor JavaDoc fd = getSheetModel().getPropertySetModel().getFeatureDescriptor(row);
1109
1110        if (fd.getValue("stringValues") != null) {
1111            return false; //NOI18N
1112
}
1113
1114        Property p = (fd instanceof Property) ? (Property) fd : null;
1115
1116        if (p != null) {
1117            Class JavaDoc c = p.getValueType();
1118
1119            //only do this if the property is supplying no special values for
1120
//the tags - if it is, we are using the radio button renderer
1121
if ((c == Boolean JavaDoc.class) || (c == boolean.class)) {
1122                if (!isCellEditable(row, 1)) {
1123                    return true;
1124                }
1125
1126                //Okay, try to toggle it
1127
try {
1128                    Boolean JavaDoc b = null;
1129
1130                    //get the current value
1131
try {
1132                        b = (Boolean JavaDoc) p.getValue();
1133                    } catch (ProxyNode.DifferentValuesException dve) {
1134                        //If we're represeting conflicting multi-selected
1135
//properties, we'll make them both true when we toggle
1136
b = Boolean.FALSE;
1137                    }
1138
1139                    if (isEditing()) {
1140                        removeEditor();
1141                    }
1142
1143                    changeSelection(row, 1, false, false);
1144
1145                    //Toggle the value
1146
Boolean JavaDoc newValue = ((b == null) || Boolean.FALSE.equals(b)) ? Boolean.TRUE : Boolean.FALSE;
1147                    p.setValue(newValue);
1148
1149                    //Force an event so we'll repaint
1150
/*
1151                    tableChanged(new TableModelEvent (getSheetModel(), row,
1152                        row, 1, TableModelEvent.UPDATE));
1153                     */

1154                    paintRow(row);
1155
1156                    return true;
1157                } catch (Exception JavaDoc ex) {
1158                    //Something wrong, log it
1159
Exceptions.printStackTrace(ex);
1160                }
1161            }
1162        }
1163
1164        return false;
1165    }
1166
1167    /** Overridden to set the colors apropriately - we always want the editor
1168    * to appear selected */

1169    public Component JavaDoc prepareEditor(TableCellEditor JavaDoc editor, int row, int col) {
1170        if (editor == null) {
1171            return null;
1172        }
1173
1174        Component JavaDoc result = super.prepareEditor(editor, row, col);
1175
1176        if (result == null) {
1177            return null;
1178        }
1179
1180        //Usually result == ine, but custom impls may not be
1181
InplaceEditor ine = getEditor().getInplaceEditor();
1182
1183        if (ine.supportsTextEntry()) {
1184            result.setBackground(PropUtils.getTextFieldBackground());
1185            result.setForeground(PropUtils.getTextFieldForeground());
1186        }
1187
1188        if (result instanceof JComponent JavaDoc) {
1189            //unlikely that it won't be
1190
((JComponent JavaDoc) result).setBorder(BorderFactory.createEmptyBorder(0, PropUtils.getTextMargin(), 0, 0));
1191        }
1192
1193        return result;
1194    }
1195
1196    //***********Methods for storing state if a recoverable change is happening****
1197

1198    /** Overridden to store some data in the event of a recoverable change,
1199     * such as the row currently being edited */

1200    public void tableChanged(TableModelEvent JavaDoc e) {
1201        boolean ed = isEditing();
1202        lastSelectedRow = ed ? getEditingRow() : getSelectionModel().getAnchorSelectionIndex();
1203
1204        if (ed) {
1205            getEditor().stopCellEditing();
1206        }
1207
1208        super.tableChanged(e);
1209        restoreEditingState();
1210    }
1211
1212    /** Temporarily store the currently edited feature descriptor and partial
1213     * value from the editor. This info is used to restore the editing state
1214     * after temporary losses of focus and recoverable changes like reordering
1215     * the model, or changes that derive from the underlying node */

1216    void saveEditingState() {
1217        storedFd = _getSelection();
1218
1219        if (isEditing()) {
1220            InplaceEditor ine = getEditor().getInplaceEditor();
1221
1222            if (ine != null) {
1223                partialValue = ine.getValue();
1224            }
1225        }
1226    }
1227
1228    /** Restore the previous editing state, if the previously edited
1229     * FeatureDescriptor is still available for editing */

1230    void restoreEditingState() {
1231        int idx = indexOfLastSelected();
1232        boolean canResumeEditing = idx != -1;
1233
1234        if (!canResumeEditing) {
1235            idx = lastSelectedRow;
1236        }
1237
1238        if (idx == -1) {
1239            clearSavedEditingState();
1240
1241            return;
1242        }
1243
1244        if (idx < getRowCount()) {
1245            changeSelection(idx, 1, false, false);
1246
1247            if ((canResumeEditing) && wasEditing) {
1248                editCellAt(idx, 1);
1249
1250                InplaceEditor ine = getEditor().getInplaceEditor();
1251
1252                if ((ine != null) && (partialValue != null)) {
1253                    ine.setValue(partialValue);
1254                }
1255            }
1256        }
1257
1258        clearSavedEditingState();
1259    }
1260
1261    /** Clear saved editing data, so no memory leaks can occur */
1262    private void clearSavedEditingState() {
1263        storedFd = null;
1264        wasEditing = false;
1265        partialValue = null;
1266    }
1267
1268    /** Find the current index of the last edited FeatureDescriptor, to
1269     * figure out in which cell to restore the editing state */

1270    private int indexOfLastSelected() {
1271        if (storedFd == null) {
1272            return -1;
1273        }
1274
1275        PropertySetModel mdl = getPropertySetModel();
1276        int idx = mdl.indexOf(storedFd);
1277        storedFd = null;
1278
1279        return idx;
1280    }
1281
1282    //*************PropertySetModelListener implementation ******************
1283

1284    /** If we know a change is going to happen, try to store the current
1285     * state to restore after the change is completed. Reordering of
1286     * properties and addition of properties by the underlying node can
1287     * trigger this. Since the PropertySetModel has a cache of current
1288     * properties, it can call this while its internal state is still
1289     * intact */

1290    public void pendingChange(PropertySetModelEvent e) {
1291        if (e.isReordering()) {
1292            wasEditing = isEditing();
1293            saveEditingState();
1294        } else {
1295            storedFd = null;
1296            wasEditing = false;
1297            partialValue = null;
1298        }
1299    }
1300
1301    public void boundedChange(PropertySetModelEvent e) {
1302        //Do nothing, we'll get notification from the TableModel
1303
}
1304
1305    public void wholesaleChange(PropertySetModelEvent e) {
1306        //Do nothing, we'll get notification from the TableModel
1307
}
1308
1309    //*************CustomEditorAction.Invoker implementation ******************
1310
//Generally, EditablePropertyDisplayer does a lot more with this
1311
//interface than SheetTable needs to
1312

1313    /** Returns the content pane of our owner, so as to display the wait
1314     * cursor while the dialog is being invoked */

1315    public Component JavaDoc getCursorChangeComponent() {
1316        Container JavaDoc cont = SheetTable.this.getTopLevelAncestor();
1317
1318        return (cont instanceof JFrame JavaDoc) ? ((JFrame JavaDoc) cont).getContentPane()
1319                                        : ((cont instanceof JDialog JavaDoc) ? ((JDialog JavaDoc) cont).getContentPane() : cont);
1320    }
1321
1322    /** If we have been editing and the user has typed something, fetch this
1323     * value to use in the custom editor */

1324    public Object JavaDoc getPartialValue() {
1325        Object JavaDoc partialValue = null;
1326
1327        if (isEditing() && (editingRow == getSelectedRow())) {
1328            InplaceEditor ine = getEditor().getInplaceEditor();
1329
1330            if (ine != null) {
1331                partialValue = ine.getValue();
1332
1333                //reset the inplace editor so the value is not taken when the editor
1334
//is closed
1335
ine.reset();
1336                getEditor().cancelCellEditing();
1337            }
1338        } else {
1339            partialValue = null;
1340
1341            if (isEditing()) {
1342                removeEditor();
1343            }
1344        }
1345
1346        return partialValue;
1347    }
1348
1349    /** Restarts inline edit mode if the the preceding custom edit failed */
1350    public void editorClosed() {
1351        if (lastFailed) {
1352            editCellAt(getSelectedRow(), 1, null);
1353        }
1354
1355        repaint();
1356        customEditorIsOpen = false;
1357    }
1358
1359    public void editorOpened() {
1360        //Make sure it's painted as non-focused
1361
paintSelectionRow();
1362        customEditorIsOpen = true;
1363    }
1364
1365    public void editorOpening() {
1366        lastFailed = false;
1367        customEditorIsOpen = true;
1368    }
1369
1370    public void valueChanged(java.beans.PropertyEditor JavaDoc editor) {
1371        lastFailed = false;
1372    }
1373
1374    public boolean allowInvoke() {
1375        return true;
1376    }
1377
1378    public void failed() {
1379        lastFailed = true;
1380    }
1381
1382    public boolean wantAllChanges() {
1383        return false;
1384    }
1385
1386    public ReusablePropertyEnv getReusablePropertyEnv() {
1387        return reusableEnv;
1388    }
1389
1390    public ReusablePropertyModel getReusablePropertyModel() {
1391        return reusableModel;
1392    }
1393
1394    private boolean isGoingToBeClosed() {
1395        // TODO mkrauskopf: try to find better way for the case that
1396
// PropertySheet is going to be removed (note that isShowing, isVisible,
1397
// ... methods return still true when this method is called)
1398
return getRowCount() <= 0;
1399    }
1400
1401    //*************Actions bound to the keyboard ******************
1402
private class ExpandAction extends AbstractAction JavaDoc {
1403        public ExpandAction() {
1404            super(ACTION_EXPAND);
1405        }
1406
1407        public void actionPerformed(ActionEvent JavaDoc ae) {
1408            FeatureDescriptor JavaDoc fd = _getSelection();
1409
1410            if (fd instanceof PropertySet) {
1411                int row = SheetTable.this.getSelectedRow();
1412                boolean b = getPropertySetModel().isExpanded(fd);
1413
1414                if (b) {
1415                    toggleExpanded(row);
1416                }
1417            }
1418        }
1419
1420        public boolean isEnabled() {
1421            return _getSelection() instanceof PropertySet;
1422        }
1423    }
1424
1425    private class CollapseAction extends AbstractAction JavaDoc {
1426        public CollapseAction() {
1427            super(ACTION_COLLAPSE);
1428        }
1429
1430        public void actionPerformed(ActionEvent JavaDoc ae) {
1431            FeatureDescriptor JavaDoc fd = _getSelection();
1432
1433            if (fd instanceof PropertySet) {
1434                int row = SheetTable.this.getSelectedRow();
1435                boolean b = getPropertySetModel().isExpanded(fd);
1436
1437                if (!b) {
1438                    toggleExpanded(row);
1439                }
1440            }
1441        }
1442
1443        public boolean isEnabled() {
1444            boolean result = _getSelection() instanceof PropertySet;
1445
1446            return result;
1447        }
1448    }
1449
1450    private class EditorClassAction extends AbstractAction JavaDoc {
1451        public EditorClassAction() {
1452            super(ACTION_EDCLASS);
1453        }
1454
1455        public void actionPerformed(ActionEvent JavaDoc ae) {
1456            int i = getSelectedRow();
1457
1458            if (i != -1) {
1459                FeatureDescriptor JavaDoc fd = getPropertySetModel().getFeatureDescriptor(i);
1460
1461                if (fd instanceof Property) {
1462                    java.beans.PropertyEditor JavaDoc ped = PropUtils.getPropertyEditor((Property) fd);
1463                    System.err.println(ped.getClass().getName());
1464                } else {
1465                    System.err.println("PropertySets - no editor"); //NOI18N
1466
}
1467            } else {
1468                System.err.println("No selection"); //NOI18N
1469
}
1470        }
1471
1472        public boolean isEnabled() {
1473            return getSelectedRow() != -1;
1474        }
1475    }
1476
1477    private class STPolicy extends ContainerOrderFocusTraversalPolicy JavaDoc {
1478        public Component JavaDoc getComponentAfter(Container JavaDoc focusCycleRoot, Component JavaDoc aComponent) {
1479            if (inEditorRemoveRequest()) {
1480                return SheetTable.this;
1481            } else {
1482                Component JavaDoc result = super.getComponentAfter(focusCycleRoot, aComponent);
1483
1484                return result;
1485            }
1486        }
1487
1488        public Component JavaDoc getComponentBefore(Container JavaDoc focusCycleRoot, Component JavaDoc aComponent) {
1489            if (inEditorRemoveRequest()) {
1490                return SheetTable.this;
1491            } else {
1492                return super.getComponentBefore(focusCycleRoot, aComponent);
1493            }
1494        }
1495
1496        public Component JavaDoc getFirstComponent(Container JavaDoc focusCycleRoot) {
1497            if (!inEditorRemoveRequest() && isEditing()) {
1498                return editorComp;
1499            } else {
1500                return SheetTable.this;
1501            }
1502        }
1503
1504        public Component JavaDoc getDefaultComponent(Container JavaDoc focusCycleRoot) {
1505            if (!inEditorRemoveRequest() && isEditing() && editorComp.isShowing()) {
1506                return editorComp;
1507            } else {
1508                return SheetTable.this;
1509            }
1510        }
1511
1512        protected boolean accept(Component JavaDoc aComponent) {
1513            //Do not allow focus to go to a child of the editor we're using if
1514
//we are in the process of removing the editor
1515
if (isEditing() && inEditorRemoveRequest()) {
1516                InplaceEditor ine = getEditor().getInplaceEditor();
1517
1518                if (ine != null) {
1519                    if ((aComponent == ine.getComponent()) || ine.isKnownComponent(aComponent)) {
1520                        return false;
1521                    }
1522                }
1523            }
1524
1525            return super.accept(aComponent) && aComponent.isShowing();
1526        }
1527    }
1528
1529    private static class SheetTableTransferHandler extends TransferHandler JavaDoc {
1530        protected Transferable JavaDoc createTransferable(JComponent JavaDoc c) {
1531            if (c instanceof SheetTable) {
1532                SheetTable table = (SheetTable) c;
1533                FeatureDescriptor JavaDoc fd = table.getSelection();
1534
1535                if (fd == null) {
1536                    return null;
1537                }
1538
1539                String JavaDoc res = fd.getDisplayName();
1540
1541                if (fd instanceof Node.Property) {
1542                    Node.Property prop = (Node.Property) fd;
1543                    res += ("\t" + PropUtils.getPropertyEditor(prop).getAsText());
1544                }
1545
1546                return new SheetTableTransferable(res);
1547            }
1548
1549            return null;
1550        }
1551
1552        public int getSourceActions(JComponent JavaDoc c) {
1553            return COPY;
1554        }
1555    }
1556
1557    /**
1558     * Transferable implementation for SheetTable.
1559     */

1560    private static class SheetTableTransferable implements Transferable JavaDoc {
1561        private static DataFlavor JavaDoc[] stringFlavors;
1562        private static DataFlavor JavaDoc[] plainFlavors;
1563
1564        static {
1565            try {
1566                plainFlavors = new DataFlavor JavaDoc[3];
1567                plainFlavors[0] = new DataFlavor JavaDoc("text/plain;class=java.lang.String"); // NOI18N
1568
plainFlavors[1] = new DataFlavor JavaDoc("text/plain;class=java.io.Reader"); // NOI18N
1569
// XXX isn't this just DataFlavor.plainTextFlavor?
1570
plainFlavors[2] = new DataFlavor JavaDoc("text/plain;charset=unicode;class=java.io.InputStream"); // NOI18N
1571

1572                stringFlavors = new DataFlavor JavaDoc[2];
1573                stringFlavors[0] = new DataFlavor JavaDoc(DataFlavor.javaJVMLocalObjectMimeType + ";class=java.lang.String"); // NOI18N
1574
stringFlavors[1] = DataFlavor.stringFlavor;
1575            } catch (ClassNotFoundException JavaDoc cle) {
1576                assert false : cle;
1577            }
1578        }
1579
1580        protected String JavaDoc plainData;
1581
1582        public SheetTableTransferable(String JavaDoc plainData) {
1583            this.plainData = plainData;
1584        }
1585
1586        public DataFlavor JavaDoc[] getTransferDataFlavors() {
1587            int nPlain = (isPlainSupported()) ? plainFlavors.length : 0;
1588            int nString = (isPlainSupported()) ? stringFlavors.length : 0;
1589            int nFlavors = nPlain + nString;
1590            DataFlavor JavaDoc[] flavors = new DataFlavor JavaDoc[nFlavors];
1591
1592            // fill in the array
1593
int nDone = 0;
1594
1595            if (nPlain > 0) {
1596                System.arraycopy(plainFlavors, 0, flavors, nDone, nPlain);
1597                nDone += nPlain;
1598            }
1599
1600            if (nString > 0) {
1601                System.arraycopy(stringFlavors, 0, flavors, nDone, nString);
1602                nDone += nString;
1603            }
1604
1605            return flavors;
1606        }
1607
1608        public boolean isDataFlavorSupported(DataFlavor JavaDoc flavor) {
1609            DataFlavor JavaDoc[] flavors = getTransferDataFlavors();
1610
1611            for (int i = 0; i < flavors.length; i++) {
1612                if (flavors[i].equals(flavor)) {
1613                    return true;
1614                }
1615            }
1616
1617            return false;
1618        }
1619
1620        public Object JavaDoc getTransferData(DataFlavor JavaDoc flavor)
1621        throws UnsupportedFlavorException JavaDoc, IOException JavaDoc {
1622            if (isPlainFlavor(flavor)) {
1623                String JavaDoc data = getPlainData();
1624                data = (data == null) ? "" : data;
1625
1626                if (String JavaDoc.class.equals(flavor.getRepresentationClass())) {
1627                    return data;
1628                } else if (Reader JavaDoc.class.equals(flavor.getRepresentationClass())) {
1629                    return new StringReader JavaDoc(data);
1630                } else if (InputStream JavaDoc.class.equals(flavor.getRepresentationClass())) {
1631                    // XXX should this enforce UTF-8 encoding?
1632
return new StringBufferInputStream JavaDoc(data);
1633                }
1634
1635                // fall through to unsupported
1636
} else if (isStringFlavor(flavor)) {
1637                String JavaDoc data = getPlainData();
1638                data = (data == null) ? "" : data;
1639
1640                return data;
1641            }
1642
1643            throw new UnsupportedFlavorException JavaDoc(flavor);
1644        }
1645
1646        // --- plain text flavors ----------------------------------------------
1647

1648        /**
1649         * Returns whether or not the specified data flavor is an plain flavor
1650         * that is supported.
1651         *
1652         * @param flavor the requested flavor for the data
1653         * @return boolean indicating whether or not the data flavor is supported
1654         */

1655        protected boolean isPlainFlavor(DataFlavor JavaDoc flavor) {
1656            DataFlavor JavaDoc[] flavors = plainFlavors;
1657
1658            for (int i = 0; i < flavors.length; i++) {
1659                if (flavors[i].equals(flavor)) {
1660                    return true;
1661                }
1662            }
1663
1664            return false;
1665        }
1666
1667        /**
1668         * Should the plain text flavors be offered? If so, the method
1669         * getPlainData should be implemented to provide something reasonable.
1670         */

1671        protected boolean isPlainSupported() {
1672            return plainData != null;
1673        }
1674
1675        /**
1676         * Fetch the data in a text/plain format.
1677         */

1678        protected String JavaDoc getPlainData() {
1679            return plainData;
1680        }
1681
1682        // --- string flavors --------------------------------------------------
1683

1684        /**
1685         * Returns whether or not the specified data flavor is a String flavor
1686         * that is supported.
1687         *
1688         * @param flavor the requested flavor for the data
1689         * @return boolean indicating whether or not the data flavor is supported
1690         */

1691        protected boolean isStringFlavor(DataFlavor JavaDoc flavor) {
1692            DataFlavor JavaDoc[] flavors = stringFlavors;
1693
1694            for (int i = 0; i < flavors.length; i++) {
1695                if (flavors[i].equals(flavor)) {
1696                    return true;
1697                }
1698            }
1699
1700            return false;
1701        }
1702    }
1703}
1704
Popular Tags