|
1 /* |
|
2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 |
|
26 package javax.swing.plaf.basic; |
|
27 |
|
28 import java.awt.*; |
|
29 import java.awt.datatransfer.*; |
|
30 import java.awt.dnd.*; |
|
31 import java.awt.event.*; |
|
32 import java.util.Enumeration; |
|
33 import java.util.EventObject; |
|
34 import java.util.Hashtable; |
|
35 import java.util.TooManyListenersException; |
|
36 import javax.swing.*; |
|
37 import javax.swing.event.*; |
|
38 import javax.swing.plaf.*; |
|
39 import javax.swing.text.*; |
|
40 import javax.swing.table.*; |
|
41 import javax.swing.plaf.basic.DragRecognitionSupport.BeforeDrag; |
|
42 import sun.swing.SwingUtilities2; |
|
43 |
|
44 |
|
45 import java.beans.PropertyChangeEvent; |
|
46 import java.beans.PropertyChangeListener; |
|
47 |
|
48 import sun.swing.DefaultLookup; |
|
49 import sun.swing.UIAction; |
|
50 |
|
51 /** |
|
52 * BasicTableUI implementation |
|
53 * |
|
54 * @author Philip Milne |
|
55 * @author Shannon Hickey (drag and drop) |
|
56 */ |
|
57 public class BasicTableUI extends TableUI |
|
58 { |
|
59 private static final StringBuilder BASELINE_COMPONENT_KEY = |
|
60 new StringBuilder("Table.baselineComponent"); |
|
61 |
|
62 // |
|
63 // Instance Variables |
|
64 // |
|
65 |
|
66 // The JTable that is delegating the painting to this UI. |
|
67 /** |
|
68 * The instance of {@code JTable}. |
|
69 */ |
|
70 protected JTable table; |
|
71 |
|
72 /** |
|
73 * The instance of {@code CellRendererPane}. |
|
74 */ |
|
75 protected CellRendererPane rendererPane; |
|
76 |
|
77 /** |
|
78 * {@code KeyListener} that are attached to the {@code JTable}. |
|
79 */ |
|
80 protected KeyListener keyListener; |
|
81 |
|
82 /** |
|
83 * {@code FocusListener} that are attached to the {@code JTable}. |
|
84 */ |
|
85 protected FocusListener focusListener; |
|
86 |
|
87 /** |
|
88 * {@code MouseInputListener} that are attached to the {@code JTable}. |
|
89 */ |
|
90 protected MouseInputListener mouseInputListener; |
|
91 |
|
92 private Handler handler; |
|
93 |
|
94 /** |
|
95 * Local cache of Table's client property "Table.isFileList" |
|
96 */ |
|
97 private boolean isFileList = false; |
|
98 |
|
99 // |
|
100 // Helper class for keyboard actions |
|
101 // |
|
102 |
|
103 private static class Actions extends UIAction { |
|
104 private static final String CANCEL_EDITING = "cancel"; |
|
105 private static final String SELECT_ALL = "selectAll"; |
|
106 private static final String CLEAR_SELECTION = "clearSelection"; |
|
107 private static final String START_EDITING = "startEditing"; |
|
108 |
|
109 private static final String NEXT_ROW = "selectNextRow"; |
|
110 private static final String NEXT_ROW_CELL = "selectNextRowCell"; |
|
111 private static final String NEXT_ROW_EXTEND_SELECTION = |
|
112 "selectNextRowExtendSelection"; |
|
113 private static final String NEXT_ROW_CHANGE_LEAD = |
|
114 "selectNextRowChangeLead"; |
|
115 private static final String PREVIOUS_ROW = "selectPreviousRow"; |
|
116 private static final String PREVIOUS_ROW_CELL = "selectPreviousRowCell"; |
|
117 private static final String PREVIOUS_ROW_EXTEND_SELECTION = |
|
118 "selectPreviousRowExtendSelection"; |
|
119 private static final String PREVIOUS_ROW_CHANGE_LEAD = |
|
120 "selectPreviousRowChangeLead"; |
|
121 |
|
122 private static final String NEXT_COLUMN = "selectNextColumn"; |
|
123 private static final String NEXT_COLUMN_CELL = "selectNextColumnCell"; |
|
124 private static final String NEXT_COLUMN_EXTEND_SELECTION = |
|
125 "selectNextColumnExtendSelection"; |
|
126 private static final String NEXT_COLUMN_CHANGE_LEAD = |
|
127 "selectNextColumnChangeLead"; |
|
128 private static final String PREVIOUS_COLUMN = "selectPreviousColumn"; |
|
129 private static final String PREVIOUS_COLUMN_CELL = |
|
130 "selectPreviousColumnCell"; |
|
131 private static final String PREVIOUS_COLUMN_EXTEND_SELECTION = |
|
132 "selectPreviousColumnExtendSelection"; |
|
133 private static final String PREVIOUS_COLUMN_CHANGE_LEAD = |
|
134 "selectPreviousColumnChangeLead"; |
|
135 |
|
136 private static final String SCROLL_LEFT_CHANGE_SELECTION = |
|
137 "scrollLeftChangeSelection"; |
|
138 private static final String SCROLL_LEFT_EXTEND_SELECTION = |
|
139 "scrollLeftExtendSelection"; |
|
140 private static final String SCROLL_RIGHT_CHANGE_SELECTION = |
|
141 "scrollRightChangeSelection"; |
|
142 private static final String SCROLL_RIGHT_EXTEND_SELECTION = |
|
143 "scrollRightExtendSelection"; |
|
144 |
|
145 private static final String SCROLL_UP_CHANGE_SELECTION = |
|
146 "scrollUpChangeSelection"; |
|
147 private static final String SCROLL_UP_EXTEND_SELECTION = |
|
148 "scrollUpExtendSelection"; |
|
149 private static final String SCROLL_DOWN_CHANGE_SELECTION = |
|
150 "scrollDownChangeSelection"; |
|
151 private static final String SCROLL_DOWN_EXTEND_SELECTION = |
|
152 "scrollDownExtendSelection"; |
|
153 |
|
154 private static final String FIRST_COLUMN = |
|
155 "selectFirstColumn"; |
|
156 private static final String FIRST_COLUMN_EXTEND_SELECTION = |
|
157 "selectFirstColumnExtendSelection"; |
|
158 private static final String LAST_COLUMN = |
|
159 "selectLastColumn"; |
|
160 private static final String LAST_COLUMN_EXTEND_SELECTION = |
|
161 "selectLastColumnExtendSelection"; |
|
162 |
|
163 private static final String FIRST_ROW = |
|
164 "selectFirstRow"; |
|
165 private static final String FIRST_ROW_EXTEND_SELECTION = |
|
166 "selectFirstRowExtendSelection"; |
|
167 private static final String LAST_ROW = |
|
168 "selectLastRow"; |
|
169 private static final String LAST_ROW_EXTEND_SELECTION = |
|
170 "selectLastRowExtendSelection"; |
|
171 |
|
172 // add the lead item to the selection without changing lead or anchor |
|
173 private static final String ADD_TO_SELECTION = "addToSelection"; |
|
174 |
|
175 // toggle the selected state of the lead item and move the anchor to it |
|
176 private static final String TOGGLE_AND_ANCHOR = "toggleAndAnchor"; |
|
177 |
|
178 // extend the selection to the lead item |
|
179 private static final String EXTEND_TO = "extendTo"; |
|
180 |
|
181 // move the anchor to the lead and ensure only that item is selected |
|
182 private static final String MOVE_SELECTION_TO = "moveSelectionTo"; |
|
183 |
|
184 // give focus to the JTableHeader, if one exists |
|
185 private static final String FOCUS_HEADER = "focusHeader"; |
|
186 |
|
187 protected int dx; |
|
188 protected int dy; |
|
189 protected boolean extend; |
|
190 protected boolean inSelection; |
|
191 |
|
192 // horizontally, forwards always means right, |
|
193 // regardless of component orientation |
|
194 protected boolean forwards; |
|
195 protected boolean vertically; |
|
196 protected boolean toLimit; |
|
197 |
|
198 protected int leadRow; |
|
199 protected int leadColumn; |
|
200 |
|
201 Actions(String name) { |
|
202 super(name); |
|
203 } |
|
204 |
|
205 Actions(String name, int dx, int dy, boolean extend, |
|
206 boolean inSelection) { |
|
207 super(name); |
|
208 |
|
209 // Actions spcifying true for "inSelection" are |
|
210 // fairly sensitive to bad parameter values. They require |
|
211 // that one of dx and dy be 0 and the other be -1 or 1. |
|
212 // Bogus parameter values could cause an infinite loop. |
|
213 // To prevent any problems we massage the params here |
|
214 // and complain if we get something we can't deal with. |
|
215 if (inSelection) { |
|
216 this.inSelection = true; |
|
217 |
|
218 // look at the sign of dx and dy only |
|
219 dx = sign(dx); |
|
220 dy = sign(dy); |
|
221 |
|
222 // make sure one is zero, but not both |
|
223 assert (dx == 0 || dy == 0) && !(dx == 0 && dy == 0); |
|
224 } |
|
225 |
|
226 this.dx = dx; |
|
227 this.dy = dy; |
|
228 this.extend = extend; |
|
229 } |
|
230 |
|
231 Actions(String name, boolean extend, boolean forwards, |
|
232 boolean vertically, boolean toLimit) { |
|
233 this(name, 0, 0, extend, false); |
|
234 this.forwards = forwards; |
|
235 this.vertically = vertically; |
|
236 this.toLimit = toLimit; |
|
237 } |
|
238 |
|
239 private static int clipToRange(int i, int a, int b) { |
|
240 return Math.min(Math.max(i, a), b-1); |
|
241 } |
|
242 |
|
243 private void moveWithinTableRange(JTable table, int dx, int dy) { |
|
244 leadRow = clipToRange(leadRow+dy, 0, table.getRowCount()); |
|
245 leadColumn = clipToRange(leadColumn+dx, 0, table.getColumnCount()); |
|
246 } |
|
247 |
|
248 private static int sign(int num) { |
|
249 return (num < 0) ? -1 : ((num == 0) ? 0 : 1); |
|
250 } |
|
251 |
|
252 /** |
|
253 * Called to move within the selected range of the given JTable. |
|
254 * This method uses the table's notion of selection, which is |
|
255 * important to allow the user to navigate between items visually |
|
256 * selected on screen. This notion may or may not be the same as |
|
257 * what could be determined by directly querying the selection models. |
|
258 * It depends on certain table properties (such as whether or not |
|
259 * row or column selection is allowed). When performing modifications, |
|
260 * it is recommended that caution be taken in order to preserve |
|
261 * the intent of this method, especially when deciding whether to |
|
262 * query the selection models or interact with JTable directly. |
|
263 */ |
|
264 private boolean moveWithinSelectedRange(JTable table, int dx, int dy, |
|
265 ListSelectionModel rsm, ListSelectionModel csm) { |
|
266 |
|
267 // Note: The Actions constructor ensures that only one of |
|
268 // dx and dy is 0, and the other is either -1 or 1 |
|
269 |
|
270 // find out how many items the table is showing as selected |
|
271 // and the range of items to navigate through |
|
272 int totalCount; |
|
273 int minX, maxX, minY, maxY; |
|
274 |
|
275 boolean rs = table.getRowSelectionAllowed(); |
|
276 boolean cs = table.getColumnSelectionAllowed(); |
|
277 |
|
278 // both column and row selection |
|
279 if (rs && cs) { |
|
280 totalCount = table.getSelectedRowCount() * table.getSelectedColumnCount(); |
|
281 minX = csm.getMinSelectionIndex(); |
|
282 maxX = csm.getMaxSelectionIndex(); |
|
283 minY = rsm.getMinSelectionIndex(); |
|
284 maxY = rsm.getMaxSelectionIndex(); |
|
285 // row selection only |
|
286 } else if (rs) { |
|
287 totalCount = table.getSelectedRowCount(); |
|
288 minX = 0; |
|
289 maxX = table.getColumnCount() - 1; |
|
290 minY = rsm.getMinSelectionIndex(); |
|
291 maxY = rsm.getMaxSelectionIndex(); |
|
292 // column selection only |
|
293 } else if (cs) { |
|
294 totalCount = table.getSelectedColumnCount(); |
|
295 minX = csm.getMinSelectionIndex(); |
|
296 maxX = csm.getMaxSelectionIndex(); |
|
297 minY = 0; |
|
298 maxY = table.getRowCount() - 1; |
|
299 // no selection allowed |
|
300 } else { |
|
301 totalCount = 0; |
|
302 // A bogus assignment to stop javac from complaining |
|
303 // about unitialized values. In this case, these |
|
304 // won't even be used. |
|
305 minX = maxX = minY = maxY = 0; |
|
306 } |
|
307 |
|
308 // For some cases, there is no point in trying to stay within the |
|
309 // selected area. Instead, move outside the selection, wrapping at |
|
310 // the table boundaries. The cases are: |
|
311 boolean stayInSelection; |
|
312 |
|
313 // - nothing selected |
|
314 if (totalCount == 0 || |
|
315 // - one item selected, and the lead is already selected |
|
316 (totalCount == 1 && table.isCellSelected(leadRow, leadColumn))) { |
|
317 |
|
318 stayInSelection = false; |
|
319 |
|
320 maxX = table.getColumnCount() - 1; |
|
321 maxY = table.getRowCount() - 1; |
|
322 |
|
323 // the mins are calculated like this in case the max is -1 |
|
324 minX = Math.min(0, maxX); |
|
325 minY = Math.min(0, maxY); |
|
326 } else { |
|
327 stayInSelection = true; |
|
328 } |
|
329 |
|
330 // the algorithm below isn't prepared to deal with -1 lead/anchor |
|
331 // so massage appropriately here first |
|
332 if (dy == 1 && leadColumn == -1) { |
|
333 leadColumn = minX; |
|
334 leadRow = -1; |
|
335 } else if (dx == 1 && leadRow == -1) { |
|
336 leadRow = minY; |
|
337 leadColumn = -1; |
|
338 } else if (dy == -1 && leadColumn == -1) { |
|
339 leadColumn = maxX; |
|
340 leadRow = maxY + 1; |
|
341 } else if (dx == -1 && leadRow == -1) { |
|
342 leadRow = maxY; |
|
343 leadColumn = maxX + 1; |
|
344 } |
|
345 |
|
346 // In cases where the lead is not within the search range, |
|
347 // we need to bring it within one cell for the search |
|
348 // to work properly. Check these here. |
|
349 leadRow = Math.min(Math.max(leadRow, minY - 1), maxY + 1); |
|
350 leadColumn = Math.min(Math.max(leadColumn, minX - 1), maxX + 1); |
|
351 |
|
352 // find the next position, possibly looping until it is selected |
|
353 do { |
|
354 calcNextPos(dx, minX, maxX, dy, minY, maxY); |
|
355 } while (stayInSelection && !table.isCellSelected(leadRow, leadColumn)); |
|
356 |
|
357 return stayInSelection; |
|
358 } |
|
359 |
|
360 /** |
|
361 * Find the next lead row and column based on the given |
|
362 * dx/dy and max/min values. |
|
363 */ |
|
364 private void calcNextPos(int dx, int minX, int maxX, |
|
365 int dy, int minY, int maxY) { |
|
366 |
|
367 if (dx != 0) { |
|
368 leadColumn += dx; |
|
369 if (leadColumn > maxX) { |
|
370 leadColumn = minX; |
|
371 leadRow++; |
|
372 if (leadRow > maxY) { |
|
373 leadRow = minY; |
|
374 } |
|
375 } else if (leadColumn < minX) { |
|
376 leadColumn = maxX; |
|
377 leadRow--; |
|
378 if (leadRow < minY) { |
|
379 leadRow = maxY; |
|
380 } |
|
381 } |
|
382 } else { |
|
383 leadRow += dy; |
|
384 if (leadRow > maxY) { |
|
385 leadRow = minY; |
|
386 leadColumn++; |
|
387 if (leadColumn > maxX) { |
|
388 leadColumn = minX; |
|
389 } |
|
390 } else if (leadRow < minY) { |
|
391 leadRow = maxY; |
|
392 leadColumn--; |
|
393 if (leadColumn < minX) { |
|
394 leadColumn = maxX; |
|
395 } |
|
396 } |
|
397 } |
|
398 } |
|
399 |
|
400 public void actionPerformed(ActionEvent e) { |
|
401 String key = getName(); |
|
402 JTable table = (JTable)e.getSource(); |
|
403 |
|
404 ListSelectionModel rsm = table.getSelectionModel(); |
|
405 leadRow = getAdjustedLead(table, true, rsm); |
|
406 |
|
407 ListSelectionModel csm = table.getColumnModel().getSelectionModel(); |
|
408 leadColumn = getAdjustedLead(table, false, csm); |
|
409 |
|
410 if (key == SCROLL_LEFT_CHANGE_SELECTION || // Paging Actions |
|
411 key == SCROLL_LEFT_EXTEND_SELECTION || |
|
412 key == SCROLL_RIGHT_CHANGE_SELECTION || |
|
413 key == SCROLL_RIGHT_EXTEND_SELECTION || |
|
414 key == SCROLL_UP_CHANGE_SELECTION || |
|
415 key == SCROLL_UP_EXTEND_SELECTION || |
|
416 key == SCROLL_DOWN_CHANGE_SELECTION || |
|
417 key == SCROLL_DOWN_EXTEND_SELECTION || |
|
418 key == FIRST_COLUMN || |
|
419 key == FIRST_COLUMN_EXTEND_SELECTION || |
|
420 key == FIRST_ROW || |
|
421 key == FIRST_ROW_EXTEND_SELECTION || |
|
422 key == LAST_COLUMN || |
|
423 key == LAST_COLUMN_EXTEND_SELECTION || |
|
424 key == LAST_ROW || |
|
425 key == LAST_ROW_EXTEND_SELECTION) { |
|
426 if (toLimit) { |
|
427 if (vertically) { |
|
428 int rowCount = table.getRowCount(); |
|
429 this.dx = 0; |
|
430 this.dy = forwards ? rowCount : -rowCount; |
|
431 } |
|
432 else { |
|
433 int colCount = table.getColumnCount(); |
|
434 this.dx = forwards ? colCount : -colCount; |
|
435 this.dy = 0; |
|
436 } |
|
437 } |
|
438 else { |
|
439 if (!(SwingUtilities.getUnwrappedParent(table).getParent() instanceof |
|
440 JScrollPane)) { |
|
441 return; |
|
442 } |
|
443 |
|
444 Dimension delta = table.getParent().getSize(); |
|
445 |
|
446 if (vertically) { |
|
447 Rectangle r = table.getCellRect(leadRow, 0, true); |
|
448 if (forwards) { |
|
449 // scroll by at least one cell |
|
450 r.y += Math.max(delta.height, r.height); |
|
451 } else { |
|
452 r.y -= delta.height; |
|
453 } |
|
454 |
|
455 this.dx = 0; |
|
456 int newRow = table.rowAtPoint(r.getLocation()); |
|
457 if (newRow == -1 && forwards) { |
|
458 newRow = table.getRowCount(); |
|
459 } |
|
460 this.dy = newRow - leadRow; |
|
461 } |
|
462 else { |
|
463 Rectangle r = table.getCellRect(0, leadColumn, true); |
|
464 |
|
465 if (forwards) { |
|
466 // scroll by at least one cell |
|
467 r.x += Math.max(delta.width, r.width); |
|
468 } else { |
|
469 r.x -= delta.width; |
|
470 } |
|
471 |
|
472 int newColumn = table.columnAtPoint(r.getLocation()); |
|
473 if (newColumn == -1) { |
|
474 boolean ltr = table.getComponentOrientation().isLeftToRight(); |
|
475 |
|
476 newColumn = forwards ? (ltr ? table.getColumnCount() : 0) |
|
477 : (ltr ? 0 : table.getColumnCount()); |
|
478 |
|
479 } |
|
480 this.dx = newColumn - leadColumn; |
|
481 this.dy = 0; |
|
482 } |
|
483 } |
|
484 } |
|
485 if (key == NEXT_ROW || // Navigate Actions |
|
486 key == NEXT_ROW_CELL || |
|
487 key == NEXT_ROW_EXTEND_SELECTION || |
|
488 key == NEXT_ROW_CHANGE_LEAD || |
|
489 key == NEXT_COLUMN || |
|
490 key == NEXT_COLUMN_CELL || |
|
491 key == NEXT_COLUMN_EXTEND_SELECTION || |
|
492 key == NEXT_COLUMN_CHANGE_LEAD || |
|
493 key == PREVIOUS_ROW || |
|
494 key == PREVIOUS_ROW_CELL || |
|
495 key == PREVIOUS_ROW_EXTEND_SELECTION || |
|
496 key == PREVIOUS_ROW_CHANGE_LEAD || |
|
497 key == PREVIOUS_COLUMN || |
|
498 key == PREVIOUS_COLUMN_CELL || |
|
499 key == PREVIOUS_COLUMN_EXTEND_SELECTION || |
|
500 key == PREVIOUS_COLUMN_CHANGE_LEAD || |
|
501 // Paging Actions. |
|
502 key == SCROLL_LEFT_CHANGE_SELECTION || |
|
503 key == SCROLL_LEFT_EXTEND_SELECTION || |
|
504 key == SCROLL_RIGHT_CHANGE_SELECTION || |
|
505 key == SCROLL_RIGHT_EXTEND_SELECTION || |
|
506 key == SCROLL_UP_CHANGE_SELECTION || |
|
507 key == SCROLL_UP_EXTEND_SELECTION || |
|
508 key == SCROLL_DOWN_CHANGE_SELECTION || |
|
509 key == SCROLL_DOWN_EXTEND_SELECTION || |
|
510 key == FIRST_COLUMN || |
|
511 key == FIRST_COLUMN_EXTEND_SELECTION || |
|
512 key == FIRST_ROW || |
|
513 key == FIRST_ROW_EXTEND_SELECTION || |
|
514 key == LAST_COLUMN || |
|
515 key == LAST_COLUMN_EXTEND_SELECTION || |
|
516 key == LAST_ROW || |
|
517 key == LAST_ROW_EXTEND_SELECTION) { |
|
518 |
|
519 if (table.isEditing() && |
|
520 !table.getCellEditor().stopCellEditing()) { |
|
521 return; |
|
522 } |
|
523 |
|
524 // Unfortunately, this strategy introduces bugs because |
|
525 // of the asynchronous nature of requestFocus() call below. |
|
526 // Introducing a delay with invokeLater() makes this work |
|
527 // in the typical case though race conditions then allow |
|
528 // focus to disappear altogether. The right solution appears |
|
529 // to be to fix requestFocus() so that it queues a request |
|
530 // for the focus regardless of who owns the focus at the |
|
531 // time the call to requestFocus() is made. The optimisation |
|
532 // to ignore the call to requestFocus() when the component |
|
533 // already has focus may ligitimately be made as the |
|
534 // request focus event is dequeued, not before. |
|
535 |
|
536 // boolean wasEditingWithFocus = table.isEditing() && |
|
537 // table.getEditorComponent().isFocusOwner(); |
|
538 |
|
539 boolean changeLead = false; |
|
540 if (key == NEXT_ROW_CHANGE_LEAD || key == PREVIOUS_ROW_CHANGE_LEAD) { |
|
541 changeLead = (rsm.getSelectionMode() |
|
542 == ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); |
|
543 } else if (key == NEXT_COLUMN_CHANGE_LEAD || key == PREVIOUS_COLUMN_CHANGE_LEAD) { |
|
544 changeLead = (csm.getSelectionMode() |
|
545 == ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); |
|
546 } |
|
547 |
|
548 if (changeLead) { |
|
549 moveWithinTableRange(table, dx, dy); |
|
550 if (dy != 0) { |
|
551 // casting should be safe since the action is only enabled |
|
552 // for DefaultListSelectionModel |
|
553 ((DefaultListSelectionModel)rsm).moveLeadSelectionIndex(leadRow); |
|
554 if (getAdjustedLead(table, false, csm) == -1 |
|
555 && table.getColumnCount() > 0) { |
|
556 |
|
557 ((DefaultListSelectionModel)csm).moveLeadSelectionIndex(0); |
|
558 } |
|
559 } else { |
|
560 // casting should be safe since the action is only enabled |
|
561 // for DefaultListSelectionModel |
|
562 ((DefaultListSelectionModel)csm).moveLeadSelectionIndex(leadColumn); |
|
563 if (getAdjustedLead(table, true, rsm) == -1 |
|
564 && table.getRowCount() > 0) { |
|
565 |
|
566 ((DefaultListSelectionModel)rsm).moveLeadSelectionIndex(0); |
|
567 } |
|
568 } |
|
569 |
|
570 Rectangle cellRect = table.getCellRect(leadRow, leadColumn, false); |
|
571 if (cellRect != null) { |
|
572 table.scrollRectToVisible(cellRect); |
|
573 } |
|
574 } else if (!inSelection) { |
|
575 moveWithinTableRange(table, dx, dy); |
|
576 table.changeSelection(leadRow, leadColumn, false, extend); |
|
577 } |
|
578 else { |
|
579 if (table.getRowCount() <= 0 || table.getColumnCount() <= 0) { |
|
580 // bail - don't try to move selection on an empty table |
|
581 return; |
|
582 } |
|
583 |
|
584 if (moveWithinSelectedRange(table, dx, dy, rsm, csm)) { |
|
585 // this is the only way we have to set both the lead |
|
586 // and the anchor without changing the selection |
|
587 if (rsm.isSelectedIndex(leadRow)) { |
|
588 rsm.addSelectionInterval(leadRow, leadRow); |
|
589 } else { |
|
590 rsm.removeSelectionInterval(leadRow, leadRow); |
|
591 } |
|
592 |
|
593 if (csm.isSelectedIndex(leadColumn)) { |
|
594 csm.addSelectionInterval(leadColumn, leadColumn); |
|
595 } else { |
|
596 csm.removeSelectionInterval(leadColumn, leadColumn); |
|
597 } |
|
598 |
|
599 Rectangle cellRect = table.getCellRect(leadRow, leadColumn, false); |
|
600 if (cellRect != null) { |
|
601 table.scrollRectToVisible(cellRect); |
|
602 } |
|
603 } |
|
604 else { |
|
605 table.changeSelection(leadRow, leadColumn, |
|
606 false, false); |
|
607 } |
|
608 } |
|
609 |
|
610 /* |
|
611 if (wasEditingWithFocus) { |
|
612 table.editCellAt(leadRow, leadColumn); |
|
613 final Component editorComp = table.getEditorComponent(); |
|
614 if (editorComp != null) { |
|
615 SwingUtilities.invokeLater(new Runnable() { |
|
616 public void run() { |
|
617 editorComp.requestFocus(); |
|
618 } |
|
619 }); |
|
620 } |
|
621 } |
|
622 */ |
|
623 } else if (key == CANCEL_EDITING) { |
|
624 table.removeEditor(); |
|
625 } else if (key == SELECT_ALL) { |
|
626 table.selectAll(); |
|
627 } else if (key == CLEAR_SELECTION) { |
|
628 table.clearSelection(); |
|
629 } else if (key == START_EDITING) { |
|
630 if (!table.hasFocus()) { |
|
631 CellEditor cellEditor = table.getCellEditor(); |
|
632 if (cellEditor != null && !cellEditor.stopCellEditing()) { |
|
633 return; |
|
634 } |
|
635 table.requestFocus(); |
|
636 return; |
|
637 } |
|
638 table.editCellAt(leadRow, leadColumn, e); |
|
639 Component editorComp = table.getEditorComponent(); |
|
640 if (editorComp != null) { |
|
641 editorComp.requestFocus(); |
|
642 } |
|
643 } else if (key == ADD_TO_SELECTION) { |
|
644 if (!table.isCellSelected(leadRow, leadColumn)) { |
|
645 int oldAnchorRow = rsm.getAnchorSelectionIndex(); |
|
646 int oldAnchorColumn = csm.getAnchorSelectionIndex(); |
|
647 rsm.setValueIsAdjusting(true); |
|
648 csm.setValueIsAdjusting(true); |
|
649 table.changeSelection(leadRow, leadColumn, true, false); |
|
650 rsm.setAnchorSelectionIndex(oldAnchorRow); |
|
651 csm.setAnchorSelectionIndex(oldAnchorColumn); |
|
652 rsm.setValueIsAdjusting(false); |
|
653 csm.setValueIsAdjusting(false); |
|
654 } |
|
655 } else if (key == TOGGLE_AND_ANCHOR) { |
|
656 table.changeSelection(leadRow, leadColumn, true, false); |
|
657 } else if (key == EXTEND_TO) { |
|
658 table.changeSelection(leadRow, leadColumn, false, true); |
|
659 } else if (key == MOVE_SELECTION_TO) { |
|
660 table.changeSelection(leadRow, leadColumn, false, false); |
|
661 } else if (key == FOCUS_HEADER) { |
|
662 JTableHeader th = table.getTableHeader(); |
|
663 if (th != null) { |
|
664 //Set the header's selected column to match the table. |
|
665 int col = table.getSelectedColumn(); |
|
666 if (col >= 0) { |
|
667 TableHeaderUI thUI = th.getUI(); |
|
668 if (thUI instanceof BasicTableHeaderUI) { |
|
669 ((BasicTableHeaderUI)thUI).selectColumn(col); |
|
670 } |
|
671 } |
|
672 |
|
673 //Then give the header the focus. |
|
674 th.requestFocusInWindow(); |
|
675 } |
|
676 } |
|
677 } |
|
678 |
|
679 @Override |
|
680 public boolean accept(Object sender) { |
|
681 String key = getName(); |
|
682 |
|
683 if (sender instanceof JTable && |
|
684 Boolean.TRUE.equals(((JTable)sender).getClientProperty("Table.isFileList"))) { |
|
685 if (key == NEXT_COLUMN || |
|
686 key == NEXT_COLUMN_CELL || |
|
687 key == NEXT_COLUMN_EXTEND_SELECTION || |
|
688 key == NEXT_COLUMN_CHANGE_LEAD || |
|
689 key == PREVIOUS_COLUMN || |
|
690 key == PREVIOUS_COLUMN_CELL || |
|
691 key == PREVIOUS_COLUMN_EXTEND_SELECTION || |
|
692 key == PREVIOUS_COLUMN_CHANGE_LEAD || |
|
693 key == SCROLL_LEFT_CHANGE_SELECTION || |
|
694 key == SCROLL_LEFT_EXTEND_SELECTION || |
|
695 key == SCROLL_RIGHT_CHANGE_SELECTION || |
|
696 key == SCROLL_RIGHT_EXTEND_SELECTION || |
|
697 key == FIRST_COLUMN || |
|
698 key == FIRST_COLUMN_EXTEND_SELECTION || |
|
699 key == LAST_COLUMN || |
|
700 key == LAST_COLUMN_EXTEND_SELECTION || |
|
701 key == NEXT_ROW_CELL || |
|
702 key == PREVIOUS_ROW_CELL) { |
|
703 |
|
704 return false; |
|
705 } |
|
706 } |
|
707 |
|
708 if (key == CANCEL_EDITING && sender instanceof JTable) { |
|
709 return ((JTable)sender).isEditing(); |
|
710 } else if (key == NEXT_ROW_CHANGE_LEAD || |
|
711 key == PREVIOUS_ROW_CHANGE_LEAD) { |
|
712 // discontinuous selection actions are only enabled for |
|
713 // DefaultListSelectionModel |
|
714 return sender != null && |
|
715 ((JTable)sender).getSelectionModel() |
|
716 instanceof DefaultListSelectionModel; |
|
717 } else if (key == NEXT_COLUMN_CHANGE_LEAD || |
|
718 key == PREVIOUS_COLUMN_CHANGE_LEAD) { |
|
719 // discontinuous selection actions are only enabled for |
|
720 // DefaultListSelectionModel |
|
721 return sender != null && |
|
722 ((JTable)sender).getColumnModel().getSelectionModel() |
|
723 instanceof DefaultListSelectionModel; |
|
724 } else if (key == ADD_TO_SELECTION && sender instanceof JTable) { |
|
725 // This action is typically bound to SPACE. |
|
726 // If the table is already in an editing mode, SPACE should |
|
727 // simply enter a space character into the table, and not |
|
728 // select a cell. Likewise, if the lead cell is already selected |
|
729 // then hitting SPACE should just enter a space character |
|
730 // into the cell and begin editing. In both of these cases |
|
731 // this action will be disabled. |
|
732 JTable table = (JTable)sender; |
|
733 int leadRow = getAdjustedLead(table, true); |
|
734 int leadCol = getAdjustedLead(table, false); |
|
735 return !(table.isEditing() || table.isCellSelected(leadRow, leadCol)); |
|
736 } else if (key == FOCUS_HEADER && sender instanceof JTable) { |
|
737 JTable table = (JTable)sender; |
|
738 return table.getTableHeader() != null; |
|
739 } |
|
740 |
|
741 return true; |
|
742 } |
|
743 } |
|
744 |
|
745 |
|
746 // |
|
747 // The Table's Key listener |
|
748 // |
|
749 |
|
750 /** |
|
751 * This class should be treated as a "protected" inner class. |
|
752 * Instantiate it only within subclasses of {@code BasicTableUI}. |
|
753 * <p>As of Java 2 platform v1.3 this class is no longer used. |
|
754 * Instead <code>JTable</code> |
|
755 * overrides <code>processKeyBinding</code> to dispatch the event to |
|
756 * the current <code>TableCellEditor</code>. |
|
757 */ |
|
758 public class KeyHandler implements KeyListener { |
|
759 // NOTE: This class exists only for backward compatibility. All |
|
760 // its functionality has been moved into Handler. If you need to add |
|
761 // new functionality add it to the Handler, but make sure this |
|
762 // class calls into the Handler. |
|
763 public void keyPressed(KeyEvent e) { |
|
764 getHandler().keyPressed(e); |
|
765 } |
|
766 |
|
767 public void keyReleased(KeyEvent e) { |
|
768 getHandler().keyReleased(e); |
|
769 } |
|
770 |
|
771 public void keyTyped(KeyEvent e) { |
|
772 getHandler().keyTyped(e); |
|
773 } |
|
774 } |
|
775 |
|
776 // |
|
777 // The Table's focus listener |
|
778 // |
|
779 |
|
780 /** |
|
781 * This class should be treated as a "protected" inner class. |
|
782 * Instantiate it only within subclasses of {@code BasicTableUI}. |
|
783 */ |
|
784 public class FocusHandler implements FocusListener { |
|
785 // NOTE: This class exists only for backward compatibility. All |
|
786 // its functionality has been moved into Handler. If you need to add |
|
787 // new functionality add it to the Handler, but make sure this |
|
788 // class calls into the Handler. |
|
789 public void focusGained(FocusEvent e) { |
|
790 getHandler().focusGained(e); |
|
791 } |
|
792 |
|
793 public void focusLost(FocusEvent e) { |
|
794 getHandler().focusLost(e); |
|
795 } |
|
796 } |
|
797 |
|
798 // |
|
799 // The Table's mouse and mouse motion listeners |
|
800 // |
|
801 |
|
802 /** |
|
803 * This class should be treated as a "protected" inner class. |
|
804 * Instantiate it only within subclasses of BasicTableUI. |
|
805 */ |
|
806 public class MouseInputHandler implements MouseInputListener { |
|
807 // NOTE: This class exists only for backward compatibility. All |
|
808 // its functionality has been moved into Handler. If you need to add |
|
809 // new functionality add it to the Handler, but make sure this |
|
810 // class calls into the Handler. |
|
811 public void mouseClicked(MouseEvent e) { |
|
812 getHandler().mouseClicked(e); |
|
813 } |
|
814 |
|
815 public void mousePressed(MouseEvent e) { |
|
816 getHandler().mousePressed(e); |
|
817 } |
|
818 |
|
819 public void mouseReleased(MouseEvent e) { |
|
820 getHandler().mouseReleased(e); |
|
821 } |
|
822 |
|
823 public void mouseEntered(MouseEvent e) { |
|
824 getHandler().mouseEntered(e); |
|
825 } |
|
826 |
|
827 public void mouseExited(MouseEvent e) { |
|
828 getHandler().mouseExited(e); |
|
829 } |
|
830 |
|
831 public void mouseMoved(MouseEvent e) { |
|
832 getHandler().mouseMoved(e); |
|
833 } |
|
834 |
|
835 public void mouseDragged(MouseEvent e) { |
|
836 getHandler().mouseDragged(e); |
|
837 } |
|
838 } |
|
839 |
|
840 private class Handler implements FocusListener, MouseInputListener, |
|
841 PropertyChangeListener, ListSelectionListener, ActionListener, |
|
842 BeforeDrag { |
|
843 |
|
844 // FocusListener |
|
845 private void repaintLeadCell( ) { |
|
846 int lr = getAdjustedLead(table, true); |
|
847 int lc = getAdjustedLead(table, false); |
|
848 |
|
849 if (lr < 0 || lc < 0) { |
|
850 return; |
|
851 } |
|
852 |
|
853 Rectangle dirtyRect = table.getCellRect(lr, lc, false); |
|
854 table.repaint(dirtyRect); |
|
855 } |
|
856 |
|
857 public void focusGained(FocusEvent e) { |
|
858 repaintLeadCell(); |
|
859 } |
|
860 |
|
861 public void focusLost(FocusEvent e) { |
|
862 repaintLeadCell(); |
|
863 } |
|
864 |
|
865 |
|
866 // KeyListener |
|
867 public void keyPressed(KeyEvent e) { } |
|
868 |
|
869 public void keyReleased(KeyEvent e) { } |
|
870 |
|
871 @SuppressWarnings("deprecation") |
|
872 public void keyTyped(KeyEvent e) { |
|
873 KeyStroke keyStroke = KeyStroke.getKeyStroke(e.getKeyChar(), |
|
874 e.getModifiers()); |
|
875 |
|
876 // We register all actions using ANCESTOR_OF_FOCUSED_COMPONENT |
|
877 // which means that we might perform the appropriate action |
|
878 // in the table and then forward it to the editor if the editor |
|
879 // had focus. Make sure this doesn't happen by checking our |
|
880 // InputMaps. |
|
881 InputMap map = table.getInputMap(JComponent.WHEN_FOCUSED); |
|
882 if (map != null && map.get(keyStroke) != null) { |
|
883 return; |
|
884 } |
|
885 map = table.getInputMap(JComponent. |
|
886 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); |
|
887 if (map != null && map.get(keyStroke) != null) { |
|
888 return; |
|
889 } |
|
890 |
|
891 keyStroke = KeyStroke.getKeyStrokeForEvent(e); |
|
892 |
|
893 // The AWT seems to generate an unconsumed \r event when |
|
894 // ENTER (\n) is pressed. |
|
895 if (e.getKeyChar() == '\r') { |
|
896 return; |
|
897 } |
|
898 |
|
899 int leadRow = getAdjustedLead(table, true); |
|
900 int leadColumn = getAdjustedLead(table, false); |
|
901 if (leadRow != -1 && leadColumn != -1 && !table.isEditing()) { |
|
902 if (!table.editCellAt(leadRow, leadColumn)) { |
|
903 return; |
|
904 } |
|
905 } |
|
906 |
|
907 // Forwarding events this way seems to put the component |
|
908 // in a state where it believes it has focus. In reality |
|
909 // the table retains focus - though it is difficult for |
|
910 // a user to tell, since the caret is visible and flashing. |
|
911 |
|
912 // Calling table.requestFocus() here, to get the focus back to |
|
913 // the table, seems to have no effect. |
|
914 |
|
915 Component editorComp = table.getEditorComponent(); |
|
916 if (table.isEditing() && editorComp != null) { |
|
917 if (editorComp instanceof JComponent) { |
|
918 JComponent component = (JComponent)editorComp; |
|
919 map = component.getInputMap(JComponent.WHEN_FOCUSED); |
|
920 Object binding = (map != null) ? map.get(keyStroke) : null; |
|
921 if (binding == null) { |
|
922 map = component.getInputMap(JComponent. |
|
923 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); |
|
924 binding = (map != null) ? map.get(keyStroke) : null; |
|
925 } |
|
926 if (binding != null) { |
|
927 ActionMap am = component.getActionMap(); |
|
928 Action action = (am != null) ? am.get(binding) : null; |
|
929 if (action != null && SwingUtilities. |
|
930 notifyAction(action, keyStroke, e, component, |
|
931 e.getModifiers())) { |
|
932 e.consume(); |
|
933 } |
|
934 } |
|
935 } |
|
936 } |
|
937 } |
|
938 |
|
939 |
|
940 // MouseInputListener |
|
941 |
|
942 // Component receiving mouse events during editing. |
|
943 // May not be editorComponent. |
|
944 private Component dispatchComponent; |
|
945 |
|
946 public void mouseClicked(MouseEvent e) {} |
|
947 |
|
948 private void setDispatchComponent(MouseEvent e) { |
|
949 Component editorComponent = table.getEditorComponent(); |
|
950 Point p = e.getPoint(); |
|
951 Point p2 = SwingUtilities.convertPoint(table, p, editorComponent); |
|
952 dispatchComponent = |
|
953 SwingUtilities.getDeepestComponentAt(editorComponent, |
|
954 p2.x, p2.y); |
|
955 SwingUtilities2.setSkipClickCount(dispatchComponent, |
|
956 e.getClickCount() - 1); |
|
957 } |
|
958 |
|
959 private boolean repostEvent(MouseEvent e) { |
|
960 // Check for isEditing() in case another event has |
|
961 // caused the editor to be removed. See bug #4306499. |
|
962 if (dispatchComponent == null || !table.isEditing()) { |
|
963 return false; |
|
964 } |
|
965 MouseEvent e2 = SwingUtilities.convertMouseEvent(table, e, |
|
966 dispatchComponent); |
|
967 dispatchComponent.dispatchEvent(e2); |
|
968 return true; |
|
969 } |
|
970 |
|
971 private void setValueIsAdjusting(boolean flag) { |
|
972 table.getSelectionModel().setValueIsAdjusting(flag); |
|
973 table.getColumnModel().getSelectionModel(). |
|
974 setValueIsAdjusting(flag); |
|
975 } |
|
976 |
|
977 // The row and column where the press occurred and the |
|
978 // press event itself |
|
979 private int pressedRow; |
|
980 private int pressedCol; |
|
981 private MouseEvent pressedEvent; |
|
982 |
|
983 // Whether or not the mouse press (which is being considered as part |
|
984 // of a drag sequence) also caused the selection change to be fully |
|
985 // processed. |
|
986 private boolean dragPressDidSelection; |
|
987 |
|
988 // Set to true when a drag gesture has been fully recognized and DnD |
|
989 // begins. Use this to ignore further mouse events which could be |
|
990 // delivered if DnD is cancelled (via ESCAPE for example) |
|
991 private boolean dragStarted; |
|
992 |
|
993 // Whether or not we should start the editing timer on release |
|
994 private boolean shouldStartTimer; |
|
995 |
|
996 // To cache the return value of pointOutsidePrefSize since we use |
|
997 // it multiple times. |
|
998 private boolean outsidePrefSize; |
|
999 |
|
1000 // Used to delay the start of editing. |
|
1001 private Timer timer = null; |
|
1002 |
|
1003 private boolean canStartDrag() { |
|
1004 if (pressedRow == -1 || pressedCol == -1) { |
|
1005 return false; |
|
1006 } |
|
1007 |
|
1008 if (isFileList) { |
|
1009 return !outsidePrefSize; |
|
1010 } |
|
1011 |
|
1012 // if this is a single selection table |
|
1013 if ((table.getSelectionModel().getSelectionMode() == |
|
1014 ListSelectionModel.SINGLE_SELECTION) && |
|
1015 (table.getColumnModel().getSelectionModel().getSelectionMode() == |
|
1016 ListSelectionModel.SINGLE_SELECTION)) { |
|
1017 |
|
1018 return true; |
|
1019 } |
|
1020 |
|
1021 return table.isCellSelected(pressedRow, pressedCol); |
|
1022 } |
|
1023 |
|
1024 public void mousePressed(MouseEvent e) { |
|
1025 if (SwingUtilities2.shouldIgnore(e, table)) { |
|
1026 return; |
|
1027 } |
|
1028 |
|
1029 if (table.isEditing() && !table.getCellEditor().stopCellEditing()) { |
|
1030 Component editorComponent = table.getEditorComponent(); |
|
1031 if (editorComponent != null && !editorComponent.hasFocus()) { |
|
1032 SwingUtilities2.compositeRequestFocus(editorComponent); |
|
1033 } |
|
1034 return; |
|
1035 } |
|
1036 |
|
1037 Point p = e.getPoint(); |
|
1038 pressedRow = table.rowAtPoint(p); |
|
1039 pressedCol = table.columnAtPoint(p); |
|
1040 outsidePrefSize = pointOutsidePrefSize(pressedRow, pressedCol, p); |
|
1041 |
|
1042 if (isFileList) { |
|
1043 shouldStartTimer = |
|
1044 table.isCellSelected(pressedRow, pressedCol) && |
|
1045 !e.isShiftDown() && |
|
1046 !BasicGraphicsUtils.isMenuShortcutKeyDown(e) && |
|
1047 !outsidePrefSize; |
|
1048 } |
|
1049 |
|
1050 if (table.getDragEnabled()) { |
|
1051 mousePressedDND(e); |
|
1052 } else { |
|
1053 SwingUtilities2.adjustFocus(table); |
|
1054 if (!isFileList) { |
|
1055 setValueIsAdjusting(true); |
|
1056 } |
|
1057 adjustSelection(e); |
|
1058 } |
|
1059 } |
|
1060 |
|
1061 private void mousePressedDND(MouseEvent e) { |
|
1062 pressedEvent = e; |
|
1063 boolean grabFocus = true; |
|
1064 dragStarted = false; |
|
1065 |
|
1066 if (canStartDrag() && DragRecognitionSupport.mousePressed(e)) { |
|
1067 |
|
1068 dragPressDidSelection = false; |
|
1069 |
|
1070 if (BasicGraphicsUtils.isMenuShortcutKeyDown(e) && isFileList) { |
|
1071 // do nothing for control - will be handled on release |
|
1072 // or when drag starts |
|
1073 return; |
|
1074 } else if (!e.isShiftDown() && table.isCellSelected(pressedRow, pressedCol)) { |
|
1075 // clicking on something that's already selected |
|
1076 // and need to make it the lead now |
|
1077 table.getSelectionModel().addSelectionInterval(pressedRow, |
|
1078 pressedRow); |
|
1079 table.getColumnModel().getSelectionModel(). |
|
1080 addSelectionInterval(pressedCol, pressedCol); |
|
1081 |
|
1082 return; |
|
1083 } |
|
1084 |
|
1085 dragPressDidSelection = true; |
|
1086 |
|
1087 // could be a drag initiating event - don't grab focus |
|
1088 grabFocus = false; |
|
1089 } else if (!isFileList) { |
|
1090 // When drag can't happen, mouse drags might change the selection in the table |
|
1091 // so we want the isAdjusting flag to be set |
|
1092 setValueIsAdjusting(true); |
|
1093 } |
|
1094 |
|
1095 if (grabFocus) { |
|
1096 SwingUtilities2.adjustFocus(table); |
|
1097 } |
|
1098 |
|
1099 adjustSelection(e); |
|
1100 } |
|
1101 |
|
1102 private void adjustSelection(MouseEvent e) { |
|
1103 // Fix for 4835633 |
|
1104 if (outsidePrefSize) { |
|
1105 // If shift is down in multi-select, we should just return. |
|
1106 // For single select or non-shift-click, clear the selection |
|
1107 if (e.getID() == MouseEvent.MOUSE_PRESSED && |
|
1108 (!e.isShiftDown() || |
|
1109 table.getSelectionModel().getSelectionMode() == |
|
1110 ListSelectionModel.SINGLE_SELECTION)) { |
|
1111 table.clearSelection(); |
|
1112 TableCellEditor tce = table.getCellEditor(); |
|
1113 if (tce != null) { |
|
1114 tce.stopCellEditing(); |
|
1115 } |
|
1116 } |
|
1117 return; |
|
1118 } |
|
1119 // The autoscroller can generate drag events outside the |
|
1120 // table's range. |
|
1121 if ((pressedCol == -1) || (pressedRow == -1)) { |
|
1122 return; |
|
1123 } |
|
1124 |
|
1125 boolean dragEnabled = table.getDragEnabled(); |
|
1126 |
|
1127 if (!dragEnabled && !isFileList && table.editCellAt(pressedRow, pressedCol, e)) { |
|
1128 setDispatchComponent(e); |
|
1129 repostEvent(e); |
|
1130 } |
|
1131 |
|
1132 CellEditor editor = table.getCellEditor(); |
|
1133 if (dragEnabled || editor == null || editor.shouldSelectCell(e)) { |
|
1134 table.changeSelection(pressedRow, pressedCol, |
|
1135 BasicGraphicsUtils.isMenuShortcutKeyDown(e), |
|
1136 e.isShiftDown()); |
|
1137 } |
|
1138 } |
|
1139 |
|
1140 public void valueChanged(ListSelectionEvent e) { |
|
1141 if (timer != null) { |
|
1142 timer.stop(); |
|
1143 timer = null; |
|
1144 } |
|
1145 } |
|
1146 |
|
1147 public void actionPerformed(ActionEvent ae) { |
|
1148 table.editCellAt(pressedRow, pressedCol, null); |
|
1149 Component editorComponent = table.getEditorComponent(); |
|
1150 if (editorComponent != null && !editorComponent.hasFocus()) { |
|
1151 SwingUtilities2.compositeRequestFocus(editorComponent); |
|
1152 } |
|
1153 return; |
|
1154 } |
|
1155 |
|
1156 private void maybeStartTimer() { |
|
1157 if (!shouldStartTimer) { |
|
1158 return; |
|
1159 } |
|
1160 |
|
1161 if (timer == null) { |
|
1162 timer = new Timer(1200, this); |
|
1163 timer.setRepeats(false); |
|
1164 } |
|
1165 |
|
1166 timer.start(); |
|
1167 } |
|
1168 |
|
1169 public void mouseReleased(MouseEvent e) { |
|
1170 if (SwingUtilities2.shouldIgnore(e, table)) { |
|
1171 return; |
|
1172 } |
|
1173 |
|
1174 if (table.getDragEnabled()) { |
|
1175 mouseReleasedDND(e); |
|
1176 } else { |
|
1177 if (isFileList) { |
|
1178 maybeStartTimer(); |
|
1179 } |
|
1180 } |
|
1181 |
|
1182 pressedEvent = null; |
|
1183 repostEvent(e); |
|
1184 dispatchComponent = null; |
|
1185 setValueIsAdjusting(false); |
|
1186 } |
|
1187 |
|
1188 private void mouseReleasedDND(MouseEvent e) { |
|
1189 MouseEvent me = DragRecognitionSupport.mouseReleased(e); |
|
1190 if (me != null) { |
|
1191 SwingUtilities2.adjustFocus(table); |
|
1192 if (!dragPressDidSelection) { |
|
1193 adjustSelection(me); |
|
1194 } |
|
1195 } |
|
1196 |
|
1197 if (!dragStarted) { |
|
1198 if (isFileList) { |
|
1199 maybeStartTimer(); |
|
1200 return; |
|
1201 } |
|
1202 |
|
1203 Point p = e.getPoint(); |
|
1204 |
|
1205 if (pressedEvent != null && |
|
1206 table.rowAtPoint(p) == pressedRow && |
|
1207 table.columnAtPoint(p) == pressedCol && |
|
1208 table.editCellAt(pressedRow, pressedCol, pressedEvent)) { |
|
1209 |
|
1210 setDispatchComponent(pressedEvent); |
|
1211 repostEvent(pressedEvent); |
|
1212 |
|
1213 // This may appear completely odd, but must be done for backward |
|
1214 // compatibility reasons. Developers have been known to rely on |
|
1215 // a call to shouldSelectCell after editing has begun. |
|
1216 CellEditor ce = table.getCellEditor(); |
|
1217 if (ce != null) { |
|
1218 ce.shouldSelectCell(pressedEvent); |
|
1219 } |
|
1220 } |
|
1221 } |
|
1222 } |
|
1223 |
|
1224 public void mouseEntered(MouseEvent e) {} |
|
1225 |
|
1226 public void mouseExited(MouseEvent e) {} |
|
1227 |
|
1228 public void mouseMoved(MouseEvent e) {} |
|
1229 |
|
1230 public void dragStarting(MouseEvent me) { |
|
1231 dragStarted = true; |
|
1232 |
|
1233 if (BasicGraphicsUtils.isMenuShortcutKeyDown(me) && isFileList) { |
|
1234 table.getSelectionModel().addSelectionInterval(pressedRow, |
|
1235 pressedRow); |
|
1236 table.getColumnModel().getSelectionModel(). |
|
1237 addSelectionInterval(pressedCol, pressedCol); |
|
1238 } |
|
1239 |
|
1240 pressedEvent = null; |
|
1241 } |
|
1242 |
|
1243 public void mouseDragged(MouseEvent e) { |
|
1244 if (SwingUtilities2.shouldIgnore(e, table)) { |
|
1245 return; |
|
1246 } |
|
1247 |
|
1248 if (table.getDragEnabled() && |
|
1249 (DragRecognitionSupport.mouseDragged(e, this) || dragStarted)) { |
|
1250 |
|
1251 return; |
|
1252 } |
|
1253 |
|
1254 repostEvent(e); |
|
1255 |
|
1256 // Check isFileList: |
|
1257 // Until we support drag-selection, dragging should not change |
|
1258 // the selection (act like single-select). |
|
1259 if (isFileList || table.isEditing()) { |
|
1260 return; |
|
1261 } |
|
1262 |
|
1263 Point p = e.getPoint(); |
|
1264 int row = table.rowAtPoint(p); |
|
1265 int column = table.columnAtPoint(p); |
|
1266 // The autoscroller can generate drag events outside the |
|
1267 // table's range. |
|
1268 if ((column == -1) || (row == -1)) { |
|
1269 return; |
|
1270 } |
|
1271 |
|
1272 table.changeSelection(row, column, |
|
1273 BasicGraphicsUtils.isMenuShortcutKeyDown(e), true); |
|
1274 } |
|
1275 |
|
1276 |
|
1277 // PropertyChangeListener |
|
1278 public void propertyChange(PropertyChangeEvent event) { |
|
1279 String changeName = event.getPropertyName(); |
|
1280 |
|
1281 if ("componentOrientation" == changeName) { |
|
1282 InputMap inputMap = getInputMap( |
|
1283 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); |
|
1284 |
|
1285 SwingUtilities.replaceUIInputMap(table, |
|
1286 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, |
|
1287 inputMap); |
|
1288 |
|
1289 JTableHeader header = table.getTableHeader(); |
|
1290 if (header != null) { |
|
1291 header.setComponentOrientation( |
|
1292 (ComponentOrientation)event.getNewValue()); |
|
1293 } |
|
1294 } else if ("dropLocation" == changeName) { |
|
1295 JTable.DropLocation oldValue = (JTable.DropLocation)event.getOldValue(); |
|
1296 repaintDropLocation(oldValue); |
|
1297 repaintDropLocation(table.getDropLocation()); |
|
1298 } else if ("Table.isFileList" == changeName) { |
|
1299 isFileList = Boolean.TRUE.equals(table.getClientProperty("Table.isFileList")); |
|
1300 table.revalidate(); |
|
1301 table.repaint(); |
|
1302 if (isFileList) { |
|
1303 table.getSelectionModel().addListSelectionListener(getHandler()); |
|
1304 } else { |
|
1305 table.getSelectionModel().removeListSelectionListener(getHandler()); |
|
1306 timer = null; |
|
1307 } |
|
1308 } else if ("selectionModel" == changeName) { |
|
1309 if (isFileList) { |
|
1310 ListSelectionModel old = (ListSelectionModel)event.getOldValue(); |
|
1311 old.removeListSelectionListener(getHandler()); |
|
1312 table.getSelectionModel().addListSelectionListener(getHandler()); |
|
1313 } |
|
1314 } |
|
1315 } |
|
1316 |
|
1317 private void repaintDropLocation(JTable.DropLocation loc) { |
|
1318 if (loc == null) { |
|
1319 return; |
|
1320 } |
|
1321 |
|
1322 if (!loc.isInsertRow() && !loc.isInsertColumn()) { |
|
1323 Rectangle rect = table.getCellRect(loc.getRow(), loc.getColumn(), false); |
|
1324 if (rect != null) { |
|
1325 table.repaint(rect); |
|
1326 } |
|
1327 return; |
|
1328 } |
|
1329 |
|
1330 if (loc.isInsertRow()) { |
|
1331 Rectangle rect = extendRect(getHDropLineRect(loc), true); |
|
1332 if (rect != null) { |
|
1333 table.repaint(rect); |
|
1334 } |
|
1335 } |
|
1336 |
|
1337 if (loc.isInsertColumn()) { |
|
1338 Rectangle rect = extendRect(getVDropLineRect(loc), false); |
|
1339 if (rect != null) { |
|
1340 table.repaint(rect); |
|
1341 } |
|
1342 } |
|
1343 } |
|
1344 } |
|
1345 |
|
1346 |
|
1347 /* |
|
1348 * Returns true if the given point is outside the preferredSize of the |
|
1349 * item at the given row of the table. (Column must be 0). |
|
1350 * Returns false if the "Table.isFileList" client property is not set. |
|
1351 */ |
|
1352 private boolean pointOutsidePrefSize(int row, int column, Point p) { |
|
1353 if (!isFileList) { |
|
1354 return false; |
|
1355 } |
|
1356 |
|
1357 return SwingUtilities2.pointOutsidePrefSize(table, row, column, p); |
|
1358 } |
|
1359 |
|
1360 // |
|
1361 // Factory methods for the Listeners |
|
1362 // |
|
1363 |
|
1364 private Handler getHandler() { |
|
1365 if (handler == null) { |
|
1366 handler = new Handler(); |
|
1367 } |
|
1368 return handler; |
|
1369 } |
|
1370 |
|
1371 /** |
|
1372 * Creates the key listener for handling keyboard navigation in the {@code JTable}. |
|
1373 * |
|
1374 * @return the key listener for handling keyboard navigation in the {@code JTable} |
|
1375 */ |
|
1376 protected KeyListener createKeyListener() { |
|
1377 return null; |
|
1378 } |
|
1379 |
|
1380 /** |
|
1381 * Creates the focus listener for handling keyboard navigation in the {@code JTable}. |
|
1382 * |
|
1383 * @return the focus listener for handling keyboard navigation in the {@code JTable} |
|
1384 */ |
|
1385 protected FocusListener createFocusListener() { |
|
1386 return getHandler(); |
|
1387 } |
|
1388 |
|
1389 /** |
|
1390 * Creates the mouse listener for the {@code JTable}. |
|
1391 * |
|
1392 * @return the mouse listener for the {@code JTable} |
|
1393 */ |
|
1394 protected MouseInputListener createMouseInputListener() { |
|
1395 return getHandler(); |
|
1396 } |
|
1397 |
|
1398 // |
|
1399 // The installation/uninstall procedures and support |
|
1400 // |
|
1401 |
|
1402 /** |
|
1403 * Returns a new instance of {@code BasicTableUI}. |
|
1404 * |
|
1405 * @param c a component |
|
1406 * @return a new instance of {@code BasicTableUI} |
|
1407 */ |
|
1408 public static ComponentUI createUI(JComponent c) { |
|
1409 return new BasicTableUI(); |
|
1410 } |
|
1411 |
|
1412 // Installation |
|
1413 |
|
1414 public void installUI(JComponent c) { |
|
1415 table = (JTable)c; |
|
1416 |
|
1417 rendererPane = new CellRendererPane(); |
|
1418 table.add(rendererPane); |
|
1419 installDefaults(); |
|
1420 installDefaults2(); |
|
1421 installListeners(); |
|
1422 installKeyboardActions(); |
|
1423 } |
|
1424 |
|
1425 /** |
|
1426 * Initialize JTable properties, e.g. font, foreground, and background. |
|
1427 * The font, foreground, and background properties are only set if their |
|
1428 * current value is either null or a UIResource, other properties are set |
|
1429 * if the current value is null. |
|
1430 * |
|
1431 * @see #installUI |
|
1432 */ |
|
1433 protected void installDefaults() { |
|
1434 LookAndFeel.installColorsAndFont(table, "Table.background", |
|
1435 "Table.foreground", "Table.font"); |
|
1436 // JTable's original row height is 16. To correctly display the |
|
1437 // contents on Linux we should have set it to 18, Windows 19 and |
|
1438 // Solaris 20. As these values vary so much it's too hard to |
|
1439 // be backward compatable and try to update the row height, we're |
|
1440 // therefor NOT going to adjust the row height based on font. If the |
|
1441 // developer changes the font, it's there responsability to update |
|
1442 // the row height. |
|
1443 |
|
1444 LookAndFeel.installProperty(table, "opaque", Boolean.TRUE); |
|
1445 |
|
1446 Color sbg = table.getSelectionBackground(); |
|
1447 if (sbg == null || sbg instanceof UIResource) { |
|
1448 sbg = UIManager.getColor("Table.selectionBackground"); |
|
1449 table.setSelectionBackground(sbg != null ? sbg : UIManager.getColor("textHighlight")); |
|
1450 } |
|
1451 |
|
1452 Color sfg = table.getSelectionForeground(); |
|
1453 if (sfg == null || sfg instanceof UIResource) { |
|
1454 sfg = UIManager.getColor("Table.selectionForeground"); |
|
1455 table.setSelectionForeground(sfg != null ? sfg : UIManager.getColor("textHighlightText")); |
|
1456 } |
|
1457 |
|
1458 Color gridColor = table.getGridColor(); |
|
1459 if (gridColor == null || gridColor instanceof UIResource) { |
|
1460 gridColor = UIManager.getColor("Table.gridColor"); |
|
1461 table.setGridColor(gridColor != null ? gridColor : Color.GRAY); |
|
1462 } |
|
1463 |
|
1464 // install the scrollpane border |
|
1465 Container parent = SwingUtilities.getUnwrappedParent(table); // should be viewport |
|
1466 if (parent != null) { |
|
1467 parent = parent.getParent(); // should be the scrollpane |
|
1468 if (parent != null && parent instanceof JScrollPane) { |
|
1469 LookAndFeel.installBorder((JScrollPane)parent, "Table.scrollPaneBorder"); |
|
1470 } |
|
1471 } |
|
1472 |
|
1473 isFileList = Boolean.TRUE.equals(table.getClientProperty("Table.isFileList")); |
|
1474 } |
|
1475 |
|
1476 private void installDefaults2() { |
|
1477 TransferHandler th = table.getTransferHandler(); |
|
1478 if (th == null || th instanceof UIResource) { |
|
1479 table.setTransferHandler(defaultTransferHandler); |
|
1480 // default TransferHandler doesn't support drop |
|
1481 // so we don't want drop handling |
|
1482 if (table.getDropTarget() instanceof UIResource) { |
|
1483 table.setDropTarget(null); |
|
1484 } |
|
1485 } |
|
1486 } |
|
1487 |
|
1488 /** |
|
1489 * Attaches listeners to the JTable. |
|
1490 */ |
|
1491 protected void installListeners() { |
|
1492 focusListener = createFocusListener(); |
|
1493 keyListener = createKeyListener(); |
|
1494 mouseInputListener = createMouseInputListener(); |
|
1495 |
|
1496 table.addFocusListener(focusListener); |
|
1497 table.addKeyListener(keyListener); |
|
1498 table.addMouseListener(mouseInputListener); |
|
1499 table.addMouseMotionListener(mouseInputListener); |
|
1500 table.addPropertyChangeListener(getHandler()); |
|
1501 if (isFileList) { |
|
1502 table.getSelectionModel().addListSelectionListener(getHandler()); |
|
1503 } |
|
1504 } |
|
1505 |
|
1506 /** |
|
1507 * Register all keyboard actions on the JTable. |
|
1508 */ |
|
1509 protected void installKeyboardActions() { |
|
1510 LazyActionMap.installLazyActionMap(table, BasicTableUI.class, |
|
1511 "Table.actionMap"); |
|
1512 |
|
1513 InputMap inputMap = getInputMap(JComponent. |
|
1514 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); |
|
1515 SwingUtilities.replaceUIInputMap(table, |
|
1516 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, |
|
1517 inputMap); |
|
1518 } |
|
1519 |
|
1520 InputMap getInputMap(int condition) { |
|
1521 if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) { |
|
1522 InputMap keyMap = |
|
1523 (InputMap)DefaultLookup.get(table, this, |
|
1524 "Table.ancestorInputMap"); |
|
1525 InputMap rtlKeyMap; |
|
1526 |
|
1527 if (table.getComponentOrientation().isLeftToRight() || |
|
1528 ((rtlKeyMap = (InputMap)DefaultLookup.get(table, this, |
|
1529 "Table.ancestorInputMap.RightToLeft")) == null)) { |
|
1530 return keyMap; |
|
1531 } else { |
|
1532 rtlKeyMap.setParent(keyMap); |
|
1533 return rtlKeyMap; |
|
1534 } |
|
1535 } |
|
1536 return null; |
|
1537 } |
|
1538 |
|
1539 static void loadActionMap(LazyActionMap map) { |
|
1540 // IMPORTANT: There is a very close coupling between the parameters |
|
1541 // passed to the Actions constructor. Only certain parameter |
|
1542 // combinations are supported. For example, the following Action would |
|
1543 // not work as expected: |
|
1544 // new Actions(Actions.NEXT_ROW_CELL, 1, 4, false, true) |
|
1545 // Actions which move within the selection only (having a true |
|
1546 // inSelection parameter) require that one of dx or dy be |
|
1547 // zero and the other be -1 or 1. The point of this warning is |
|
1548 // that you should be very careful about making sure a particular |
|
1549 // combination of parameters is supported before changing or |
|
1550 // adding anything here. |
|
1551 |
|
1552 map.put(new Actions(Actions.NEXT_COLUMN, 1, 0, |
|
1553 false, false)); |
|
1554 map.put(new Actions(Actions.NEXT_COLUMN_CHANGE_LEAD, 1, 0, |
|
1555 false, false)); |
|
1556 map.put(new Actions(Actions.PREVIOUS_COLUMN, -1, 0, |
|
1557 false, false)); |
|
1558 map.put(new Actions(Actions.PREVIOUS_COLUMN_CHANGE_LEAD, -1, 0, |
|
1559 false, false)); |
|
1560 map.put(new Actions(Actions.NEXT_ROW, 0, 1, |
|
1561 false, false)); |
|
1562 map.put(new Actions(Actions.NEXT_ROW_CHANGE_LEAD, 0, 1, |
|
1563 false, false)); |
|
1564 map.put(new Actions(Actions.PREVIOUS_ROW, 0, -1, |
|
1565 false, false)); |
|
1566 map.put(new Actions(Actions.PREVIOUS_ROW_CHANGE_LEAD, 0, -1, |
|
1567 false, false)); |
|
1568 map.put(new Actions(Actions.NEXT_COLUMN_EXTEND_SELECTION, |
|
1569 1, 0, true, false)); |
|
1570 map.put(new Actions(Actions.PREVIOUS_COLUMN_EXTEND_SELECTION, |
|
1571 -1, 0, true, false)); |
|
1572 map.put(new Actions(Actions.NEXT_ROW_EXTEND_SELECTION, |
|
1573 0, 1, true, false)); |
|
1574 map.put(new Actions(Actions.PREVIOUS_ROW_EXTEND_SELECTION, |
|
1575 0, -1, true, false)); |
|
1576 map.put(new Actions(Actions.SCROLL_UP_CHANGE_SELECTION, |
|
1577 false, false, true, false)); |
|
1578 map.put(new Actions(Actions.SCROLL_DOWN_CHANGE_SELECTION, |
|
1579 false, true, true, false)); |
|
1580 map.put(new Actions(Actions.FIRST_COLUMN, |
|
1581 false, false, false, true)); |
|
1582 map.put(new Actions(Actions.LAST_COLUMN, |
|
1583 false, true, false, true)); |
|
1584 |
|
1585 map.put(new Actions(Actions.SCROLL_UP_EXTEND_SELECTION, |
|
1586 true, false, true, false)); |
|
1587 map.put(new Actions(Actions.SCROLL_DOWN_EXTEND_SELECTION, |
|
1588 true, true, true, false)); |
|
1589 map.put(new Actions(Actions.FIRST_COLUMN_EXTEND_SELECTION, |
|
1590 true, false, false, true)); |
|
1591 map.put(new Actions(Actions.LAST_COLUMN_EXTEND_SELECTION, |
|
1592 true, true, false, true)); |
|
1593 |
|
1594 map.put(new Actions(Actions.FIRST_ROW, false, false, true, true)); |
|
1595 map.put(new Actions(Actions.LAST_ROW, false, true, true, true)); |
|
1596 |
|
1597 map.put(new Actions(Actions.FIRST_ROW_EXTEND_SELECTION, |
|
1598 true, false, true, true)); |
|
1599 map.put(new Actions(Actions.LAST_ROW_EXTEND_SELECTION, |
|
1600 true, true, true, true)); |
|
1601 |
|
1602 map.put(new Actions(Actions.NEXT_COLUMN_CELL, |
|
1603 1, 0, false, true)); |
|
1604 map.put(new Actions(Actions.PREVIOUS_COLUMN_CELL, |
|
1605 -1, 0, false, true)); |
|
1606 map.put(new Actions(Actions.NEXT_ROW_CELL, 0, 1, false, true)); |
|
1607 map.put(new Actions(Actions.PREVIOUS_ROW_CELL, |
|
1608 0, -1, false, true)); |
|
1609 |
|
1610 map.put(new Actions(Actions.SELECT_ALL)); |
|
1611 map.put(new Actions(Actions.CLEAR_SELECTION)); |
|
1612 map.put(new Actions(Actions.CANCEL_EDITING)); |
|
1613 map.put(new Actions(Actions.START_EDITING)); |
|
1614 |
|
1615 map.put(TransferHandler.getCutAction().getValue(Action.NAME), |
|
1616 TransferHandler.getCutAction()); |
|
1617 map.put(TransferHandler.getCopyAction().getValue(Action.NAME), |
|
1618 TransferHandler.getCopyAction()); |
|
1619 map.put(TransferHandler.getPasteAction().getValue(Action.NAME), |
|
1620 TransferHandler.getPasteAction()); |
|
1621 |
|
1622 map.put(new Actions(Actions.SCROLL_LEFT_CHANGE_SELECTION, |
|
1623 false, false, false, false)); |
|
1624 map.put(new Actions(Actions.SCROLL_RIGHT_CHANGE_SELECTION, |
|
1625 false, true, false, false)); |
|
1626 map.put(new Actions(Actions.SCROLL_LEFT_EXTEND_SELECTION, |
|
1627 true, false, false, false)); |
|
1628 map.put(new Actions(Actions.SCROLL_RIGHT_EXTEND_SELECTION, |
|
1629 true, true, false, false)); |
|
1630 |
|
1631 map.put(new Actions(Actions.ADD_TO_SELECTION)); |
|
1632 map.put(new Actions(Actions.TOGGLE_AND_ANCHOR)); |
|
1633 map.put(new Actions(Actions.EXTEND_TO)); |
|
1634 map.put(new Actions(Actions.MOVE_SELECTION_TO)); |
|
1635 map.put(new Actions(Actions.FOCUS_HEADER)); |
|
1636 } |
|
1637 |
|
1638 // Uninstallation |
|
1639 |
|
1640 public void uninstallUI(JComponent c) { |
|
1641 uninstallDefaults(); |
|
1642 uninstallListeners(); |
|
1643 uninstallKeyboardActions(); |
|
1644 |
|
1645 table.remove(rendererPane); |
|
1646 rendererPane = null; |
|
1647 table = null; |
|
1648 } |
|
1649 |
|
1650 /** |
|
1651 * Uninstalls default properties. |
|
1652 */ |
|
1653 protected void uninstallDefaults() { |
|
1654 if (table.getTransferHandler() instanceof UIResource) { |
|
1655 table.setTransferHandler(null); |
|
1656 } |
|
1657 } |
|
1658 |
|
1659 /** |
|
1660 * Unregisters listeners. |
|
1661 */ |
|
1662 protected void uninstallListeners() { |
|
1663 table.removeFocusListener(focusListener); |
|
1664 table.removeKeyListener(keyListener); |
|
1665 table.removeMouseListener(mouseInputListener); |
|
1666 table.removeMouseMotionListener(mouseInputListener); |
|
1667 table.removePropertyChangeListener(getHandler()); |
|
1668 if (isFileList) { |
|
1669 table.getSelectionModel().removeListSelectionListener(getHandler()); |
|
1670 } |
|
1671 |
|
1672 focusListener = null; |
|
1673 keyListener = null; |
|
1674 mouseInputListener = null; |
|
1675 handler = null; |
|
1676 } |
|
1677 |
|
1678 /** |
|
1679 * Unregisters keyboard actions. |
|
1680 */ |
|
1681 protected void uninstallKeyboardActions() { |
|
1682 SwingUtilities.replaceUIInputMap(table, JComponent. |
|
1683 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); |
|
1684 SwingUtilities.replaceUIActionMap(table, null); |
|
1685 } |
|
1686 |
|
1687 /** |
|
1688 * Returns the baseline. |
|
1689 * |
|
1690 * @throws NullPointerException {@inheritDoc} |
|
1691 * @throws IllegalArgumentException {@inheritDoc} |
|
1692 * @see javax.swing.JComponent#getBaseline(int, int) |
|
1693 * @since 1.6 |
|
1694 */ |
|
1695 public int getBaseline(JComponent c, int width, int height) { |
|
1696 super.getBaseline(c, width, height); |
|
1697 UIDefaults lafDefaults = UIManager.getLookAndFeelDefaults(); |
|
1698 Component renderer = (Component)lafDefaults.get( |
|
1699 BASELINE_COMPONENT_KEY); |
|
1700 if (renderer == null) { |
|
1701 DefaultTableCellRenderer tcr = new DefaultTableCellRenderer(); |
|
1702 renderer = tcr.getTableCellRendererComponent( |
|
1703 table, "a", false, false, -1, -1); |
|
1704 lafDefaults.put(BASELINE_COMPONENT_KEY, renderer); |
|
1705 } |
|
1706 renderer.setFont(table.getFont()); |
|
1707 int rowMargin = table.getRowMargin(); |
|
1708 return renderer.getBaseline(Integer.MAX_VALUE, table.getRowHeight() - |
|
1709 rowMargin) + rowMargin / 2; |
|
1710 } |
|
1711 |
|
1712 /** |
|
1713 * Returns an enum indicating how the baseline of the component |
|
1714 * changes as the size changes. |
|
1715 * |
|
1716 * @throws NullPointerException {@inheritDoc} |
|
1717 * @see javax.swing.JComponent#getBaseline(int, int) |
|
1718 * @since 1.6 |
|
1719 */ |
|
1720 public Component.BaselineResizeBehavior getBaselineResizeBehavior( |
|
1721 JComponent c) { |
|
1722 super.getBaselineResizeBehavior(c); |
|
1723 return Component.BaselineResizeBehavior.CONSTANT_ASCENT; |
|
1724 } |
|
1725 |
|
1726 // |
|
1727 // Size Methods |
|
1728 // |
|
1729 |
|
1730 private Dimension createTableSize(long width) { |
|
1731 int height = 0; |
|
1732 int rowCount = table.getRowCount(); |
|
1733 if (rowCount > 0 && table.getColumnCount() > 0) { |
|
1734 Rectangle r = table.getCellRect(rowCount-1, 0, true); |
|
1735 height = r.y + r.height; |
|
1736 } |
|
1737 // Width is always positive. The call to abs() is a workaround for |
|
1738 // a bug in the 1.1.6 JIT on Windows. |
|
1739 long tmp = Math.abs(width); |
|
1740 if (tmp > Integer.MAX_VALUE) { |
|
1741 tmp = Integer.MAX_VALUE; |
|
1742 } |
|
1743 return new Dimension((int)tmp, height); |
|
1744 } |
|
1745 |
|
1746 /** |
|
1747 * Return the minimum size of the table. The minimum height is the |
|
1748 * row height times the number of rows. |
|
1749 * The minimum width is the sum of the minimum widths of each column. |
|
1750 */ |
|
1751 public Dimension getMinimumSize(JComponent c) { |
|
1752 long width = 0; |
|
1753 Enumeration<TableColumn> enumeration = table.getColumnModel().getColumns(); |
|
1754 while (enumeration.hasMoreElements()) { |
|
1755 TableColumn aColumn = enumeration.nextElement(); |
|
1756 width = width + aColumn.getMinWidth(); |
|
1757 } |
|
1758 return createTableSize(width); |
|
1759 } |
|
1760 |
|
1761 /** |
|
1762 * Return the preferred size of the table. The preferred height is the |
|
1763 * row height times the number of rows. |
|
1764 * The preferred width is the sum of the preferred widths of each column. |
|
1765 */ |
|
1766 public Dimension getPreferredSize(JComponent c) { |
|
1767 long width = 0; |
|
1768 Enumeration<TableColumn> enumeration = table.getColumnModel().getColumns(); |
|
1769 while (enumeration.hasMoreElements()) { |
|
1770 TableColumn aColumn = enumeration.nextElement(); |
|
1771 width = width + aColumn.getPreferredWidth(); |
|
1772 } |
|
1773 return createTableSize(width); |
|
1774 } |
|
1775 |
|
1776 /** |
|
1777 * Return the maximum size of the table. The maximum height is the |
|
1778 * row heighttimes the number of rows. |
|
1779 * The maximum width is the sum of the maximum widths of each column. |
|
1780 */ |
|
1781 public Dimension getMaximumSize(JComponent c) { |
|
1782 long width = 0; |
|
1783 Enumeration<TableColumn> enumeration = table.getColumnModel().getColumns(); |
|
1784 while (enumeration.hasMoreElements()) { |
|
1785 TableColumn aColumn = enumeration.nextElement(); |
|
1786 width = width + aColumn.getMaxWidth(); |
|
1787 } |
|
1788 return createTableSize(width); |
|
1789 } |
|
1790 |
|
1791 // |
|
1792 // Paint methods and support |
|
1793 // |
|
1794 |
|
1795 /** Paint a representation of the <code>table</code> instance |
|
1796 * that was set in installUI(). |
|
1797 */ |
|
1798 public void paint(Graphics g, JComponent c) { |
|
1799 Rectangle clip = g.getClipBounds(); |
|
1800 |
|
1801 Rectangle bounds = table.getBounds(); |
|
1802 // account for the fact that the graphics has already been translated |
|
1803 // into the table's bounds |
|
1804 bounds.x = bounds.y = 0; |
|
1805 |
|
1806 if (table.getRowCount() <= 0 || table.getColumnCount() <= 0 || |
|
1807 // this check prevents us from painting the entire table |
|
1808 // when the clip doesn't intersect our bounds at all |
|
1809 !bounds.intersects(clip)) { |
|
1810 |
|
1811 paintDropLines(g); |
|
1812 return; |
|
1813 } |
|
1814 |
|
1815 boolean ltr = table.getComponentOrientation().isLeftToRight(); |
|
1816 Point upperLeft, lowerRight; |
|
1817 // compute the visible part of table which needs to be painted |
|
1818 Rectangle visibleBounds = clip.intersection(bounds); |
|
1819 upperLeft = visibleBounds.getLocation(); |
|
1820 lowerRight = new Point(visibleBounds.x + visibleBounds.width - 1, |
|
1821 visibleBounds.y + visibleBounds.height - 1); |
|
1822 |
|
1823 int rMin = table.rowAtPoint(upperLeft); |
|
1824 int rMax = table.rowAtPoint(lowerRight); |
|
1825 // This should never happen (as long as our bounds intersect the clip, |
|
1826 // which is why we bail above if that is the case). |
|
1827 if (rMin == -1) { |
|
1828 rMin = 0; |
|
1829 } |
|
1830 // If the table does not have enough rows to fill the view we'll get -1. |
|
1831 // (We could also get -1 if our bounds don't intersect the clip, |
|
1832 // which is why we bail above if that is the case). |
|
1833 // Replace this with the index of the last row. |
|
1834 if (rMax == -1) { |
|
1835 rMax = table.getRowCount()-1; |
|
1836 } |
|
1837 |
|
1838 // For FIT_WIDTH, all columns should be printed irrespective of |
|
1839 // how many columns are visible. So, we used clip which is already set to |
|
1840 // total col width instead of visible region |
|
1841 // Since JTable.PrintMode is not accessible |
|
1842 // from here, we aet "Table.printMode" in TablePrintable#print and |
|
1843 // access from here. |
|
1844 Object printMode = table.getClientProperty("Table.printMode"); |
|
1845 if ((printMode == JTable.PrintMode.FIT_WIDTH)) { |
|
1846 upperLeft = clip.getLocation(); |
|
1847 lowerRight = new Point(clip.x + clip.width - 1, |
|
1848 clip.y + clip.height - 1); |
|
1849 } |
|
1850 int cMin = table.columnAtPoint(ltr ? upperLeft : lowerRight); |
|
1851 int cMax = table.columnAtPoint(ltr ? lowerRight : upperLeft); |
|
1852 // This should never happen. |
|
1853 if (cMin == -1) { |
|
1854 cMin = 0; |
|
1855 } |
|
1856 // If the table does not have enough columns to fill the view we'll get -1. |
|
1857 // Replace this with the index of the last column. |
|
1858 if (cMax == -1) { |
|
1859 cMax = table.getColumnCount()-1; |
|
1860 } |
|
1861 |
|
1862 Container comp = SwingUtilities.getUnwrappedParent(table); |
|
1863 if (comp != null) { |
|
1864 comp = comp.getParent(); |
|
1865 } |
|
1866 |
|
1867 if (comp != null && !(comp instanceof JViewport) && !(comp instanceof JScrollPane)) { |
|
1868 // We did rMax-1 to paint the same number of rows that are drawn on console |
|
1869 // otherwise 1 extra row is printed per page than that are displayed |
|
1870 // when there is no scrollPane and we do printing of table |
|
1871 // but not when rmax is already pointing to index of last row |
|
1872 // and if there is any selected rows |
|
1873 if (rMax != (table.getRowCount() - 1) && |
|
1874 (table.getSelectedRow() == -1)) { |
|
1875 rMax = rMax - 1; |
|
1876 } |
|
1877 } |
|
1878 |
|
1879 // Paint the grid. |
|
1880 paintGrid(g, rMin, rMax, cMin, cMax); |
|
1881 |
|
1882 // Paint the cells. |
|
1883 paintCells(g, rMin, rMax, cMin, cMax); |
|
1884 |
|
1885 paintDropLines(g); |
|
1886 } |
|
1887 |
|
1888 private void paintDropLines(Graphics g) { |
|
1889 JTable.DropLocation loc = table.getDropLocation(); |
|
1890 if (loc == null) { |
|
1891 return; |
|
1892 } |
|
1893 |
|
1894 Color color = UIManager.getColor("Table.dropLineColor"); |
|
1895 Color shortColor = UIManager.getColor("Table.dropLineShortColor"); |
|
1896 if (color == null && shortColor == null) { |
|
1897 return; |
|
1898 } |
|
1899 |
|
1900 Rectangle rect; |
|
1901 |
|
1902 rect = getHDropLineRect(loc); |
|
1903 if (rect != null) { |
|
1904 int x = rect.x; |
|
1905 int w = rect.width; |
|
1906 if (color != null) { |
|
1907 extendRect(rect, true); |
|
1908 g.setColor(color); |
|
1909 g.fillRect(rect.x, rect.y, rect.width, rect.height); |
|
1910 } |
|
1911 if (!loc.isInsertColumn() && shortColor != null) { |
|
1912 g.setColor(shortColor); |
|
1913 g.fillRect(x, rect.y, w, rect.height); |
|
1914 } |
|
1915 } |
|
1916 |
|
1917 rect = getVDropLineRect(loc); |
|
1918 if (rect != null) { |
|
1919 int y = rect.y; |
|
1920 int h = rect.height; |
|
1921 if (color != null) { |
|
1922 extendRect(rect, false); |
|
1923 g.setColor(color); |
|
1924 g.fillRect(rect.x, rect.y, rect.width, rect.height); |
|
1925 } |
|
1926 if (!loc.isInsertRow() && shortColor != null) { |
|
1927 g.setColor(shortColor); |
|
1928 g.fillRect(rect.x, y, rect.width, h); |
|
1929 } |
|
1930 } |
|
1931 } |
|
1932 |
|
1933 private Rectangle getHDropLineRect(JTable.DropLocation loc) { |
|
1934 if (!loc.isInsertRow()) { |
|
1935 return null; |
|
1936 } |
|
1937 |
|
1938 int row = loc.getRow(); |
|
1939 int col = loc.getColumn(); |
|
1940 if (col >= table.getColumnCount()) { |
|
1941 col--; |
|
1942 } |
|
1943 |
|
1944 Rectangle rect = table.getCellRect(row, col, true); |
|
1945 |
|
1946 if (row >= table.getRowCount()) { |
|
1947 row--; |
|
1948 Rectangle prevRect = table.getCellRect(row, col, true); |
|
1949 rect.y = prevRect.y + prevRect.height; |
|
1950 } |
|
1951 |
|
1952 if (rect.y == 0) { |
|
1953 rect.y = -1; |
|
1954 } else { |
|
1955 rect.y -= 2; |
|
1956 } |
|
1957 |
|
1958 rect.height = 3; |
|
1959 |
|
1960 return rect; |
|
1961 } |
|
1962 |
|
1963 private Rectangle getVDropLineRect(JTable.DropLocation loc) { |
|
1964 if (!loc.isInsertColumn()) { |
|
1965 return null; |
|
1966 } |
|
1967 |
|
1968 boolean ltr = table.getComponentOrientation().isLeftToRight(); |
|
1969 int col = loc.getColumn(); |
|
1970 Rectangle rect = table.getCellRect(loc.getRow(), col, true); |
|
1971 |
|
1972 if (col >= table.getColumnCount()) { |
|
1973 col--; |
|
1974 rect = table.getCellRect(loc.getRow(), col, true); |
|
1975 if (ltr) { |
|
1976 rect.x = rect.x + rect.width; |
|
1977 } |
|
1978 } else if (!ltr) { |
|
1979 rect.x = rect.x + rect.width; |
|
1980 } |
|
1981 |
|
1982 if (rect.x == 0) { |
|
1983 rect.x = -1; |
|
1984 } else { |
|
1985 rect.x -= 2; |
|
1986 } |
|
1987 |
|
1988 rect.width = 3; |
|
1989 |
|
1990 return rect; |
|
1991 } |
|
1992 |
|
1993 private Rectangle extendRect(Rectangle rect, boolean horizontal) { |
|
1994 if (rect == null) { |
|
1995 return rect; |
|
1996 } |
|
1997 |
|
1998 if (horizontal) { |
|
1999 rect.x = 0; |
|
2000 rect.width = table.getWidth(); |
|
2001 } else { |
|
2002 rect.y = 0; |
|
2003 |
|
2004 if (table.getRowCount() != 0) { |
|
2005 Rectangle lastRect = table.getCellRect(table.getRowCount() - 1, 0, true); |
|
2006 rect.height = lastRect.y + lastRect.height; |
|
2007 } else { |
|
2008 rect.height = table.getHeight(); |
|
2009 } |
|
2010 } |
|
2011 |
|
2012 return rect; |
|
2013 } |
|
2014 |
|
2015 /* |
|
2016 * Paints the grid lines within <I>aRect</I>, using the grid |
|
2017 * color set with <I>setGridColor</I>. Paints vertical lines |
|
2018 * if <code>getShowVerticalLines()</code> returns true and paints |
|
2019 * horizontal lines if <code>getShowHorizontalLines()</code> |
|
2020 * returns true. |
|
2021 */ |
|
2022 private void paintGrid(Graphics g, int rMin, int rMax, int cMin, int cMax) { |
|
2023 g.setColor(table.getGridColor()); |
|
2024 |
|
2025 Rectangle minCell = table.getCellRect(rMin, cMin, true); |
|
2026 Rectangle maxCell = table.getCellRect(rMax, cMax, true); |
|
2027 Rectangle damagedArea = minCell.union( maxCell ); |
|
2028 |
|
2029 if (table.getShowHorizontalLines()) { |
|
2030 int tableWidth = damagedArea.x + damagedArea.width; |
|
2031 int y = damagedArea.y; |
|
2032 for (int row = rMin; row <= rMax; row++) { |
|
2033 y += table.getRowHeight(row); |
|
2034 SwingUtilities2.drawHLine(g, damagedArea.x, tableWidth - 1, y - 1); |
|
2035 } |
|
2036 } |
|
2037 if (table.getShowVerticalLines()) { |
|
2038 TableColumnModel cm = table.getColumnModel(); |
|
2039 int tableHeight = damagedArea.y + damagedArea.height; |
|
2040 int x; |
|
2041 if (table.getComponentOrientation().isLeftToRight()) { |
|
2042 x = damagedArea.x; |
|
2043 for (int column = cMin; column <= cMax; column++) { |
|
2044 int w = cm.getColumn(column).getWidth(); |
|
2045 x += w; |
|
2046 SwingUtilities2.drawVLine(g, x - 1, 0, tableHeight - 1); |
|
2047 } |
|
2048 } else { |
|
2049 x = damagedArea.x; |
|
2050 for (int column = cMax; column >= cMin; column--) { |
|
2051 int w = cm.getColumn(column).getWidth(); |
|
2052 x += w; |
|
2053 SwingUtilities2.drawVLine(g, x - 1, 0, tableHeight - 1); |
|
2054 } |
|
2055 } |
|
2056 } |
|
2057 } |
|
2058 |
|
2059 private int viewIndexForColumn(TableColumn aColumn) { |
|
2060 TableColumnModel cm = table.getColumnModel(); |
|
2061 for (int column = 0; column < cm.getColumnCount(); column++) { |
|
2062 if (cm.getColumn(column) == aColumn) { |
|
2063 return column; |
|
2064 } |
|
2065 } |
|
2066 return -1; |
|
2067 } |
|
2068 |
|
2069 private void paintCells(Graphics g, int rMin, int rMax, int cMin, int cMax) { |
|
2070 JTableHeader header = table.getTableHeader(); |
|
2071 TableColumn draggedColumn = (header == null) ? null : header.getDraggedColumn(); |
|
2072 |
|
2073 TableColumnModel cm = table.getColumnModel(); |
|
2074 int columnMargin = cm.getColumnMargin(); |
|
2075 |
|
2076 Rectangle cellRect; |
|
2077 TableColumn aColumn; |
|
2078 int columnWidth; |
|
2079 if (table.getComponentOrientation().isLeftToRight()) { |
|
2080 for(int row = rMin; row <= rMax; row++) { |
|
2081 cellRect = table.getCellRect(row, cMin, false); |
|
2082 for(int column = cMin; column <= cMax; column++) { |
|
2083 aColumn = cm.getColumn(column); |
|
2084 columnWidth = aColumn.getWidth(); |
|
2085 cellRect.width = columnWidth - columnMargin; |
|
2086 if (aColumn != draggedColumn) { |
|
2087 paintCell(g, cellRect, row, column); |
|
2088 } |
|
2089 cellRect.x += columnWidth; |
|
2090 } |
|
2091 } |
|
2092 } else { |
|
2093 for(int row = rMin; row <= rMax; row++) { |
|
2094 cellRect = table.getCellRect(row, cMin, false); |
|
2095 aColumn = cm.getColumn(cMin); |
|
2096 if (aColumn != draggedColumn) { |
|
2097 columnWidth = aColumn.getWidth(); |
|
2098 cellRect.width = columnWidth - columnMargin; |
|
2099 paintCell(g, cellRect, row, cMin); |
|
2100 } |
|
2101 for(int column = cMin+1; column <= cMax; column++) { |
|
2102 aColumn = cm.getColumn(column); |
|
2103 columnWidth = aColumn.getWidth(); |
|
2104 cellRect.width = columnWidth - columnMargin; |
|
2105 cellRect.x -= columnWidth; |
|
2106 if (aColumn != draggedColumn) { |
|
2107 paintCell(g, cellRect, row, column); |
|
2108 } |
|
2109 } |
|
2110 } |
|
2111 } |
|
2112 |
|
2113 // Paint the dragged column if we are dragging. |
|
2114 if (draggedColumn != null) { |
|
2115 paintDraggedArea(g, rMin, rMax, draggedColumn, header.getDraggedDistance()); |
|
2116 } |
|
2117 |
|
2118 // Remove any renderers that may be left in the rendererPane. |
|
2119 rendererPane.removeAll(); |
|
2120 } |
|
2121 |
|
2122 private void paintDraggedArea(Graphics g, int rMin, int rMax, TableColumn draggedColumn, int distance) { |
|
2123 int draggedColumnIndex = viewIndexForColumn(draggedColumn); |
|
2124 |
|
2125 Rectangle minCell = table.getCellRect(rMin, draggedColumnIndex, true); |
|
2126 Rectangle maxCell = table.getCellRect(rMax, draggedColumnIndex, true); |
|
2127 |
|
2128 Rectangle vacatedColumnRect = minCell.union(maxCell); |
|
2129 |
|
2130 // Paint a gray well in place of the moving column. |
|
2131 g.setColor(table.getParent().getBackground()); |
|
2132 g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y, |
|
2133 vacatedColumnRect.width, vacatedColumnRect.height); |
|
2134 |
|
2135 // Move to the where the cell has been dragged. |
|
2136 vacatedColumnRect.x += distance; |
|
2137 |
|
2138 // Fill the background. |
|
2139 g.setColor(table.getBackground()); |
|
2140 g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y, |
|
2141 vacatedColumnRect.width, vacatedColumnRect.height); |
|
2142 |
|
2143 // Paint the vertical grid lines if necessary. |
|
2144 if (table.getShowVerticalLines()) { |
|
2145 g.setColor(table.getGridColor()); |
|
2146 int x1 = vacatedColumnRect.x; |
|
2147 int y1 = vacatedColumnRect.y; |
|
2148 int x2 = x1 + vacatedColumnRect.width - 1; |
|
2149 int y2 = y1 + vacatedColumnRect.height - 1; |
|
2150 // Left |
|
2151 g.drawLine(x1-1, y1, x1-1, y2); |
|
2152 // Right |
|
2153 g.drawLine(x2, y1, x2, y2); |
|
2154 } |
|
2155 |
|
2156 for(int row = rMin; row <= rMax; row++) { |
|
2157 // Render the cell value |
|
2158 Rectangle r = table.getCellRect(row, draggedColumnIndex, false); |
|
2159 r.x += distance; |
|
2160 paintCell(g, r, row, draggedColumnIndex); |
|
2161 |
|
2162 // Paint the (lower) horizontal grid line if necessary. |
|
2163 if (table.getShowHorizontalLines()) { |
|
2164 g.setColor(table.getGridColor()); |
|
2165 Rectangle rcr = table.getCellRect(row, draggedColumnIndex, true); |
|
2166 rcr.x += distance; |
|
2167 int x1 = rcr.x; |
|
2168 int y1 = rcr.y; |
|
2169 int x2 = x1 + rcr.width - 1; |
|
2170 int y2 = y1 + rcr.height - 1; |
|
2171 g.drawLine(x1, y2, x2, y2); |
|
2172 } |
|
2173 } |
|
2174 } |
|
2175 |
|
2176 private void paintCell(Graphics g, Rectangle cellRect, int row, int column) { |
|
2177 if (table.isEditing() && table.getEditingRow()==row && |
|
2178 table.getEditingColumn()==column) { |
|
2179 Component component = table.getEditorComponent(); |
|
2180 component.setBounds(cellRect); |
|
2181 component.validate(); |
|
2182 } |
|
2183 else { |
|
2184 TableCellRenderer renderer = table.getCellRenderer(row, column); |
|
2185 Component component = table.prepareRenderer(renderer, row, column); |
|
2186 rendererPane.paintComponent(g, component, table, cellRect.x, cellRect.y, |
|
2187 cellRect.width, cellRect.height, true); |
|
2188 } |
|
2189 } |
|
2190 |
|
2191 private static int getAdjustedLead(JTable table, |
|
2192 boolean row, |
|
2193 ListSelectionModel model) { |
|
2194 |
|
2195 int index = model.getLeadSelectionIndex(); |
|
2196 int compare = row ? table.getRowCount() : table.getColumnCount(); |
|
2197 return index < compare ? index : -1; |
|
2198 } |
|
2199 |
|
2200 private static int getAdjustedLead(JTable table, boolean row) { |
|
2201 return row ? getAdjustedLead(table, row, table.getSelectionModel()) |
|
2202 : getAdjustedLead(table, row, table.getColumnModel().getSelectionModel()); |
|
2203 } |
|
2204 |
|
2205 |
|
2206 private static final TransferHandler defaultTransferHandler = new TableTransferHandler(); |
|
2207 |
|
2208 @SuppressWarnings("serial") // JDK-implementation class |
|
2209 static class TableTransferHandler extends TransferHandler implements UIResource { |
|
2210 |
|
2211 /** |
|
2212 * Create a Transferable to use as the source for a data transfer. |
|
2213 * |
|
2214 * @param c The component holding the data to be transfered. This |
|
2215 * argument is provided to enable sharing of TransferHandlers by |
|
2216 * multiple components. |
|
2217 * @return The representation of the data to be transfered. |
|
2218 * |
|
2219 */ |
|
2220 protected Transferable createTransferable(JComponent c) { |
|
2221 if (c instanceof JTable) { |
|
2222 JTable table = (JTable) c; |
|
2223 int[] rows; |
|
2224 int[] cols; |
|
2225 |
|
2226 if (!table.getRowSelectionAllowed() && !table.getColumnSelectionAllowed()) { |
|
2227 return null; |
|
2228 } |
|
2229 |
|
2230 if (!table.getRowSelectionAllowed()) { |
|
2231 int rowCount = table.getRowCount(); |
|
2232 |
|
2233 rows = new int[rowCount]; |
|
2234 for (int counter = 0; counter < rowCount; counter++) { |
|
2235 rows[counter] = counter; |
|
2236 } |
|
2237 } else { |
|
2238 rows = table.getSelectedRows(); |
|
2239 } |
|
2240 |
|
2241 if (!table.getColumnSelectionAllowed()) { |
|
2242 int colCount = table.getColumnCount(); |
|
2243 |
|
2244 cols = new int[colCount]; |
|
2245 for (int counter = 0; counter < colCount; counter++) { |
|
2246 cols[counter] = counter; |
|
2247 } |
|
2248 } else { |
|
2249 cols = table.getSelectedColumns(); |
|
2250 } |
|
2251 |
|
2252 if (rows == null || cols == null || rows.length == 0 || cols.length == 0) { |
|
2253 return null; |
|
2254 } |
|
2255 |
|
2256 StringBuilder plainStr = new StringBuilder(); |
|
2257 StringBuilder htmlStr = new StringBuilder(); |
|
2258 |
|
2259 htmlStr.append("<html>\n<body>\n<table>\n"); |
|
2260 |
|
2261 for (int row = 0; row < rows.length; row++) { |
|
2262 htmlStr.append("<tr>\n"); |
|
2263 for (int col = 0; col < cols.length; col++) { |
|
2264 Object obj = table.getValueAt(rows[row], cols[col]); |
|
2265 String val = ((obj == null) ? "" : obj.toString()); |
|
2266 plainStr.append(val).append('\t'); |
|
2267 htmlStr.append(" <td>").append(val).append("</td>\n"); |
|
2268 } |
|
2269 // we want a newline at the end of each line and not a tab |
|
2270 plainStr.deleteCharAt(plainStr.length() - 1).append('\n'); |
|
2271 htmlStr.append("</tr>\n"); |
|
2272 } |
|
2273 |
|
2274 // remove the last newline |
|
2275 plainStr.deleteCharAt(plainStr.length() - 1); |
|
2276 htmlStr.append("</table>\n</body>\n</html>"); |
|
2277 |
|
2278 return new BasicTransferable(plainStr.toString(), htmlStr.toString()); |
|
2279 } |
|
2280 |
|
2281 return null; |
|
2282 } |
|
2283 |
|
2284 public int getSourceActions(JComponent c) { |
|
2285 return COPY; |
|
2286 } |
|
2287 |
|
2288 } |
|
2289 } // End of Class BasicTableUI |