KickJava   Java API By Example, From Geeks To Geeks.

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


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  * BaseTable.java
21  *
22  * Created on 13 October 2003, 12:52
23  */

24 package org.openide.explorer.propertysheet;
25
26 import java.awt.AWTKeyStroke JavaDoc;
27 import org.openide.util.NbBundle;
28
29 import java.awt.AWTEvent JavaDoc;
30 import java.awt.Component JavaDoc;
31 import java.awt.Container JavaDoc;
32 import java.awt.Cursor JavaDoc;
33 import java.awt.Dialog JavaDoc;
34 import java.awt.Event JavaDoc;
35 import java.awt.EventQueue JavaDoc;
36 import java.awt.Font JavaDoc;
37 import java.awt.FontMetrics JavaDoc;
38 import java.awt.Graphics JavaDoc;
39 import java.awt.Insets JavaDoc;
40 import java.awt.KeyboardFocusManager JavaDoc;
41 import java.awt.Point JavaDoc;
42 import java.awt.Rectangle JavaDoc;
43 import java.awt.Toolkit JavaDoc;
44 import java.awt.Window JavaDoc;
45 import java.awt.event.ActionEvent JavaDoc;
46 import java.awt.event.ActionListener JavaDoc;
47 import java.awt.event.FocusEvent JavaDoc;
48 import java.awt.event.FocusListener JavaDoc;
49 import java.awt.event.KeyEvent JavaDoc;
50 import java.awt.event.MouseAdapter JavaDoc;
51 import java.awt.event.MouseEvent JavaDoc;
52 import java.awt.event.MouseMotionListener JavaDoc;
53 import java.awt.event.WindowAdapter JavaDoc;
54
55 import java.util.ArrayList JavaDoc;
56 import java.util.Collections JavaDoc;
57 import java.util.EventObject JavaDoc;
58 import java.util.List JavaDoc;
59
60 import javax.swing.AbstractAction JavaDoc;
61 import javax.swing.Action JavaDoc;
62 import javax.swing.ActionMap JavaDoc;
63 import javax.swing.BorderFactory JavaDoc;
64 import javax.swing.BoxLayout JavaDoc;
65 import javax.swing.InputMap JavaDoc;
66 import javax.swing.JButton JavaDoc;
67 import javax.swing.JComboBox JavaDoc;
68 import javax.swing.JComponent JavaDoc;
69 import javax.swing.JLabel JavaDoc;
70 import javax.swing.JPanel JavaDoc;
71 import javax.swing.JRootPane JavaDoc;
72 import javax.swing.JScrollPane JavaDoc;
73 import javax.swing.JTable JavaDoc;
74 import javax.swing.JTextField JavaDoc;
75 import javax.swing.JViewport JavaDoc;
76 import javax.swing.KeyStroke JavaDoc;
77 import javax.swing.ListSelectionModel JavaDoc;
78 import javax.swing.SwingUtilities JavaDoc;
79 import javax.swing.UIManager JavaDoc;
80 import javax.swing.event.ChangeEvent JavaDoc;
81 import javax.swing.event.ChangeListener JavaDoc;
82 import javax.swing.event.TableModelEvent JavaDoc;
83 import javax.swing.plaf.TableUI JavaDoc;
84 import javax.swing.table.TableCellEditor JavaDoc;
85 import javax.swing.table.TableCellRenderer JavaDoc;
86 import javax.swing.table.TableColumn JavaDoc;
87 import javax.swing.table.TableColumnModel JavaDoc;
88 import javax.swing.table.TableModel JavaDoc;
89 import javax.swing.text.JTextComponent JavaDoc;
90
91
92 /** A base class for property-sheet style tables. This class handles all of
93  * the non-property specific behaviors of the property sheet. It is not
94  * intended for subclassing except by SheetTable - it exists mainly to keep
95  * orthagonal code separate and maintainable, and was factored out of the
96  * original implementation of SheetTable. Basically it provides focus
97  * handling, row painting and some generic actions used by the property
98  * sheet, constituting those customizations to a standard JTable which
99  * the property sheet requires.
100  *
101  * @author Tim Boudreau
102  */

103 abstract class BaseTable extends JTable JavaDoc implements FocusListener JavaDoc {
104     /** Action key for the action that will move to the next row via TAB,
105      * or to the next focusable component if on the last row */

106     protected static final String JavaDoc ACTION_NEXT = "next"; //NOI18N
107

108     /** Action key for the action that will move to the previous row via TAB,
109      * or to the next focusable component if on the first row */

110     protected static final String JavaDoc ACTION_PREV = "prev"; //NOI18N
111

112     /** Action key for the action that will start editing the cell via the
113      * keyboard */

114     protected static final String JavaDoc ACTION_INLINE_EDITOR = "invokeInlineEditor"; //NOI18N
115

116     /** Action key for cancelling an edit by pressing escape */
117     protected static final String JavaDoc ACTION_CANCEL_EDIT = "cancelEditing"; //NOI18N
118

119     /** Action key for user pressing enter when not in edit mode */
120     protected static final String JavaDoc ACTION_ENTER = "enterPressed"; //NOI18N
121

122     /** Action key for up/down focus action */
123     protected static final String JavaDoc ACTION_FOCUS_NEXT = "focusNext"; //NOI18N
124

125     /** Number of pixels on each side of the column split in which the mouse
126      * cursor should be the resize cursor and mouse events should be
127      * interpreted as initiating a drag. */

128     private static final int centerLineFudgeFactor = 3;
129
130     /** Static start-an-edit action shared by all instances */
131     protected static Action JavaDoc editAction = null;
132
133     /** Static cancel-an-edit action shared by all instances */
134     protected static Action JavaDoc cancelAction = null;
135
136     /** Action which will try to invoke the default button if the table is in
137      * a dialog */

138     protected static Action JavaDoc enterAction = null;
139
140     /** Listener for drag events on the center line, for resizing columns when
141      * there are no headers */

142     protected LineDragListener dragListener;
143
144     /**A change event for reuse */
145     private transient final ChangeEvent JavaDoc chEvent = new ChangeEvent JavaDoc(this);
146
147     /** The list of change listeners */
148     private transient List JavaDoc<ChangeListener JavaDoc> changeListenerList;
149
150     /** Flag which, if true, means that the next call to paint() should trigger
151      * calculating the fixed row height based on the font size */

152     boolean needCalcRowHeight = true;
153
154     /** Flag used by addFocusListener to block the UI delegate from adding
155      * a focus listener (which will repaint the table incorrectly) */

156     private boolean inSetUI = false;
157
158     //Variables used by subclasses to track when edit requests start/end,
159
//to determine when it's appropriate to repaint. A sort of reference
160
//counting.
161
private int editRequests = 0;
162     private int editorRemoveRequests = 0;
163     private int editorChangeRequests = 0;
164     private boolean searchArmed = false;
165     private transient SearchField searchField = null;
166     private transient JPanel JavaDoc searchpanel = null;
167     private transient ChangeListener JavaDoc viewportListener;
168     private transient Point JavaDoc prevViewPosition = null;
169
170     /** Creates a new instance of BaseTable. */
171     public BaseTable(TableModel JavaDoc dm, TableColumnModel JavaDoc cm, ListSelectionModel JavaDoc sm) {
172         super(dm, cm, sm);
173
174         //set single selection mode
175
getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
176
177         setSurrendersFocusOnKeystroke(true);
178
179         setCellSelectionEnabled(false);
180         setRowSelectionAllowed(true);
181         setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
182
183         //See the sources for JTable for what these do
184
putClientProperty("JTable.autoStartsEdit", Boolean.FALSE); //NOI18N
185
putClientProperty("terminateEditOnFocusLost", PropUtils.psCommitOnFocusLoss ? Boolean.FALSE : Boolean.TRUE); //NOI18N
186

187         //create a listener for dragging the grid center line to resize columns
188
dragListener = new LineDragListener();
189         addMouseListener(dragListener);
190         addMouseMotionListener(dragListener);
191
192         //If we are not focus cycle root, when an editor is removed, focus
193
//will get set to a random component which is usually not the property
194
//sheet
195
setFocusCycleRoot(true);
196
197         enableEvents(AWTEvent.FOCUS_EVENT_MASK); //JDK 1.5
198

199         if (getClass() != SheetTable.class) {
200             throw new NoClassDefFoundError JavaDoc("Only SheetTable may subclass BaseTable, for good reasons"); //NOI18N
201
}
202     }
203
204     /** Initialize keystrokes and actions */
205     protected void initKeysAndActions() {
206         //Kill off the focus traversal keys. NavigationAction will find the
207
//next/previous components if the keyboard moves the position beyond
208
//the ends of the table, and manage the focus thus.
209
setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, Collections.<AWTKeyStroke JavaDoc>emptySet());
210         setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, Collections.<AWTKeyStroke JavaDoc>emptySet());
211
212         //Next two lines do not work using inputmap/actionmap, but do work
213
//using the older API. We will process ENTER to skip to next row,
214
//not next cell
215
unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0));
216         unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, Event.SHIFT_MASK));
217
218         InputMap JavaDoc imp = getInputMap();
219         ActionMap JavaDoc am = getActionMap();
220
221         //Issue 37919, reinstate support for up/down cycle focus transfer.
222
//being focus cycle root mangles this in some dialogs
223
imp.put(
224             KeyStroke.getKeyStroke(
225                 KeyEvent.VK_TAB, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() | KeyEvent.SHIFT_MASK, false
226             ), ACTION_FOCUS_NEXT
227         );
228         imp.put(
229             KeyStroke.getKeyStroke(KeyEvent.VK_TAB, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false),
230             ACTION_FOCUS_NEXT
231         );
232
233         Action JavaDoc ctrlTab = new CTRLTabAction();
234         am.put(ACTION_FOCUS_NEXT, ctrlTab);
235
236         imp.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), ACTION_NEXT);
237
238         imp.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK), ACTION_PREV);
239
240         am.put(ACTION_NEXT, new NavigationAction(true));
241         am.put(ACTION_PREV, new NavigationAction(false));
242
243         imp.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), ACTION_INLINE_EDITOR);
244         imp.put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0), ACTION_INLINE_EDITOR);
245         am.put(ACTION_INLINE_EDITOR, getEditAction());
246
247         imp.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), ACTION_ENTER);
248         am.put(ACTION_ENTER, getEnterAction());
249
250         InputMap JavaDoc impAncestor = getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
251
252         impAncestor.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), ACTION_CANCEL_EDIT);
253         am.put(ACTION_CANCEL_EDIT, new CancelAction());
254
255         impAncestor.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), ACTION_CANCEL_EDIT);
256     }
257
258     /** Called by the sheet table finalizer */
259     protected static final void cleanup() {
260         editAction = null;
261         cancelAction = null;
262         enterAction = null;
263     }
264
265     /** Overridden to set the flag for recalculating the fixed row height */
266     public void setFont(Font JavaDoc f) {
267         needCalcRowHeight = true;
268         super.setFont(f);
269     }
270
271     /** Lazily create the edit-on-spacebar action */
272     private static Action JavaDoc getEditAction() {
273         if (editAction == null) {
274             editAction = new EditAction();
275         }
276
277         return editAction;
278     }
279
280     /** Lazily create the cancel-on-escape action */
281
282     /*private static Action getCancelAction() {
283         if (cancelAction == null) {
284             cancelAction = new CancelAction();
285         }
286         return cancelAction;
287     }*/

288     private static Action JavaDoc getEnterAction() {
289         if (enterAction == null) {
290             enterAction = new EnterAction();
291         }
292
293         return enterAction;
294     }
295
296     /** Calculate the height of rows based on the current font. This is
297      * done when the first paint occurs, to ensure that a valid Graphics
298      * object is available. */

299     private void calcRowHeight(Graphics JavaDoc g) {
300         //Users of themes can set an explicit row height, so check for it
301
Integer JavaDoc i = (Integer JavaDoc) UIManager.get(PropUtils.KEY_ROWHEIGHT); //NOI18N
302

303         int rowHeight;
304
305         if (i != null) {
306             rowHeight = i.intValue();
307         } else {
308             //Derive a row height to accomodate the font and expando icon
309
Font JavaDoc f = getFont();
310             FontMetrics JavaDoc fm = g.getFontMetrics(f);
311             rowHeight = Math.max(fm.getHeight() + 3, PropUtils.getSpinnerHeight());
312         }
313
314         //Clear the flag
315
needCalcRowHeight = false;
316
317         //Set row height. If displayable, this will generate a new call
318
//to paint()
319
setRowHeight(rowHeight);
320     }
321
322     protected int getFirstVisibleRow() {
323         if (getParent() instanceof JViewport JavaDoc) {
324             JViewport JavaDoc jvp = (JViewport JavaDoc) getParent();
325
326             return rowAtPoint(jvp.getViewPosition());
327         } else {
328             Insets JavaDoc ins = getInsets();
329
330             return rowAtPoint(new Point JavaDoc(ins.left, ins.top));
331         }
332     }
333
334     protected int getVisibleRowCount() {
335         int rowCount = getRowCount();
336         int rowHeight = getRowHeight();
337
338         if ((rowCount == 0) || (rowHeight == 0)) {
339             return 0;
340         }
341
342         if (getParent() instanceof JViewport JavaDoc) {
343             JViewport JavaDoc jvp = (JViewport JavaDoc) getParent();
344
345             // +1 to return also half-displayed rows (issue 53660)
346
int result = Math.min(rowCount, (jvp.getExtentSize().height / rowHeight) + 1);
347
348             return result;
349         } else {
350             return Math.min(rowCount, getHeight() / rowHeight);
351         }
352     }
353
354     /** Overridden to not allow edits on the names column (0) */
355     public boolean isCellEditable(int row, int col) {
356         return col != 0;
357     }
358
359     /** The old window system will force focus back to the table, when an editor
360      * becomes visible. This will cause the combo box to close its popup because
361      * it has lost focus, unless we intervene here and make sure focus must be
362      * passed directly to the editor if present */

363     public final void requestFocus() {
364         if (isEditing()) {
365             if (PropUtils.isLoggable(BaseTable.class)) {
366                 PropUtils.log(BaseTable.class, "RequestFocus on table delegating to editor component"); //NOI18N
367
}
368
369             editorComp.requestFocus();
370         } else {
371             if (!inEditorChangeRequest()) {
372                 if (PropUtils.isLoggable(BaseTable.class)) {
373                     PropUtils.log(BaseTable.class, "RequestFocus on table with no editor present"); //NOI18N
374
}
375
376                 super.requestFocus();
377             }
378         }
379     }
380
381     /** The old window system will force focus back to the table, when an editor
382      * becomes visible. This will cause the combo box to close its popup because
383      * it has lost focus, unless we intervene here and make sure focus must be
384      * passed directly to the editor if present */

385     public final boolean requestFocusInWindow() {
386         if (isEditing()) {
387             if (PropUtils.isLoggable(BaseTable.class)) {
388                 PropUtils.log(BaseTable.class, "RequestFocusInWindow on table delegating to editor"); //NOI18N
389
}
390
391             return editorComp.requestFocusInWindow();
392         } else {
393             if (!inEditorChangeRequest()) {
394                 if (PropUtils.isLoggable(BaseTable.class)) {
395                     PropUtils.log(BaseTable.class, "RequestFocusInWindow on table with no editor present"); //NOI18N
396
}
397
398                 boolean result = super.requestFocusInWindow();
399
400                 if (PropUtils.isLoggable(BaseTable.class)) {
401                     PropUtils.log(BaseTable.class, " RequestFocusInWindow result " + result); //NOI18N
402
}
403
404                 return result;
405             } else {
406                 return false;
407             }
408         }
409     }
410
411     /** Overridden to remove the editor before editing, so a new edit
412      * can be started in a single click, to set the selection before editing,
413      * so that the editor will be painted with the selection color, and
414      * to request focus on the editor component */

415     public boolean editCellAt(int row, int col, EventObject JavaDoc e) {
416         enterEditRequest();
417
418         if (e instanceof MouseEvent JavaDoc) {
419             if (PropUtils.isLoggable(BaseTable.class)) {
420                 PropUtils.log(BaseTable.class, "editCellAt " + row + "," + col + " triggered by mouse event"); //NOI18N
421
}
422
423             //Ensure that the we end up being the focus owner. In the case
424
//of the radio button editor, focus can remain with the previous
425
//focus owner
426
Component JavaDoc focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner();
427
428             if (focusOwner != this) {
429                 if (!requestFocusInWindow()) {
430                     requestFocus();
431                 }
432             }
433         } else {
434             if (PropUtils.isLoggable(BaseTable.class)) {
435                 PropUtils.log(BaseTable.class, "editCellAt " + row + "," + col + " triggered by (null = kbd evt)" + e); //NOI18N
436
}
437         }
438
439         boolean wasEditing = isEditing();
440
441         //Cancel any current edit. By default, if you click a cell in
442
//a JTable while another cell is being edited, it will change
443
//the selection and stop the edit, but it will not initiate a
444
//new edit. So we need to be sure that we are not editing by
445
//the time super.editCellAt is called
446
if (wasEditing) {
447             if (PropUtils.isLoggable(BaseTable.class)) {
448                 PropUtils.log(BaseTable.class, " was already editing, removing the editor"); //NOI18N
449
}
450
451             removeEditor();
452         }
453
454         //Update the selection first - we want to change this now, so the
455
//row will be painted correctly, rather than when
456
//TableCellEditor.shouldSelectCell is called
457
int prevSel = getSelectedRow();
458         changeSelection(row, col, false, false);
459
460         boolean result = false;
461
462         //Set a flag - we'll want to behave slightly differently in terms
463
//of repaints if we're going from editing -> editing - there's no
464
//need for an update to reflect a non-editing state
465
final boolean editorChange = wasEditing && isCellEditable(row, col);
466
467         if (editorChange) {
468             enterEditorChangeRequest();
469         }
470
471         try {
472             //Do the super call to really start the edit
473
result = super.editCellAt(row, col, e);
474
475             if (PropUtils.isLoggable(BaseTable.class)) {
476                 PropUtils.log(BaseTable.class, " Result of super.editCellAt is " + result); //NOI18N
477
}
478
479             //For the sake of the radio button editor, these paints really
480
//need to be done synchronously - the editor will be added,
481
//painted, handed a mouse event, process it, fire an event and
482
//be removed before the next event on the event queue gets handled
483
// paintRow(prevSel);
484
// paintSelectionRow();
485
//JTable will not set focus to the editor by default, so we
486
//need to or it will never get focus when invoked by the
487
//keyboard
488
if (editorComp != null) {
489                 Component JavaDoc c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
490
491                 //Add ourselves as a focus listener to the component
492
editorComp.addFocusListener(this);
493             }
494         } finally {
495             //Reset the flags no matter what happened
496
try {
497                 //in its own try-catch in case of assertion failure
498
exitEditRequest();
499             } finally {
500                 if (editorChange) {
501                     exitEditorChangeRequest();
502                 }
503             }
504         }
505
506         return result;
507     }
508
509     /** Called when an edit request is received, to indicate that some
510      * repaints should be blocked while previous editors are removed,
511      * selection is changed, etc. */

512     protected final void enterEditRequest() {
513         editRequests++;
514
515         if (PropUtils.isLoggable(BaseTable.class)) {
516             PropUtils.log(BaseTable.class, " entering edit request"); //NOI18N
517
}
518     }
519
520     protected final void enterEditorRemoveRequest() {
521         editorRemoveRequests++;
522
523         if (PropUtils.isLoggable(BaseTable.class)) {
524             PropUtils.log(BaseTable.class, " entering editor remove request"); //NOI18N
525
}
526     }
527
528     protected final void enterEditorChangeRequest() {
529         editorChangeRequests++;
530
531         if (PropUtils.isLoggable(BaseTable.class)) {
532             PropUtils.log(BaseTable.class, " entering editor change request"); //NOI18N
533
}
534     }
535
536     protected final void exitEditRequest() {
537         editRequests--;
538
539         if (PropUtils.isLoggable(BaseTable.class)) {
540             PropUtils.log(BaseTable.class, " exiting edit change request"); //NOI18N
541
}
542
543         assert editRequests >= 0;
544     }
545
546     protected final void exitEditorRemoveRequest() {
547         editorRemoveRequests--;
548         PropUtils.log(BaseTable.class, " exiting editor remove request"); //NOI18N
549
assert editorRemoveRequests >= 0;
550     }
551
552     protected final void exitEditorChangeRequest() {
553         editorChangeRequests--;
554
555         if (PropUtils.isLoggable(BaseTable.class)) {
556             PropUtils.log(BaseTable.class, " exiting editor change request"); //NOI18N
557
}
558
559         assert editorRemoveRequests >= 0;
560     }
561
562     protected final boolean inEditRequest() {
563         return editRequests > 0;
564     }
565
566     protected final boolean inEditorChangeRequest() {
567         return editorChangeRequests > 0;
568     }
569
570     protected final boolean inEditorRemoveRequest() {
571         return editorRemoveRequests > 0;
572     }
573
574     /** Overridden to set the colors apropriately - we always want the editor
575      * to appear selected */

576     public Component JavaDoc prepareEditor(TableCellEditor JavaDoc editor, int row, int col) {
577         Component JavaDoc result = editor.getTableCellEditorComponent(this, getValueAt(row, col), false, row, col);
578
579         if (result != null) {
580             result.setBackground(getSelectionBackground());
581             result.setForeground(getSelectionForeground());
582             result.setFont(getFont());
583         }
584
585         return result;
586     }
587
588     /** Overridden to hide the selection when not focused, and paint across the
589      * selected row if focused. */

590     public Component JavaDoc prepareRenderer(TableCellRenderer JavaDoc renderer, int row, int col) {
591         Object JavaDoc value = getValueAt(row, col);
592
593         Component JavaDoc focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner();
594
595         boolean isSelected = isSelected(row, focusOwner);
596
597         Component JavaDoc result = renderer.getTableCellRendererComponent(this, value, isSelected, false, row, col);
598
599         return result;
600     }
601
602     /** Determines if the row should be painted as if it were selected. This
603      * is overridden by SheetTable to also check if the focused component is
604      * known to the current inplace editor, if any */

605     protected boolean isSelected(int row, Component JavaDoc focusOwner) {
606         return ((getSelectedRow() == row) || ((editingRow == row) && !inEditorRemoveRequest())) &&
607         (hasFocus() || isKnownComponent(focusOwner) || inEditRequest());
608     }
609
610     public void setUI(TableUI JavaDoc ui) {
611         needCalcRowHeight = true;
612         inSetUI = true;
613         super.setUI(ui);
614         inSetUI = false;
615     }
616
617     /** Overridden to not allow the UI to install a focus listener. Reason:
618      * This focus listener is installed to repaint on focus loss, but it will
619      * only repaint the selected *cell*. Since we don't differentiate selecting
620      * only a cell, we need to repaint the entire row that is selected, which
621      * we will do from processFocusEvent() */

622     public void addFocusListener(FocusListener JavaDoc fl) {
623         if (!inSetUI) {
624             super.addFocusListener(fl);
625         }
626     }
627
628     public void updateUI() {
629         super.updateUI();
630
631         //Initialize keys and actions after updateUI; the UI will overwrite
632
//arrow key actions if this is done in the constructor
633
initKeysAndActions();
634     }
635
636     /** Paint the table. After the super.paint() call, calls paintMargin() to fill
637      * in the left edge with the appropriate color, and then calls paintExpandableSets()
638      * to paint the property sets, which are not painted by the default painting
639      * methods because they need to be painted across two rows. */

640     public void paint(Graphics JavaDoc g) {
641         if (needCalcRowHeight) {
642             calcRowHeight(g);
643
644             return;
645         }
646
647         super.paint(g);
648     }
649
650     protected void paintRow(int row) {
651         if (row == -1) {
652             return;
653         }
654
655         Rectangle JavaDoc dirtyRect = getCellRect(row, 0, false);
656         dirtyRect.x = 0;
657         dirtyRect.width = getWidth();
658         repaint(dirtyRect);
659     }
660
661     /** Our own painting code for the selection row - normally the UI delegate
662      * would do this, but we specifically block it from adding a focus listener
663      * and do it ourselves, since when focus changes, we need to repaint both
664      * rows, not just the selected cell. */

665     protected void paintSelectionRow() {
666         paintRow(getSelectedRow());
667     }
668
669     /** Overridden to add the entire row that was being edited to RepaintManager
670      * as a dirty region */

671     public void removeEditor() {
672         enterEditorRemoveRequest();
673
674         try {
675             int i = editingRow;
676
677             if (editorComp != null) {
678                 editorComp.removeFocusListener(this);
679             }
680
681             if (PropUtils.isLoggable(BaseTable.class)) {
682                 PropUtils.log(BaseTable.class, " removing editor"); //NOI18N
683
}
684
685             super.removeEditor();
686
687             if (i != -1) {
688                 //Do schedule a repaint for the row just in case
689
paintRow(i);
690             }
691         } finally {
692             exitEditorRemoveRequest();
693         }
694     }
695
696     /** Overridden - JTable's implementation of the method will
697      * actually attach (and leave behind) a gratuitous border
698      * on the enclosing scroll pane. */

699     protected final void configureEnclosingScrollPane() {
700         Container JavaDoc p = getParent();
701
702         if (p instanceof JViewport JavaDoc) {
703             Container JavaDoc gp = p.getParent();
704
705             if (gp instanceof JScrollPane JavaDoc) {
706                 JScrollPane JavaDoc scrollPane = (JScrollPane JavaDoc) gp;
707                 JViewport JavaDoc viewport = scrollPane.getViewport();
708
709                 if ((viewport == null) || (viewport.getView() != this)) {
710                     return;
711                 }
712
713                 scrollPane.setColumnHeaderView(getTableHeader());
714             }
715         }
716     }
717
718     /** Returns true if the passed X axis pixel position is within the
719      * bounds where a drag can be initiated to resize columns */

720     protected final boolean onCenterLine(int pos) {
721         int line = getColumnModel().getColumn(0).getWidth();
722
723         return (pos > (line - centerLineFudgeFactor)) && (pos < (line + centerLineFudgeFactor));
724     }
725
726     /** Returns true if the passed event occured within the
727      * bounds where a drag can be initiated to resize columns */

728     protected final boolean onCenterLine(MouseEvent JavaDoc me) {
729         int pos = me.getPoint().x;
730
731         return (onCenterLine(pos));
732     }
733
734     /** Overridden to not change the selection if the user is currently
735      * dragging the center line */

736     public void changeSelection(int row, int column, boolean toggle, boolean extend) {
737         //DragListener can be null, because changeSelection is called in
738
//superclass constructor
739
if ((dragListener != null) && dragListener.isArmed()) {
740             return;
741         }
742
743         if (PropUtils.isLoggable(BaseTable.class)) {
744             PropUtils.log(BaseTable.class, "ChangeSelection to " + row + "," + column); //NOI18N
745
}
746
747         super.changeSelection(row, column, toggle, extend);
748         fireChange();
749     }
750
751     /** This method exists to support experimental support for commit-on-focus-loss
752      * if NetBeans is started with a specific line switch - SheetTable overrides
753      * this method to stop cell editing if the flag is true. */

754     protected void focusLostCancel() {
755         removeEditor();
756     }
757
758     /** Overridden to remove the editor on focus lost */
759     public void processFocusEvent(FocusEvent JavaDoc fe) {
760         super.processFocusEvent(fe);
761
762         if (PropUtils.isLoggable(BaseTable.class)) {
763             PropUtils.log(BaseTable.class, "processFocusEvent - "); //NOI18N
764
PropUtils.log(BaseTable.class, fe);
765         }
766
767         if (!isAncestorOf(fe.getOppositeComponent()) || (fe.getOppositeComponent() == null)) {
768             if (isEditing() && (fe.getID() == fe.FOCUS_LOST)) {
769                 if (PropUtils.isLoggable(BaseTable.class)) {
770                     PropUtils.log(
771                         BaseTable.class, "ProcessFocusEvent got focus lost to unknown component, removing editor"
772                     ); //NOI18N
773
}
774
775                 focusLostCancel();
776             }
777         }
778
779         if (!inEditorRemoveRequest() && !inEditRequest()) { //XXX inEditRequest probably shouldn't be here
780

781             if ((fe.getOppositeComponent() == null) && (fe.getID() == fe.FOCUS_LOST)) {
782                 //ignore the strange focus to null stuff NetBeans does
783
return;
784             }
785
786             paintSelectionRow();
787         } else {
788             paintSelectionRow();
789         }
790     }
791
792     /** Overridden to allow standard keybinding processing of VK_TAB and
793      * abort any pending drag operation on the vertical grid. */

794     public void processKeyEvent(KeyEvent JavaDoc e) {
795         if (dragListener.isArmed()) {
796             dragListener.setArmed(false);
797         }
798
799         boolean suppressDefaultHandling = ((searchField != null) && searchField.isShowing()) &&
800             ((e.getKeyCode() == KeyEvent.VK_UP) || (e.getKeyCode() == KeyEvent.VK_DOWN));
801
802         //Manually hook in the bindings for tab - does not seem to get called
803
//automatically
804
if (e.getKeyCode() != e.VK_TAB) {
805             if (!suppressDefaultHandling) {
806                 //Either the search field or the table should handle up/down, not both
807
super.processKeyEvent(e);
808             }
809
810             if (!e.isConsumed()) {
811                 if ((e.getID() == KeyEvent.KEY_PRESSED) && !isEditing()) {
812                     int modifiers = e.getModifiers();
813                     int keyCode = e.getKeyCode();
814
815                     if (((modifiers > 0) && (modifiers != KeyEvent.SHIFT_MASK)) || e.isActionKey()) {
816                         return;
817                     }
818
819                     char c = e.getKeyChar();
820
821                     if (!Character.isISOControl(c) && (keyCode != KeyEvent.VK_SHIFT) &&
822                             (keyCode != KeyEvent.VK_ESCAPE)) {
823                         searchArmed = true;
824                         e.consume();
825                     }
826                 } else if (searchArmed && (e.getID() == KeyEvent.KEY_TYPED)) {
827                     passToSearchField(e);
828                     e.consume();
829                     searchArmed = false;
830                 } else {
831                     searchArmed = false;
832                 }
833             }
834         } else {
835             processKeyBinding(
836                 KeyStroke.getKeyStroke(e.VK_TAB, e.getModifiersEx(), e.getID() == e.KEY_RELEASED), e,
837                 JComponent.WHEN_FOCUSED, e.getID() == e.KEY_PRESSED
838             );
839         }
840     }
841
842     void passToSearchField(KeyEvent JavaDoc e) {
843         //Don't do anything for normal navigation keys
844
if (
845             (e.getKeyCode() == KeyEvent.VK_TAB) || (e.getKeyCode() == KeyEvent.VK_ENTER) ||
846                 (((e.getKeyCode() == KeyEvent.VK_UP) || (e.getKeyCode() == KeyEvent.VK_DOWN)) &&
847                 ((searchField == null) || !searchField.isShowing()))
848         ) {
849             return;
850         }
851
852         if (getRowCount() == 0) {
853             return;
854         }
855
856         if ((searchField == null) || !searchField.isShowing()) {
857             showSearchField();
858             searchField.setText(String.valueOf(e.getKeyChar()));
859         }
860     }
861
862     private void showSearchField() {
863         if (searchField == null) {
864             searchField = new SearchField();
865             searchpanel = new JPanel JavaDoc();
866
867             JLabel JavaDoc lbl = new JLabel JavaDoc(NbBundle.getMessage(BaseTable.class, "LBL_QUICKSEARCH")); //NOI18N
868
searchpanel.setLayout(new BoxLayout JavaDoc(searchpanel, BoxLayout.X_AXIS));
869             searchpanel.add(lbl);
870             searchpanel.add(searchField);
871             lbl.setLabelFor(searchField);
872             searchpanel.setBorder(BorderFactory.createRaisedBevelBorder());
873             lbl.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
874         }
875
876         JComponent JavaDoc dest = getRootPane().getLayeredPane();
877
878         Point JavaDoc loc;
879
880         if (getParent() instanceof JViewport JavaDoc) {
881             JViewport JavaDoc jvp = (JViewport JavaDoc) getParent();
882             loc = jvp.getViewPosition();
883             loc.x += getColumnModel().getColumn(0).getWidth();
884             //#68516 repaint the table when scrolling
885
viewportListener = new ChangeListener JavaDoc() {
886                 public void stateChanged(ChangeEvent JavaDoc e) {
887                     if( null != searchField && searchField.isVisible() ) {
888                         if( null != prevViewPosition )
889                             repaint( 0, prevViewPosition.y, getWidth(), searchpanel.getHeight() );
890                         assert getParent() instanceof JViewport JavaDoc;
891                         prevViewPosition = new Point JavaDoc( ((JViewport JavaDoc)getParent()).getViewPosition() );
892                     }
893                 }
894             };
895             jvp.addChangeListener( viewportListener );
896             prevViewPosition = new Point JavaDoc( loc );
897         } else {
898             loc = new Point JavaDoc(getColumnModel().getColumn(0).getWidth(), getRowHeight() / 2);
899         }
900
901         loc = SwingUtilities.convertPoint(this, loc, dest);
902
903         int width = getColumnModel().getColumn(1).getWidth();
904         int height = getRowHeight() + 5;
905
906         if (width < 120) {
907             //too narrow
908
width = 160;
909             loc.x -= 160;
910         }
911
912         searchpanel.setBounds(loc.x, loc.y, width, height);
913         dest.add(searchpanel);
914         searchpanel.setVisible(true);
915         searchField.requestFocus();
916     }
917
918     private void hideSearchField() {
919         if (searchField == null) {
920             return;
921         }
922
923         searchpanel.setVisible(false);
924
925         if (getParent() instanceof JViewport JavaDoc && null != viewportListener ) {
926             JViewport JavaDoc jvp = (JViewport JavaDoc) getParent();
927             jvp.removeChangeListener( viewportListener );
928             viewportListener = null;
929         }
930         
931         if (searchpanel.getParent() != null) {
932             searchpanel.getParent().remove(searchpanel);
933         }
934
935         paintSelectionRow();
936     }
937
938     /** Called to determine if the search field text matches an object
939      * from column 0 (the passed object value). Subclasses should override
940      * to check specific info - the default implementation simply compares
941      * value.toString().startsWith(text) */

942     protected boolean matchText(Object JavaDoc value, String JavaDoc text) {
943         if (value != null) {
944             return value.toString().startsWith(text);
945         } else {
946             return false;
947         }
948     }
949
950     public boolean isOptimizedDrawingEnabled() {
951         if ((searchField != null) && searchField.isShowing()) {
952             return false;
953         } else {
954             return super.isOptimizedDrawingEnabled();
955         }
956     }
957
958     public void paintComponent(Graphics JavaDoc g) {
959         super.paintComponent(g);
960
961         //Issue 41546 - bad repaint when scrolling
962
if ((searchField != null) && searchField.isVisible()) {
963             searchpanel.repaint();
964         }
965     }
966
967     /** Overridden to fire a change event on a change in the table, so the
968      * property sheet can refresh the displayed description if necessary */

969     public void tableChanged(TableModelEvent JavaDoc e) {
970         super.tableChanged(e);
971         fireChange();
972     }
973
974     //****************Change listener support ***************
975

976     /** Registers ChangeListener to receive events.
977      * @param listener The listener to register. */

978     public final synchronized void addChangeListener(ChangeListener JavaDoc listener) {
979         if (changeListenerList == null) {
980             changeListenerList = new ArrayList JavaDoc<ChangeListener JavaDoc>();
981         }
982
983         changeListenerList.add(listener);
984     }
985
986     /** Removes ChangeListener from the list of listeners.
987      * @param listener The listener to remove. */

988     public final synchronized void removeChangeListener(ChangeListener JavaDoc listener) {
989         if (changeListenerList != null) {
990             changeListenerList.remove(listener);
991         }
992     }
993
994     /** Notifies all registered listeners about the event.
995      */

996     void fireChange() {
997         List JavaDoc list;
998
999         synchronized (this) {
1000            if (changeListenerList == null) {
1001                return;
1002            }
1003
1004            list = (List JavaDoc) ((ArrayList JavaDoc) changeListenerList).clone();
1005        }
1006
1007        for (int i = 0; i < list.size(); i++) {
1008            ((ChangeListener JavaDoc) list.get(i)).stateChanged(chEvent);
1009        }
1010    }
1011
1012    //****************************** Focus listener implementation ************
1013
protected boolean isKnownComponent(Component JavaDoc c) {
1014        if (c == null) {
1015            return false;
1016        }
1017
1018        if (c == this) {
1019            return true;
1020        }
1021
1022        if (c == editorComp) {
1023            return true;
1024        }
1025
1026        if (c == searchField) {
1027            return true;
1028        }
1029
1030        if (c == this.getRootPane()) {
1031            return true;
1032        }
1033
1034        if (c instanceof Container JavaDoc && ((Container JavaDoc) c).isAncestorOf(this)) {
1035            return true;
1036        }
1037
1038        if ((editorComp instanceof Container JavaDoc) && ((Container JavaDoc) editorComp).isAncestorOf(c)) {
1039            return true;
1040        }
1041
1042        return false;
1043    }
1044
1045    public void focusGained(FocusEvent JavaDoc fe) {
1046        Component JavaDoc c = fe.getOppositeComponent();
1047
1048        /*
1049        //handy for debugging
1050        System.out.println("Focus gained to " + (fe.getComponent().getName() == null ? fe.getComponent().getClass().getName() : fe.getComponent().getName()) + " temporary: " + fe.isTemporary()
1051        + " from " + (fe.getOppositeComponent() == null ? "null" :
1052            (fe.getOppositeComponent().getName() == null ? fe.getOppositeComponent().getClass().getName() : fe.getOppositeComponent().getName()))
1053        );
1054         */

1055        PropUtils.log(BaseTable.class, fe);
1056
1057        if (!isKnownComponent(c)) {
1058            fireChange();
1059        }
1060
1061        if (!inEditRequest() && !inEditorRemoveRequest() && (fe.getComponent() == this)) {
1062            // System.out.println("Painting due to focus gain " + fe.getComponent());
1063
// repaint(0,0,getWidth(),getHeight());
1064
paintSelectionRow();
1065        }
1066    }
1067
1068    //Focus listener implementation
1069
public void focusLost(FocusEvent JavaDoc fe) {
1070        if ((dragListener != null) && dragListener.isDragging()) {
1071            dragListener.abortDrag();
1072        }
1073
1074        PropUtils.log(BaseTable.class, fe);
1075
1076        //Ignore temporary focus changes, so sloppy focus middle mouse button
1077
//cut/paste can work
1078
if (fe.isTemporary()) {
1079            return;
1080        }
1081
1082        Component JavaDoc opposite = fe.getOppositeComponent();
1083
1084        if (!isKnownComponent(opposite)) {
1085            doFocusLost(opposite);
1086        }
1087    }
1088
1089    private void doFocusLost(Component JavaDoc opposite) {
1090        // TerminateEditOnFocusLost does not always work, ensure it
1091
PropUtils.log(BaseTable.class, " removing editor due to focus change"); //NOI18N
1092

1093        if (PropUtils.psCommitOnFocusLoss && isEditing()) { // && (source instanceof InplaceEditor)) {
1094
getCellEditor().stopCellEditing();
1095        } else {
1096            removeEditor();
1097        }
1098
1099        // fire a change if focus did not go to null, so the property sheet will
1100
// display the node name, not the selected property
1101
if (opposite != null) {
1102            fireChange();
1103        }
1104
1105        paintSelectionRow();
1106    }
1107
1108    WL parentListener;
1109    public void addNotify() {
1110        super.addNotify();
1111
1112        // #57560: properties should always save changes
1113
Container JavaDoc top = getTopLevelAncestor();
1114
1115        if (top instanceof Window JavaDoc) {
1116            ((Window JavaDoc) top).addWindowListener(parentListener = new WL());
1117        }
1118    }
1119    
1120    private class WL extends WindowAdapter JavaDoc {
1121        public void windowDeactivated(java.awt.event.WindowEvent JavaDoc we) {
1122            doFocusLost(we.getOppositeWindow());
1123        }
1124    }
1125    
1126    public void removeNotify() {
1127        // #57560: properties should always save changes
1128
Container JavaDoc top = getTopLevelAncestor();
1129
1130        if (top instanceof Window JavaDoc && parentListener != null) {
1131            ((Window JavaDoc) top).removeWindowListener(parentListener);
1132            parentListener = null;
1133        }
1134        super.removeNotify();
1135    }
1136
1137    private class SearchField extends JTextField JavaDoc implements ActionListener JavaDoc, FocusListener JavaDoc {
1138        private int selectionBeforeLastShow = -1;
1139
1140        public SearchField() {
1141            addActionListener(this);
1142            addFocusListener(this);
1143            setFont(BaseTable.this.getFont());
1144        }
1145
1146        public void addNotify() {
1147            super.addNotify();
1148            selectionBeforeLastShow = BaseTable.this.getSelectedRow();
1149        }
1150
1151        public void processKeyEvent(KeyEvent JavaDoc ke) {
1152            if (!isShowing()) {
1153                super.processKeyEvent(ke);
1154
1155                return;
1156            }
1157
1158            //override the default handling so that
1159
//the parent will never receive the escape key and
1160
//close a modal dialog
1161
if (ke.getKeyCode() == ke.VK_ESCAPE) {
1162                //The focus request will hide the field without focus getting
1163
//lost to somewhere else in the main window first.
1164
BaseTable.this.changeSelection(selectionBeforeLastShow, 0, false, false);
1165                BaseTable.this.requestFocus();
1166                ke.consume();
1167            } else if ((ke.getKeyCode() == ke.VK_UP) && (ke.getID() == ke.KEY_PRESSED)) {
1168                reverseSearch(getText());
1169            } else if ((ke.getKeyCode() == ke.VK_DOWN) && (ke.getID() == ke.KEY_PRESSED)) {
1170                forwardSearch(getText());
1171            } else {
1172                super.processKeyEvent(ke);
1173
1174                if ((ke.getKeyCode() != ke.VK_UP) && (ke.getKeyCode() != ke.VK_DOWN)) {
1175                    processSearchText(getText());
1176                }
1177            }
1178        }
1179
1180        public void keyPressed(KeyEvent JavaDoc ke) {
1181            if (ke.getKeyCode() == ke.VK_ESCAPE) {
1182                hideSearchField();
1183                ke.consume();
1184            }
1185        }
1186
1187        public void keyReleased(KeyEvent JavaDoc ke) {
1188            processSearchText(((JTextField JavaDoc) ke.getSource()).getText());
1189        }
1190
1191        public void actionPerformed(ActionEvent JavaDoc e) {
1192            processSearchText(((JTextField JavaDoc) e.getSource()).getText());
1193
1194            //Use the focus request to hide the field, otherwise focus will
1195
//be sent to Explorer or some random component
1196
BaseTable.this.requestFocus();
1197        }
1198
1199        public void focusGained(FocusEvent JavaDoc e) {
1200            //it will be the first focus gained event, so go select
1201
//whatever matches the first character
1202
processSearchText(((JTextField JavaDoc) e.getSource()).getText());
1203
1204            JRootPane JavaDoc root = getRootPane();
1205
1206            if (root != null) { // #57417 NPE
1207
root.getLayeredPane().repaint();
1208            }
1209        }
1210
1211        public void focusLost(FocusEvent JavaDoc e) {
1212            hideSearchField();
1213        }
1214
1215        private void processSearchText(String JavaDoc txt) {
1216            if ((txt == null) || (txt.length() == 0)) {
1217                return;
1218            }
1219
1220            int max = getRowCount();
1221            int pos = getSelectedRow();
1222
1223            if ((pos == (max - 1)) || (pos < 0)) {
1224                pos = 0;
1225            }
1226
1227            for (int i = 0; i < max; i++) {
1228                boolean match = matchText(BaseTable.this.getValueAt(i, 0), txt);
1229
1230                if (match) {
1231                    changeSelection(i, 0, false, false);
1232
1233                    //Set renderers can overpaint whole field's panel, so repaint
1234
getRootPane().getLayeredPane().repaint();
1235
1236                    break;
1237                }
1238
1239                if (pos++ == (max - 1)) {
1240                    pos = 0;
1241                }
1242            }
1243        }
1244
1245        private void forwardSearch(String JavaDoc txt) {
1246            if ((txt == null) || (txt.length() == 0)) {
1247                return;
1248            }
1249
1250            int max = getRowCount();
1251            int pos = getSelectedRow() + 1;
1252
1253            if ((pos == (max - 1)) || (pos < 0)) {
1254                pos = 0;
1255            }
1256
1257            for (int i = pos; i < max; i++) {
1258                boolean match = matchText(BaseTable.this.getValueAt(i, 0), txt);
1259
1260                if (match) {
1261                    changeSelection(i, 0, false, false);
1262
1263                    //Set renderers can overpaint it, so repaint
1264
repaint();
1265
1266                    break;
1267                }
1268            }
1269        }
1270
1271        private void reverseSearch(String JavaDoc txt) {
1272            if ((txt == null) || (txt.length() == 0)) {
1273                return;
1274            }
1275
1276            int max = getRowCount();
1277            int pos = getSelectedRow();
1278
1279            if (pos < 1) {
1280                pos = max - 1;
1281            }
1282
1283            for (int i = pos - 1; i >= 0; i--) {
1284                boolean match = matchText(BaseTable.this.getValueAt(i, 0), txt);
1285
1286                if (match) {
1287                    changeSelection(i, 0, false, false);
1288
1289                    //Set renderers can overpaint it, so repaint
1290
repaint();
1291
1292                    break;
1293                }
1294            }
1295        }
1296    }
1297
1298    /** Action to edit via the keyboard */
1299    private static class EditAction extends AbstractAction JavaDoc {
1300        public void actionPerformed(ActionEvent JavaDoc ae) {
1301            JTable JavaDoc jt = (JTable JavaDoc) ae.getSource();
1302            int row = jt.getSelectedRow();
1303            int col = jt.getSelectedColumn();
1304
1305            if ((row != -1) && (col != -1)) {
1306                if (PropUtils.isLoggable(BaseTable.class)) {
1307                    PropUtils.log(BaseTable.class, "Starting edit due to key event for row " + row); //NOI18N
1308
}
1309
1310                jt.editCellAt(row, 1, null);
1311
1312                //Focus will be rerouted to the editor via this call:
1313
jt.requestFocus();
1314            }
1315        }
1316    }
1317
1318    /** Action to cancel an inline editor */
1319    private class CancelAction extends AbstractAction JavaDoc {
1320        public void actionPerformed(ActionEvent JavaDoc ae) {
1321            JTable JavaDoc jt = (JTable JavaDoc) ae.getSource();
1322
1323            if (jt != null) {
1324                if (jt.isEditing()) {
1325                    TableCellEditor JavaDoc tce = jt.getCellEditor();
1326
1327                    if (PropUtils.isLoggable(BaseTable.class)) {
1328                        PropUtils.log(BaseTable.class, "Cancelling edit due to keyboard event"); //NOI18N
1329
}
1330
1331                    if (tce != null) {
1332                        jt.getCellEditor().cancelCellEditing();
1333                    }
1334                } else {
1335                    //If we're in a dialog, try to close it
1336
trySendEscToDialog(jt);
1337                }
1338            }
1339        }
1340
1341        public boolean isEnabled() {
1342            return isEditing();
1343        }
1344
1345        private void trySendEscToDialog(JTable JavaDoc jt) {
1346            // System.err.println("SendEscToDialog");
1347
EventObject JavaDoc ev = EventQueue.getCurrentEvent();
1348
1349            if (ev instanceof KeyEvent JavaDoc && (((KeyEvent JavaDoc) ev).getKeyCode() == KeyEvent.VK_ESCAPE)) {
1350                if (ev.getSource() instanceof JComboBox JavaDoc && ((JComboBox JavaDoc) ev.getSource()).isPopupVisible()) {
1351                    return;
1352                }
1353
1354                if (
1355                    ev.getSource() instanceof JTextComponent JavaDoc &&
1356                        ((JTextComponent JavaDoc) ev.getSource()).getParent() instanceof JComboBox JavaDoc &&
1357                        ((JComboBox JavaDoc) ((JTextComponent JavaDoc) ev.getSource()).getParent()).isPopupVisible()
1358                ) {
1359                    return;
1360                }
1361
1362                InputMap JavaDoc imp = jt.getRootPane().getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
1363                ActionMap JavaDoc am = jt.getRootPane().getActionMap();
1364
1365                KeyStroke JavaDoc escape = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
1366                Object JavaDoc key = imp.get(escape);
1367
1368                if (key != null) {
1369                    Action JavaDoc a = am.get(key);
1370
1371                    if (a != null) {
1372                        if (Boolean.getBoolean("netbeans.proppanel.logDialogActions")) { //NOI18N
1373
System.err.println("Action bound to escape key is " + a); //NOI18N
1374
}
1375
1376                        //Actions registered with deprecated registerKeyboardAction will
1377
//need this lookup of the action command
1378
String JavaDoc commandKey = (String JavaDoc) a.getValue(Action.ACTION_COMMAND_KEY);
1379
1380                        if (commandKey == null) {
1381                            commandKey = "cancel"; //NOI18N
1382
}
1383
1384                        a.actionPerformed(new ActionEvent JavaDoc(this, ActionEvent.ACTION_PERFORMED, commandKey)); //NOI18N
1385
}
1386                }
1387            }
1388        }
1389    }
1390
1391    private static class EnterAction extends AbstractAction JavaDoc {
1392        public void actionPerformed(ActionEvent JavaDoc ae) {
1393            if (ae.getSource() instanceof BaseTable) {
1394                BaseTable bt = (BaseTable) ae.getSource();
1395
1396                if (bt.isEditing()) {
1397                    return;
1398                }
1399
1400                trySendEnterToDialog(bt);
1401            }
1402        }
1403
1404        private void trySendEnterToDialog(BaseTable bt) {
1405            // System.err.println("SendEnterToDialog");
1406
EventObject JavaDoc ev = EventQueue.getCurrentEvent();
1407
1408            if (ev instanceof KeyEvent JavaDoc && (((KeyEvent JavaDoc) ev).getKeyCode() == KeyEvent.VK_ENTER)) {
1409                if (ev.getSource() instanceof JComboBox JavaDoc && ((JComboBox JavaDoc) ev.getSource()).isPopupVisible()) {
1410                    return;
1411                }
1412
1413                if (
1414                    ev.getSource() instanceof JTextComponent JavaDoc &&
1415                        ((JTextComponent JavaDoc) ev.getSource()).getParent() instanceof JComboBox JavaDoc &&
1416                        ((JComboBox JavaDoc) ((JTextComponent JavaDoc) ev.getSource()).getParent()).isPopupVisible()
1417                ) {
1418                    return;
1419                }
1420
1421                JRootPane JavaDoc jrp = bt.getRootPane();
1422
1423                if (jrp != null) {
1424                    JButton JavaDoc b = jrp.getDefaultButton();
1425
1426                    if ((b != null) && b.isEnabled()) {
1427                        b.doClick();
1428                    }
1429                }
1430            }
1431        }
1432    }
1433
1434    /** Enables tab keys to navigate between rows */
1435    private final class NavigationAction extends AbstractAction JavaDoc {
1436        private boolean direction;
1437
1438        public NavigationAction(boolean direction) {
1439            this.direction = direction;
1440        }
1441
1442        public void actionPerformed(ActionEvent JavaDoc e) {
1443            int next = getSelectedRow() + (direction ? 1 : (-1));
1444
1445            //if we're off the end, try to find a sibling component to pass
1446
//focus to
1447
if ((next >= getRowCount()) || (next < 0)) {
1448                if (!(BaseTable.this.getTopLevelAncestor() instanceof Dialog JavaDoc)) {
1449                    //If we're not in a dialog, we're in the main window - don't
1450
//send focus somewhere because the winsys won't change the
1451
//active mode
1452
next = (next >= getRowCount()) ? 0 : (getRowCount() - 1);
1453                } else if ((next >= getRowCount()) || (next < 0)) {
1454                    //if we're off the end, try to find a sibling component to pass
1455
//focus to
1456
//This code is a bit ugly, but works
1457
Container JavaDoc ancestor = getFocusCycleRootAncestor();
1458
1459                    //Find the next component in our parent's focus cycle
1460
Component JavaDoc sibling = direction
1461                        ? ancestor.getFocusTraversalPolicy().getComponentAfter(ancestor, BaseTable.this.getParent())
1462                        : ancestor.getFocusTraversalPolicy().getComponentBefore(ancestor, BaseTable.this);
1463
1464                    //Often LayoutFocusTranferPolicy will return ourselves if we're
1465
//the last. First try to find a parent focus cycle root that
1466
//will be a little more polite
1467
if (sibling == BaseTable.this) {
1468                        Container JavaDoc grandcestor = ancestor.getFocusCycleRootAncestor();
1469
1470                        if (grandcestor != null) {
1471                            sibling = direction
1472                                ? grandcestor.getFocusTraversalPolicy().getComponentAfter(grandcestor, ancestor)
1473                                : grandcestor.getFocusTraversalPolicy().getComponentBefore(grandcestor, ancestor);
1474                            ancestor = grandcestor;
1475                        }
1476                    }
1477
1478                    //Okay, we still ended up with ourselves, or there is only one focus
1479
//cycle root ancestor. Try to find the first component according to
1480
//the policy
1481
if (sibling == BaseTable.this) {
1482                        if (ancestor.getFocusTraversalPolicy().getFirstComponent(ancestor) != null) {
1483                            sibling = ancestor.getFocusTraversalPolicy().getFirstComponent(ancestor);
1484                        }
1485                    }
1486
1487                    //If we're *still* getting ourselves, find the default button and punt
1488
if (sibling == BaseTable.this) {
1489                        JRootPane JavaDoc rp = getRootPane();
1490                        JButton JavaDoc jb = rp.getDefaultButton();
1491
1492                        if (jb != null) {
1493                            sibling = jb;
1494                        }
1495                    }
1496
1497                    //See if it's us, or something we know about, and if so, just
1498
//loop around to the top or bottom row - there's noplace
1499
//interesting for focus to go to
1500
if (sibling != null) {
1501                        if (sibling == BaseTable.this) {
1502                            //set the selection if there's nothing else to do
1503
changeSelection(
1504                                direction ? 0 : (getRowCount() - 1), direction ? 0 : (getColumnCount() - 1), false,
1505                                false
1506                            );
1507                        } else {
1508                            //Request focus on the sibling
1509
sibling.requestFocus();
1510                        }
1511
1512                        return;
1513                    }
1514                }
1515
1516                changeSelection(next, getSelectedColumn(), false, false);
1517            }
1518
1519            if( getSelectionModel().getAnchorSelectionIndex() < 0 )
1520                getSelectionModel().setAnchorSelectionIndex(next);
1521            getSelectionModel().setLeadSelectionIndex(next);
1522        }
1523    }
1524
1525    /** Listener for drag events that should resize columns */
1526    final class LineDragListener extends MouseAdapter JavaDoc implements MouseMotionListener JavaDoc {
1527        private long dragStartTime = -1;
1528        boolean armed;
1529        boolean dragging;
1530        int pos = -1;
1531
1532        public void mouseExited(MouseEvent JavaDoc e) {
1533            setArmed(false);
1534        }
1535
1536        public void mousePressed(MouseEvent JavaDoc e) {
1537            if (isArmed() && onCenterLine(e)) {
1538                beginDrag();
1539            }
1540        }
1541
1542        public void mouseReleased(MouseEvent JavaDoc e) {
1543            if (isDragging()) {
1544                finishDrag();
1545                setArmed(false);
1546            }
1547        }
1548
1549        public void mouseMoved(MouseEvent JavaDoc e) {
1550            setArmed(!isEditing() && onCenterLine(e));
1551        }
1552
1553        public void mouseDragged(MouseEvent JavaDoc e) {
1554            if (!armed && !dragging) {
1555                return;
1556            }
1557
1558            int newPos = e.getPoint().x;
1559            TableColumn JavaDoc c0 = getColumnModel().getColumn(0);
1560            TableColumn JavaDoc c1 = getColumnModel().getColumn(1);
1561            int min = Math.max(c0.getMinWidth(), getWidth() - c1.getMaxWidth());
1562            int max = Math.min(c0.getMaxWidth(), getWidth() - c1.getMinWidth());
1563
1564            if ((newPos >= min) && (newPos <= max)) {
1565                pos = newPos;
1566                update();
1567            }
1568        }
1569
1570        public boolean isArmed() {
1571            return armed;
1572        }
1573
1574        public boolean isDragging() {
1575            return dragging;
1576        }
1577
1578        public void setArmed(boolean val) {
1579            if (val != armed) {
1580                this.armed = val;
1581
1582                if (armed) {
1583                    BaseTable.this.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
1584                } else {
1585                    BaseTable.this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
1586                }
1587            }
1588        }
1589
1590        private void beginDrag() {
1591            dragging = true;
1592            dragStartTime = System.currentTimeMillis();
1593        }
1594
1595        public void abortDrag() {
1596            dragging = false;
1597            setArmed(false);
1598            repaint();
1599        }
1600
1601        private void finishDrag() {
1602            dragging = false;
1603
1604            if ((System.currentTimeMillis() - dragStartTime) < 400) {
1605                update();
1606            } else {
1607                abortDrag();
1608            }
1609        }
1610
1611        private void update() {
1612            if ((pos < 0) || (pos > getWidth())) {
1613                repaint();
1614
1615                return;
1616            }
1617
1618            int pos0 = pos;
1619            int pos1 = getWidth() - pos;
1620
1621            synchronized (getTreeLock()) {
1622                getColumnModel().getColumn(0).setWidth(pos0);
1623                getColumnModel().getColumn(1).setWidth(pos1);
1624                getColumnModel().getColumn(0).setPreferredWidth(pos0);
1625                getColumnModel().getColumn(1).setPreferredWidth(pos1);
1626            }
1627
1628            BaseTable.this.repaint();
1629        }
1630    }
1631
1632    private class CTRLTabAction extends AbstractAction JavaDoc {
1633        public void actionPerformed(ActionEvent JavaDoc e) {
1634            setFocusCycleRoot(false);
1635
1636            try {
1637                Container JavaDoc con = BaseTable.this.getFocusCycleRootAncestor();
1638
1639                if (con != null) {
1640                    Component JavaDoc target = BaseTable.this;
1641
1642                    if (getParent() instanceof JViewport JavaDoc) {
1643                        target = getParent().getParent();
1644
1645                        if (target == con) {
1646                            target = BaseTable.this;
1647                        }
1648                    }
1649
1650                    EventObject JavaDoc eo = EventQueue.getCurrentEvent();
1651                    boolean backward = false;
1652
1653                    if (eo instanceof KeyEvent JavaDoc) {
1654                        backward = ((((KeyEvent JavaDoc) eo).getModifiers() & KeyEvent.SHIFT_MASK) != 0) &&
1655                            ((((KeyEvent JavaDoc) eo).getModifiersEx() & KeyEvent.SHIFT_DOWN_MASK) != 0);
1656                    }
1657
1658                    Component JavaDoc to = backward ? con.getFocusTraversalPolicy().getComponentAfter(con, BaseTable.this)
1659                                            : con.getFocusTraversalPolicy().getComponentAfter(con, BaseTable.this);
1660
1661                    if (to == BaseTable.this) {
1662                        to = backward ? con.getFocusTraversalPolicy().getFirstComponent(con)
1663                                      : con.getFocusTraversalPolicy().getLastComponent(con);
1664                    }
1665
1666                    to.requestFocus();
1667                }
1668            } finally {
1669                setFocusCycleRoot(true);
1670            }
1671        }
1672    }
1673}
1674
Popular Tags