1 |
772 |
jeremybenn |
/* BasicTableUI.java --
|
2 |
|
|
Copyright (C) 2004 Free Software Foundation, Inc.
|
3 |
|
|
|
4 |
|
|
This file is part of GNU Classpath.
|
5 |
|
|
|
6 |
|
|
GNU Classpath is free software; you can redistribute it and/or modify
|
7 |
|
|
it under the terms of the GNU General Public License as published by
|
8 |
|
|
the Free Software Foundation; either version 2, or (at your option)
|
9 |
|
|
any later version.
|
10 |
|
|
|
11 |
|
|
GNU Classpath is distributed in the hope that it will be useful, but
|
12 |
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 |
|
|
General Public License for more details.
|
15 |
|
|
|
16 |
|
|
You should have received a copy of the GNU General Public License
|
17 |
|
|
along with GNU Classpath; see the file COPYING. If not, write to the
|
18 |
|
|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 |
|
|
02110-1301 USA.
|
20 |
|
|
|
21 |
|
|
Linking this library statically or dynamically with other modules is
|
22 |
|
|
making a combined work based on this library. Thus, the terms and
|
23 |
|
|
conditions of the GNU General Public License cover the whole
|
24 |
|
|
combination.
|
25 |
|
|
|
26 |
|
|
As a special exception, the copyright holders of this library give you
|
27 |
|
|
permission to link this library with independent modules to produce an
|
28 |
|
|
executable, regardless of the license terms of these independent
|
29 |
|
|
modules, and to copy and distribute the resulting executable under
|
30 |
|
|
terms of your choice, provided that you also meet, for each linked
|
31 |
|
|
independent module, the terms and conditions of the license of that
|
32 |
|
|
module. An independent module is a module which is not derived from
|
33 |
|
|
or based on this library. If you modify this library, you may extend
|
34 |
|
|
this exception to your version of the library, but you are not
|
35 |
|
|
obligated to do so. If you do not wish to do so, delete this
|
36 |
|
|
exception statement from your version. */
|
37 |
|
|
|
38 |
|
|
|
39 |
|
|
package javax.swing.plaf.basic;
|
40 |
|
|
|
41 |
|
|
import java.awt.Color;
|
42 |
|
|
import java.awt.Component;
|
43 |
|
|
import java.awt.ComponentOrientation;
|
44 |
|
|
import java.awt.Dimension;
|
45 |
|
|
import java.awt.Graphics;
|
46 |
|
|
import java.awt.Point;
|
47 |
|
|
import java.awt.Rectangle;
|
48 |
|
|
import java.awt.event.ActionEvent;
|
49 |
|
|
import java.awt.event.FocusEvent;
|
50 |
|
|
import java.awt.event.FocusListener;
|
51 |
|
|
import java.awt.event.KeyEvent;
|
52 |
|
|
import java.awt.event.KeyListener;
|
53 |
|
|
import java.awt.event.MouseEvent;
|
54 |
|
|
import java.beans.PropertyChangeEvent;
|
55 |
|
|
import java.beans.PropertyChangeListener;
|
56 |
|
|
|
57 |
|
|
import javax.swing.AbstractAction;
|
58 |
|
|
import javax.swing.Action;
|
59 |
|
|
import javax.swing.ActionMap;
|
60 |
|
|
import javax.swing.CellRendererPane;
|
61 |
|
|
import javax.swing.DefaultCellEditor;
|
62 |
|
|
import javax.swing.DefaultListSelectionModel;
|
63 |
|
|
import javax.swing.InputMap;
|
64 |
|
|
import javax.swing.JComponent;
|
65 |
|
|
import javax.swing.JTable;
|
66 |
|
|
import javax.swing.ListSelectionModel;
|
67 |
|
|
import javax.swing.LookAndFeel;
|
68 |
|
|
import javax.swing.SwingUtilities;
|
69 |
|
|
import javax.swing.TransferHandler;
|
70 |
|
|
import javax.swing.UIManager;
|
71 |
|
|
import javax.swing.border.Border;
|
72 |
|
|
import javax.swing.event.ChangeEvent;
|
73 |
|
|
import javax.swing.event.MouseInputListener;
|
74 |
|
|
import javax.swing.plaf.ActionMapUIResource;
|
75 |
|
|
import javax.swing.plaf.ComponentUI;
|
76 |
|
|
import javax.swing.plaf.TableUI;
|
77 |
|
|
import javax.swing.table.TableCellEditor;
|
78 |
|
|
import javax.swing.table.TableCellRenderer;
|
79 |
|
|
import javax.swing.table.TableColumn;
|
80 |
|
|
import javax.swing.table.TableColumnModel;
|
81 |
|
|
import javax.swing.table.TableModel;
|
82 |
|
|
|
83 |
|
|
public class BasicTableUI extends TableUI
|
84 |
|
|
{
|
85 |
|
|
public static ComponentUI createUI(JComponent comp)
|
86 |
|
|
{
|
87 |
|
|
return new BasicTableUI();
|
88 |
|
|
}
|
89 |
|
|
|
90 |
|
|
protected FocusListener focusListener;
|
91 |
|
|
protected KeyListener keyListener;
|
92 |
|
|
protected MouseInputListener mouseInputListener;
|
93 |
|
|
protected CellRendererPane rendererPane;
|
94 |
|
|
protected JTable table;
|
95 |
|
|
|
96 |
|
|
/** The normal cell border. */
|
97 |
|
|
Border cellBorder;
|
98 |
|
|
|
99 |
|
|
/** The action bound to KeyStrokes. */
|
100 |
|
|
TableAction action;
|
101 |
|
|
|
102 |
|
|
/**
|
103 |
|
|
* Listens for changes to the tables properties.
|
104 |
|
|
*/
|
105 |
|
|
private PropertyChangeListener propertyChangeListener;
|
106 |
|
|
|
107 |
|
|
/**
|
108 |
|
|
* Handles key events for the JTable. Key events should be handled through
|
109 |
|
|
* the InputMap/ActionMap mechanism since JDK1.3. This class is only there
|
110 |
|
|
* for backwards compatibility.
|
111 |
|
|
*
|
112 |
|
|
* @author Roman Kennke (kennke@aicas.com)
|
113 |
|
|
*/
|
114 |
|
|
public class KeyHandler implements KeyListener
|
115 |
|
|
{
|
116 |
|
|
|
117 |
|
|
/**
|
118 |
|
|
* Receives notification that a key has been pressed and released.
|
119 |
|
|
* Activates the editing session for the focused cell by pressing the
|
120 |
|
|
* character keys.
|
121 |
|
|
*
|
122 |
|
|
* @param event the key event
|
123 |
|
|
*/
|
124 |
|
|
public void keyTyped(KeyEvent event)
|
125 |
|
|
{
|
126 |
|
|
// Key events should be handled through the InputMap/ActionMap mechanism
|
127 |
|
|
// since JDK1.3. This class is only there for backwards compatibility.
|
128 |
|
|
|
129 |
|
|
// Editor activation is a specific kind of response to ''any''
|
130 |
|
|
// character key. Hence it is handled here.
|
131 |
|
|
if (!table.isEditing() && table.isEnabled())
|
132 |
|
|
{
|
133 |
|
|
int r = table.getSelectedRow();
|
134 |
|
|
int c = table.getSelectedColumn();
|
135 |
|
|
if (table.isCellEditable(r, c))
|
136 |
|
|
table.editCellAt(r, c);
|
137 |
|
|
}
|
138 |
|
|
}
|
139 |
|
|
|
140 |
|
|
/**
|
141 |
|
|
* Receives notification that a key has been pressed.
|
142 |
|
|
*
|
143 |
|
|
* @param event the key event
|
144 |
|
|
*/
|
145 |
|
|
public void keyPressed(KeyEvent event)
|
146 |
|
|
{
|
147 |
|
|
// Key events should be handled through the InputMap/ActionMap mechanism
|
148 |
|
|
// since JDK1.3. This class is only there for backwards compatibility.
|
149 |
|
|
}
|
150 |
|
|
|
151 |
|
|
/**
|
152 |
|
|
* Receives notification that a key has been released.
|
153 |
|
|
*
|
154 |
|
|
* @param event the key event
|
155 |
|
|
*/
|
156 |
|
|
public void keyReleased(KeyEvent event)
|
157 |
|
|
{
|
158 |
|
|
// Key events should be handled through the InputMap/ActionMap mechanism
|
159 |
|
|
// since JDK1.3. This class is only there for backwards compatibility.
|
160 |
|
|
}
|
161 |
|
|
}
|
162 |
|
|
|
163 |
|
|
public class FocusHandler implements FocusListener
|
164 |
|
|
{
|
165 |
|
|
public void focusGained(FocusEvent e)
|
166 |
|
|
{
|
167 |
|
|
// The only thing that is affected by a focus change seems to be
|
168 |
|
|
// how the lead cell is painted. So we repaint this cell.
|
169 |
|
|
repaintLeadCell();
|
170 |
|
|
}
|
171 |
|
|
|
172 |
|
|
public void focusLost(FocusEvent e)
|
173 |
|
|
{
|
174 |
|
|
// The only thing that is affected by a focus change seems to be
|
175 |
|
|
// how the lead cell is painted. So we repaint this cell.
|
176 |
|
|
repaintLeadCell();
|
177 |
|
|
}
|
178 |
|
|
|
179 |
|
|
/**
|
180 |
|
|
* Repaints the lead cell in response to a focus change, to refresh
|
181 |
|
|
* the display of the focus indicator.
|
182 |
|
|
*/
|
183 |
|
|
private void repaintLeadCell()
|
184 |
|
|
{
|
185 |
|
|
int rowCount = table.getRowCount();
|
186 |
|
|
int columnCount = table.getColumnCount();
|
187 |
|
|
int rowLead = table.getSelectionModel().getLeadSelectionIndex();
|
188 |
|
|
int columnLead = table.getColumnModel().getSelectionModel().
|
189 |
|
|
getLeadSelectionIndex();
|
190 |
|
|
if (rowLead >= 0 && rowLead < rowCount && columnLead >= 0
|
191 |
|
|
&& columnLead < columnCount)
|
192 |
|
|
{
|
193 |
|
|
Rectangle dirtyRect = table.getCellRect(rowLead, columnLead, false);
|
194 |
|
|
table.repaint(dirtyRect);
|
195 |
|
|
}
|
196 |
|
|
}
|
197 |
|
|
}
|
198 |
|
|
|
199 |
|
|
public class MouseInputHandler implements MouseInputListener
|
200 |
|
|
{
|
201 |
|
|
Point begin, curr;
|
202 |
|
|
|
203 |
|
|
private void updateSelection(boolean controlPressed)
|
204 |
|
|
{
|
205 |
|
|
// Update the rows
|
206 |
|
|
int lo_row = table.rowAtPoint(begin);
|
207 |
|
|
int hi_row = table.rowAtPoint(curr);
|
208 |
|
|
ListSelectionModel rowModel = table.getSelectionModel();
|
209 |
|
|
if (lo_row != -1 && hi_row != -1)
|
210 |
|
|
{
|
211 |
|
|
if (controlPressed && rowModel.getSelectionMode()
|
212 |
|
|
!= ListSelectionModel.SINGLE_SELECTION)
|
213 |
|
|
rowModel.addSelectionInterval(lo_row, hi_row);
|
214 |
|
|
else
|
215 |
|
|
rowModel.setSelectionInterval(lo_row, hi_row);
|
216 |
|
|
}
|
217 |
|
|
|
218 |
|
|
// Update the columns
|
219 |
|
|
int lo_col = table.columnAtPoint(begin);
|
220 |
|
|
int hi_col = table.columnAtPoint(curr);
|
221 |
|
|
ListSelectionModel colModel = table.getColumnModel().
|
222 |
|
|
getSelectionModel();
|
223 |
|
|
if (lo_col != -1 && hi_col != -1)
|
224 |
|
|
{
|
225 |
|
|
if (controlPressed && colModel.getSelectionMode() !=
|
226 |
|
|
ListSelectionModel.SINGLE_SELECTION)
|
227 |
|
|
colModel.addSelectionInterval(lo_col, hi_col);
|
228 |
|
|
else
|
229 |
|
|
colModel.setSelectionInterval(lo_col, hi_col);
|
230 |
|
|
}
|
231 |
|
|
}
|
232 |
|
|
|
233 |
|
|
/**
|
234 |
|
|
* For the double click, start the cell editor.
|
235 |
|
|
*/
|
236 |
|
|
public void mouseClicked(MouseEvent e)
|
237 |
|
|
{
|
238 |
|
|
Point p = e.getPoint();
|
239 |
|
|
int row = table.rowAtPoint(p);
|
240 |
|
|
int col = table.columnAtPoint(p);
|
241 |
|
|
if (table.isCellEditable(row, col))
|
242 |
|
|
{
|
243 |
|
|
// If the cell editor is the default editor, we request the
|
244 |
|
|
// number of the required clicks from it. Otherwise,
|
245 |
|
|
// require two clicks (double click).
|
246 |
|
|
TableCellEditor editor = table.getCellEditor(row, col);
|
247 |
|
|
if (editor instanceof DefaultCellEditor)
|
248 |
|
|
{
|
249 |
|
|
DefaultCellEditor ce = (DefaultCellEditor) editor;
|
250 |
|
|
if (e.getClickCount() < ce.getClickCountToStart())
|
251 |
|
|
return;
|
252 |
|
|
}
|
253 |
|
|
table.editCellAt(row, col);
|
254 |
|
|
}
|
255 |
|
|
}
|
256 |
|
|
|
257 |
|
|
public void mouseDragged(MouseEvent e)
|
258 |
|
|
{
|
259 |
|
|
if (table.isEnabled())
|
260 |
|
|
{
|
261 |
|
|
curr = new Point(e.getX(), e.getY());
|
262 |
|
|
updateSelection(e.isControlDown());
|
263 |
|
|
}
|
264 |
|
|
}
|
265 |
|
|
|
266 |
|
|
public void mouseEntered(MouseEvent e)
|
267 |
|
|
{
|
268 |
|
|
// Nothing to do here.
|
269 |
|
|
}
|
270 |
|
|
|
271 |
|
|
public void mouseExited(MouseEvent e)
|
272 |
|
|
{
|
273 |
|
|
// Nothing to do here.
|
274 |
|
|
}
|
275 |
|
|
|
276 |
|
|
public void mouseMoved(MouseEvent e)
|
277 |
|
|
{
|
278 |
|
|
// Nothing to do here.
|
279 |
|
|
}
|
280 |
|
|
|
281 |
|
|
public void mousePressed(MouseEvent e)
|
282 |
|
|
{
|
283 |
|
|
if (table.isEnabled())
|
284 |
|
|
{
|
285 |
|
|
ListSelectionModel rowModel = table.getSelectionModel();
|
286 |
|
|
ListSelectionModel colModel = table.getColumnModel().getSelectionModel();
|
287 |
|
|
int rowLead = rowModel.getLeadSelectionIndex();
|
288 |
|
|
int colLead = colModel.getLeadSelectionIndex();
|
289 |
|
|
|
290 |
|
|
begin = new Point(e.getX(), e.getY());
|
291 |
|
|
curr = new Point(e.getX(), e.getY());
|
292 |
|
|
//if control is pressed and the cell is already selected, deselect it
|
293 |
|
|
if (e.isControlDown() && table.isCellSelected(
|
294 |
|
|
table.rowAtPoint(begin), table.columnAtPoint(begin)))
|
295 |
|
|
{
|
296 |
|
|
table.getSelectionModel().
|
297 |
|
|
removeSelectionInterval(table.rowAtPoint(begin),
|
298 |
|
|
table.rowAtPoint(begin));
|
299 |
|
|
table.getColumnModel().getSelectionModel().
|
300 |
|
|
removeSelectionInterval(table.columnAtPoint(begin),
|
301 |
|
|
table.columnAtPoint(begin));
|
302 |
|
|
}
|
303 |
|
|
else
|
304 |
|
|
updateSelection(e.isControlDown());
|
305 |
|
|
|
306 |
|
|
// If we were editing, but the moved to another cell, stop editing
|
307 |
|
|
if (rowLead != rowModel.getLeadSelectionIndex() ||
|
308 |
|
|
colLead != colModel.getLeadSelectionIndex())
|
309 |
|
|
if (table.isEditing())
|
310 |
|
|
table.editingStopped(new ChangeEvent(e));
|
311 |
|
|
|
312 |
|
|
// Must request focus explicitly.
|
313 |
|
|
table.requestFocusInWindow();
|
314 |
|
|
}
|
315 |
|
|
}
|
316 |
|
|
|
317 |
|
|
public void mouseReleased(MouseEvent e)
|
318 |
|
|
{
|
319 |
|
|
if (table.isEnabled())
|
320 |
|
|
{
|
321 |
|
|
begin = null;
|
322 |
|
|
curr = null;
|
323 |
|
|
}
|
324 |
|
|
}
|
325 |
|
|
}
|
326 |
|
|
|
327 |
|
|
/**
|
328 |
|
|
* Listens for changes to the model property of the JTable and adjusts some
|
329 |
|
|
* settings.
|
330 |
|
|
*
|
331 |
|
|
* @author Roman Kennke (kennke@aicas.com)
|
332 |
|
|
*/
|
333 |
|
|
private class PropertyChangeHandler implements PropertyChangeListener
|
334 |
|
|
{
|
335 |
|
|
/**
|
336 |
|
|
* Receives notification if one of the JTable's properties changes.
|
337 |
|
|
*
|
338 |
|
|
* @param ev the property change event
|
339 |
|
|
*/
|
340 |
|
|
public void propertyChange(PropertyChangeEvent ev)
|
341 |
|
|
{
|
342 |
|
|
String propName = ev.getPropertyName();
|
343 |
|
|
if (propName.equals("model"))
|
344 |
|
|
{
|
345 |
|
|
ListSelectionModel rowSel = table.getSelectionModel();
|
346 |
|
|
rowSel.clearSelection();
|
347 |
|
|
ListSelectionModel colSel = table.getColumnModel().getSelectionModel();
|
348 |
|
|
colSel.clearSelection();
|
349 |
|
|
TableModel model = table.getModel();
|
350 |
|
|
|
351 |
|
|
// Adjust lead and anchor selection indices of the row and column
|
352 |
|
|
// selection models.
|
353 |
|
|
if (model.getRowCount() > 0)
|
354 |
|
|
{
|
355 |
|
|
rowSel.setAnchorSelectionIndex(0);
|
356 |
|
|
rowSel.setLeadSelectionIndex(0);
|
357 |
|
|
}
|
358 |
|
|
else
|
359 |
|
|
{
|
360 |
|
|
rowSel.setAnchorSelectionIndex(-1);
|
361 |
|
|
rowSel.setLeadSelectionIndex(-1);
|
362 |
|
|
}
|
363 |
|
|
if (model.getColumnCount() > 0)
|
364 |
|
|
{
|
365 |
|
|
colSel.setAnchorSelectionIndex(0);
|
366 |
|
|
colSel.setLeadSelectionIndex(0);
|
367 |
|
|
}
|
368 |
|
|
else
|
369 |
|
|
{
|
370 |
|
|
colSel.setAnchorSelectionIndex(-1);
|
371 |
|
|
colSel.setLeadSelectionIndex(-1);
|
372 |
|
|
}
|
373 |
|
|
}
|
374 |
|
|
}
|
375 |
|
|
}
|
376 |
|
|
|
377 |
|
|
protected FocusListener createFocusListener()
|
378 |
|
|
{
|
379 |
|
|
return new FocusHandler();
|
380 |
|
|
}
|
381 |
|
|
|
382 |
|
|
protected MouseInputListener createMouseInputListener()
|
383 |
|
|
{
|
384 |
|
|
return new MouseInputHandler();
|
385 |
|
|
}
|
386 |
|
|
|
387 |
|
|
|
388 |
|
|
/**
|
389 |
|
|
* Creates and returns a key listener for the JTable.
|
390 |
|
|
*
|
391 |
|
|
* @return a key listener for the JTable
|
392 |
|
|
*/
|
393 |
|
|
protected KeyListener createKeyListener()
|
394 |
|
|
{
|
395 |
|
|
return new KeyHandler();
|
396 |
|
|
}
|
397 |
|
|
|
398 |
|
|
/**
|
399 |
|
|
* Return the maximum size of the table. The maximum height is the row
|
400 |
|
|
* height times the number of rows. The maximum width is the sum of
|
401 |
|
|
* the maximum widths of each column.
|
402 |
|
|
*
|
403 |
|
|
* @param comp the component whose maximum size is being queried,
|
404 |
|
|
* this is ignored.
|
405 |
|
|
* @return a Dimension object representing the maximum size of the table,
|
406 |
|
|
* or null if the table has no elements.
|
407 |
|
|
*/
|
408 |
|
|
public Dimension getMaximumSize(JComponent comp)
|
409 |
|
|
{
|
410 |
|
|
int maxTotalColumnWidth = 0;
|
411 |
|
|
for (int i = 0; i < table.getColumnCount(); i++)
|
412 |
|
|
maxTotalColumnWidth += table.getColumnModel().getColumn(i).getMaxWidth();
|
413 |
|
|
|
414 |
|
|
return new Dimension(maxTotalColumnWidth, getHeight());
|
415 |
|
|
}
|
416 |
|
|
|
417 |
|
|
/**
|
418 |
|
|
* Return the minimum size of the table. The minimum height is the row
|
419 |
|
|
* height times the number of rows. The minimum width is the sum of
|
420 |
|
|
* the minimum widths of each column.
|
421 |
|
|
*
|
422 |
|
|
* @param comp the component whose minimum size is being queried,
|
423 |
|
|
* this is ignored.
|
424 |
|
|
* @return a Dimension object representing the minimum size of the table,
|
425 |
|
|
* or null if the table has no elements.
|
426 |
|
|
*/
|
427 |
|
|
public Dimension getMinimumSize(JComponent comp)
|
428 |
|
|
{
|
429 |
|
|
int minTotalColumnWidth = 0;
|
430 |
|
|
for (int i = 0; i < table.getColumnCount(); i++)
|
431 |
|
|
minTotalColumnWidth += table.getColumnModel().getColumn(i).getMinWidth();
|
432 |
|
|
|
433 |
|
|
return new Dimension(minTotalColumnWidth, getHeight());
|
434 |
|
|
}
|
435 |
|
|
|
436 |
|
|
/**
|
437 |
|
|
* Returns the preferred size for the table of that UI.
|
438 |
|
|
*
|
439 |
|
|
* @param comp ignored, the <code>table</code> field is used instead
|
440 |
|
|
*
|
441 |
|
|
* @return the preferred size for the table of that UI
|
442 |
|
|
*/
|
443 |
|
|
public Dimension getPreferredSize(JComponent comp)
|
444 |
|
|
{
|
445 |
|
|
int prefTotalColumnWidth = 0;
|
446 |
|
|
TableColumnModel tcm = table.getColumnModel();
|
447 |
|
|
|
448 |
|
|
for (int i = 0; i < tcm.getColumnCount(); i++)
|
449 |
|
|
{
|
450 |
|
|
TableColumn col = tcm.getColumn(i);
|
451 |
|
|
prefTotalColumnWidth += col.getPreferredWidth();
|
452 |
|
|
}
|
453 |
|
|
|
454 |
|
|
return new Dimension(prefTotalColumnWidth, getHeight());
|
455 |
|
|
}
|
456 |
|
|
|
457 |
|
|
/**
|
458 |
|
|
* Returns the table height. This helper method is used by
|
459 |
|
|
* {@link #getMinimumSize(JComponent)}, {@link #getPreferredSize(JComponent)}
|
460 |
|
|
* and {@link #getMaximumSize(JComponent)} to determine the table height.
|
461 |
|
|
*
|
462 |
|
|
* @return the table height
|
463 |
|
|
*/
|
464 |
|
|
private int getHeight()
|
465 |
|
|
{
|
466 |
|
|
int height = 0;
|
467 |
|
|
int rowCount = table.getRowCount();
|
468 |
|
|
if (rowCount > 0 && table.getColumnCount() > 0)
|
469 |
|
|
{
|
470 |
|
|
Rectangle r = table.getCellRect(rowCount - 1, 0, true);
|
471 |
|
|
height = r.y + r.height;
|
472 |
|
|
}
|
473 |
|
|
return height;
|
474 |
|
|
}
|
475 |
|
|
|
476 |
|
|
protected void installDefaults()
|
477 |
|
|
{
|
478 |
|
|
LookAndFeel.installColorsAndFont(table, "Table.background",
|
479 |
|
|
"Table.foreground", "Table.font");
|
480 |
|
|
table.setGridColor(UIManager.getColor("Table.gridColor"));
|
481 |
|
|
table.setSelectionForeground(UIManager.getColor("Table.selectionForeground"));
|
482 |
|
|
table.setSelectionBackground(UIManager.getColor("Table.selectionBackground"));
|
483 |
|
|
table.setOpaque(true);
|
484 |
|
|
}
|
485 |
|
|
|
486 |
|
|
/**
|
487 |
|
|
* Installs keyboard actions on the table.
|
488 |
|
|
*/
|
489 |
|
|
protected void installKeyboardActions()
|
490 |
|
|
{
|
491 |
|
|
// Install the input map.
|
492 |
|
|
InputMap inputMap =
|
493 |
|
|
(InputMap) SharedUIDefaults.get("Table.ancestorInputMap");
|
494 |
|
|
SwingUtilities.replaceUIInputMap(table,
|
495 |
|
|
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
|
496 |
|
|
inputMap);
|
497 |
|
|
|
498 |
|
|
// FIXME: The JDK uses a LazyActionMap for parentActionMap
|
499 |
|
|
SwingUtilities.replaceUIActionMap(table, getActionMap());
|
500 |
|
|
|
501 |
|
|
}
|
502 |
|
|
|
503 |
|
|
/**
|
504 |
|
|
* Fetches the action map from the UI defaults, or create a new one
|
505 |
|
|
* if the action map hasn't been initialized.
|
506 |
|
|
*
|
507 |
|
|
* @return the action map
|
508 |
|
|
*/
|
509 |
|
|
private ActionMap getActionMap()
|
510 |
|
|
{
|
511 |
|
|
ActionMap am = (ActionMap) UIManager.get("Table.actionMap");
|
512 |
|
|
if (am == null)
|
513 |
|
|
{
|
514 |
|
|
am = createDefaultActions();
|
515 |
|
|
UIManager.getLookAndFeelDefaults().put("Table.actionMap", am);
|
516 |
|
|
}
|
517 |
|
|
return am;
|
518 |
|
|
}
|
519 |
|
|
|
520 |
|
|
private ActionMap createDefaultActions()
|
521 |
|
|
{
|
522 |
|
|
ActionMapUIResource am = new ActionMapUIResource();
|
523 |
|
|
Action action = new TableAction();
|
524 |
|
|
|
525 |
|
|
am.put("cut", TransferHandler.getCutAction());
|
526 |
|
|
am.put("copy", TransferHandler.getCopyAction());
|
527 |
|
|
am.put("paste", TransferHandler.getPasteAction());
|
528 |
|
|
|
529 |
|
|
am.put("cancel", action);
|
530 |
|
|
am.put("selectAll", action);
|
531 |
|
|
am.put("clearSelection", action);
|
532 |
|
|
am.put("startEditing", action);
|
533 |
|
|
|
534 |
|
|
am.put("selectNextRow", action);
|
535 |
|
|
am.put("selectNextRowCell", action);
|
536 |
|
|
am.put("selectNextRowExtendSelection", action);
|
537 |
|
|
am.put("selectNextRowChangeLead", action);
|
538 |
|
|
|
539 |
|
|
am.put("selectPreviousRow", action);
|
540 |
|
|
am.put("selectPreviousRowCell", action);
|
541 |
|
|
am.put("selectPreviousRowExtendSelection", action);
|
542 |
|
|
am.put("selectPreviousRowChangeLead", action);
|
543 |
|
|
|
544 |
|
|
am.put("selectNextColumn", action);
|
545 |
|
|
am.put("selectNextColumnCell", action);
|
546 |
|
|
am.put("selectNextColumnExtendSelection", action);
|
547 |
|
|
am.put("selectNextColumnChangeLead", action);
|
548 |
|
|
|
549 |
|
|
am.put("selectPreviousColumn", action);
|
550 |
|
|
am.put("selectPreviousColumnCell", action);
|
551 |
|
|
am.put("selectPreviousColumnExtendSelection", action);
|
552 |
|
|
am.put("selectPreviousColumnChangeLead", action);
|
553 |
|
|
|
554 |
|
|
am.put("scrollLeftChangeSelection", action);
|
555 |
|
|
am.put("scrollLeftExtendSelection", action);
|
556 |
|
|
am.put("scrollRightChangeSelection", action);
|
557 |
|
|
am.put("scrollRightExtendSelection", action);
|
558 |
|
|
|
559 |
|
|
am.put("scrollUpChangeSelection", action);
|
560 |
|
|
am.put("scrollUpExtendSelection", action);
|
561 |
|
|
am.put("scrollDownChangeSelection", action);
|
562 |
|
|
am.put("scrolldownExtendSelection", action);
|
563 |
|
|
|
564 |
|
|
am.put("selectFirstColumn", action);
|
565 |
|
|
am.put("selectFirstColumnExtendSelection", action);
|
566 |
|
|
am.put("selectLastColumn", action);
|
567 |
|
|
am.put("selectLastColumnExtendSelection", action);
|
568 |
|
|
|
569 |
|
|
am.put("selectFirstRow", action);
|
570 |
|
|
am.put("selectFirstRowExtendSelection", action);
|
571 |
|
|
am.put("selectLastRow", action);
|
572 |
|
|
am.put("selectLastRowExtendSelection", action);
|
573 |
|
|
|
574 |
|
|
am.put("addToSelection", action);
|
575 |
|
|
am.put("toggleAndAnchor", action);
|
576 |
|
|
am.put("extendTo", action);
|
577 |
|
|
am.put("moveSelectionTo", action);
|
578 |
|
|
|
579 |
|
|
return am;
|
580 |
|
|
}
|
581 |
|
|
|
582 |
|
|
/**
|
583 |
|
|
* This class implements the actions that we want to happen
|
584 |
|
|
* when specific keys are pressed for the JTable. The actionPerformed
|
585 |
|
|
* method is called when a key that has been registered for the JTable
|
586 |
|
|
* is received.
|
587 |
|
|
*/
|
588 |
|
|
private static class TableAction
|
589 |
|
|
extends AbstractAction
|
590 |
|
|
{
|
591 |
|
|
/**
|
592 |
|
|
* What to do when this action is called.
|
593 |
|
|
*
|
594 |
|
|
* @param e the ActionEvent that caused this action.
|
595 |
|
|
*/
|
596 |
|
|
public void actionPerformed(ActionEvent e)
|
597 |
|
|
{
|
598 |
|
|
JTable table = (JTable) e.getSource();
|
599 |
|
|
|
600 |
|
|
DefaultListSelectionModel rowModel
|
601 |
|
|
= (DefaultListSelectionModel) table.getSelectionModel();
|
602 |
|
|
DefaultListSelectionModel colModel
|
603 |
|
|
= (DefaultListSelectionModel) table.getColumnModel().getSelectionModel();
|
604 |
|
|
|
605 |
|
|
int rowLead = rowModel.getLeadSelectionIndex();
|
606 |
|
|
int rowMax = table.getModel().getRowCount() - 1;
|
607 |
|
|
|
608 |
|
|
int colLead = colModel.getLeadSelectionIndex();
|
609 |
|
|
int colMax = table.getModel().getColumnCount() - 1;
|
610 |
|
|
|
611 |
|
|
// The command with which the action has been called is stored
|
612 |
|
|
// in this undocumented action value. This allows us to have only
|
613 |
|
|
// one Action instance to serve all keyboard input for JTable.
|
614 |
|
|
String command = (String) getValue("__command__");
|
615 |
|
|
if (command.equals("selectPreviousRowExtendSelection"))
|
616 |
|
|
{
|
617 |
|
|
rowModel.setLeadSelectionIndex(Math.max(rowLead - 1, 0));
|
618 |
|
|
}
|
619 |
|
|
else if (command.equals("selectLastColumn"))
|
620 |
|
|
{
|
621 |
|
|
colModel.setSelectionInterval(colMax, colMax);
|
622 |
|
|
}
|
623 |
|
|
else if (command.equals("startEditing"))
|
624 |
|
|
{
|
625 |
|
|
if (table.isCellEditable(rowLead, colLead))
|
626 |
|
|
table.editCellAt(rowLead, colLead);
|
627 |
|
|
}
|
628 |
|
|
else if (command.equals("selectFirstRowExtendSelection"))
|
629 |
|
|
{
|
630 |
|
|
rowModel.setLeadSelectionIndex(0);
|
631 |
|
|
}
|
632 |
|
|
else if (command.equals("selectFirstColumn"))
|
633 |
|
|
{
|
634 |
|
|
colModel.setSelectionInterval(0, 0);
|
635 |
|
|
}
|
636 |
|
|
else if (command.equals("selectFirstColumnExtendSelection"))
|
637 |
|
|
{
|
638 |
|
|
colModel.setLeadSelectionIndex(0);
|
639 |
|
|
}
|
640 |
|
|
else if (command.equals("selectLastRow"))
|
641 |
|
|
{
|
642 |
|
|
rowModel.setSelectionInterval(rowMax, rowMax);
|
643 |
|
|
}
|
644 |
|
|
else if (command.equals("selectNextRowExtendSelection"))
|
645 |
|
|
{
|
646 |
|
|
rowModel.setLeadSelectionIndex(Math.min(rowLead + 1, rowMax));
|
647 |
|
|
}
|
648 |
|
|
else if (command.equals("selectFirstRow"))
|
649 |
|
|
{
|
650 |
|
|
rowModel.setSelectionInterval(0, 0);
|
651 |
|
|
}
|
652 |
|
|
else if (command.equals("selectNextColumnExtendSelection"))
|
653 |
|
|
{
|
654 |
|
|
colModel.setLeadSelectionIndex(Math.min(colLead + 1, colMax));
|
655 |
|
|
}
|
656 |
|
|
else if (command.equals("selectLastColumnExtendSelection"))
|
657 |
|
|
{
|
658 |
|
|
colModel.setLeadSelectionIndex(colMax);
|
659 |
|
|
}
|
660 |
|
|
else if (command.equals("selectPreviousColumnExtendSelection"))
|
661 |
|
|
{
|
662 |
|
|
colModel.setLeadSelectionIndex(Math.max(colLead - 1, 0));
|
663 |
|
|
}
|
664 |
|
|
else if (command.equals("selectNextRow"))
|
665 |
|
|
{
|
666 |
|
|
rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax),
|
667 |
|
|
Math.min(rowLead + 1, rowMax));
|
668 |
|
|
}
|
669 |
|
|
else if (command.equals("scrollUpExtendSelection"))
|
670 |
|
|
{
|
671 |
|
|
int target;
|
672 |
|
|
if (rowLead == getFirstVisibleRowIndex(table))
|
673 |
|
|
target = Math.max(0, rowLead - (getLastVisibleRowIndex(table)
|
674 |
|
|
- getFirstVisibleRowIndex(table) + 1));
|
675 |
|
|
else
|
676 |
|
|
target = getFirstVisibleRowIndex(table);
|
677 |
|
|
|
678 |
|
|
rowModel.setLeadSelectionIndex(target);
|
679 |
|
|
colModel.setLeadSelectionIndex(colLead);
|
680 |
|
|
}
|
681 |
|
|
else if (command.equals("selectPreviousRow"))
|
682 |
|
|
{
|
683 |
|
|
rowModel.setSelectionInterval(Math.max(rowLead - 1, 0),
|
684 |
|
|
Math.max(rowLead - 1, 0));
|
685 |
|
|
}
|
686 |
|
|
else if (command.equals("scrollRightChangeSelection"))
|
687 |
|
|
{
|
688 |
|
|
int target;
|
689 |
|
|
if (colLead == getLastVisibleColumnIndex(table))
|
690 |
|
|
target = Math.min(colMax, colLead
|
691 |
|
|
+ (getLastVisibleColumnIndex(table)
|
692 |
|
|
- getFirstVisibleColumnIndex(table) + 1));
|
693 |
|
|
else
|
694 |
|
|
target = getLastVisibleColumnIndex(table);
|
695 |
|
|
|
696 |
|
|
colModel.setSelectionInterval(target, target);
|
697 |
|
|
rowModel.setSelectionInterval(rowLead, rowLead);
|
698 |
|
|
}
|
699 |
|
|
else if (command.equals("selectPreviousColumn"))
|
700 |
|
|
{
|
701 |
|
|
colModel.setSelectionInterval(Math.max(colLead - 1, 0),
|
702 |
|
|
Math.max(colLead - 1, 0));
|
703 |
|
|
}
|
704 |
|
|
else if (command.equals("scrollLeftChangeSelection"))
|
705 |
|
|
{
|
706 |
|
|
int target;
|
707 |
|
|
if (colLead == getFirstVisibleColumnIndex(table))
|
708 |
|
|
target = Math.max(0, colLead - (getLastVisibleColumnIndex(table)
|
709 |
|
|
- getFirstVisibleColumnIndex(table) + 1));
|
710 |
|
|
else
|
711 |
|
|
target = getFirstVisibleColumnIndex(table);
|
712 |
|
|
|
713 |
|
|
colModel.setSelectionInterval(target, target);
|
714 |
|
|
rowModel.setSelectionInterval(rowLead, rowLead);
|
715 |
|
|
}
|
716 |
|
|
else if (command.equals("clearSelection"))
|
717 |
|
|
{
|
718 |
|
|
table.clearSelection();
|
719 |
|
|
}
|
720 |
|
|
else if (command.equals("cancel"))
|
721 |
|
|
{
|
722 |
|
|
// FIXME: implement other parts of "cancel" like undo-ing last
|
723 |
|
|
// selection. Right now it just calls editingCancelled if
|
724 |
|
|
// we're currently editing.
|
725 |
|
|
if (table.isEditing())
|
726 |
|
|
table.editingCanceled(new ChangeEvent("cancel"));
|
727 |
|
|
}
|
728 |
|
|
else if (command.equals("selectNextRowCell")
|
729 |
|
|
|| command.equals("selectPreviousRowCell")
|
730 |
|
|
|| command.equals("selectNextColumnCell")
|
731 |
|
|
|| command.equals("selectPreviousColumnCell"))
|
732 |
|
|
{
|
733 |
|
|
// If nothing is selected, select the first cell in the table
|
734 |
|
|
if (table.getSelectedRowCount() == 0 &&
|
735 |
|
|
table.getSelectedColumnCount() == 0)
|
736 |
|
|
{
|
737 |
|
|
rowModel.setSelectionInterval(0, 0);
|
738 |
|
|
colModel.setSelectionInterval(0, 0);
|
739 |
|
|
return;
|
740 |
|
|
}
|
741 |
|
|
|
742 |
|
|
// If the lead selection index isn't selected (ie a remove operation
|
743 |
|
|
// happened, then set the lead to the first selected cell in the
|
744 |
|
|
// table
|
745 |
|
|
if (!table.isCellSelected(rowLead, colLead))
|
746 |
|
|
{
|
747 |
|
|
rowModel.addSelectionInterval(rowModel.getMinSelectionIndex(),
|
748 |
|
|
rowModel.getMinSelectionIndex());
|
749 |
|
|
colModel.addSelectionInterval(colModel.getMinSelectionIndex(),
|
750 |
|
|
colModel.getMinSelectionIndex());
|
751 |
|
|
return;
|
752 |
|
|
}
|
753 |
|
|
|
754 |
|
|
// multRowsSelected and multColsSelected tell us if multiple rows or
|
755 |
|
|
// columns are selected, respectively
|
756 |
|
|
boolean multRowsSelected, multColsSelected;
|
757 |
|
|
multRowsSelected = table.getSelectedRowCount() > 1 &&
|
758 |
|
|
table.getRowSelectionAllowed();
|
759 |
|
|
|
760 |
|
|
multColsSelected = table.getSelectedColumnCount() > 1 &&
|
761 |
|
|
table.getColumnSelectionAllowed();
|
762 |
|
|
|
763 |
|
|
// If there is just one selection, select the next cell, and wrap
|
764 |
|
|
// when you get to the edges of the table.
|
765 |
|
|
if (!multColsSelected && !multRowsSelected)
|
766 |
|
|
{
|
767 |
|
|
if (command.indexOf("Column") != -1)
|
768 |
|
|
advanceSingleSelection(colModel, colMax, rowModel, rowMax,
|
769 |
|
|
command.equals("selectPreviousColumnCell"));
|
770 |
|
|
else
|
771 |
|
|
advanceSingleSelection(rowModel, rowMax, colModel, colMax,
|
772 |
|
|
command.equals("selectPreviousRowCell"));
|
773 |
|
|
return;
|
774 |
|
|
}
|
775 |
|
|
|
776 |
|
|
|
777 |
|
|
// rowMinSelected and rowMaxSelected are the minimum and maximum
|
778 |
|
|
// values respectively of selected cells in the row selection model
|
779 |
|
|
// Similarly for colMinSelected and colMaxSelected.
|
780 |
|
|
int rowMaxSelected = table.getRowSelectionAllowed() ?
|
781 |
|
|
rowModel.getMaxSelectionIndex() : table.getModel().getRowCount() - 1;
|
782 |
|
|
int rowMinSelected = table.getRowSelectionAllowed() ?
|
783 |
|
|
rowModel.getMinSelectionIndex() : 0;
|
784 |
|
|
int colMaxSelected = table.getColumnSelectionAllowed() ?
|
785 |
|
|
colModel.getMaxSelectionIndex() :
|
786 |
|
|
table.getModel().getColumnCount() - 1;
|
787 |
|
|
int colMinSelected = table.getColumnSelectionAllowed() ?
|
788 |
|
|
colModel.getMinSelectionIndex() : 0;
|
789 |
|
|
|
790 |
|
|
// If there are multiple rows and columns selected, select the next
|
791 |
|
|
// cell and wrap at the edges of the selection.
|
792 |
|
|
if (command.indexOf("Column") != -1)
|
793 |
|
|
advanceMultipleSelection(table, colModel, colMinSelected,
|
794 |
|
|
colMaxSelected, rowModel, rowMinSelected,
|
795 |
|
|
rowMaxSelected,
|
796 |
|
|
command.equals("selectPreviousColumnCell"),
|
797 |
|
|
true);
|
798 |
|
|
|
799 |
|
|
else
|
800 |
|
|
advanceMultipleSelection(table, rowModel, rowMinSelected,
|
801 |
|
|
rowMaxSelected, colModel, colMinSelected,
|
802 |
|
|
colMaxSelected,
|
803 |
|
|
command.equals("selectPreviousRowCell"),
|
804 |
|
|
false);
|
805 |
|
|
}
|
806 |
|
|
else if (command.equals("selectNextColumn"))
|
807 |
|
|
{
|
808 |
|
|
colModel.setSelectionInterval(Math.min(colLead + 1, colMax),
|
809 |
|
|
Math.min(colLead + 1, colMax));
|
810 |
|
|
}
|
811 |
|
|
else if (command.equals("scrollLeftExtendSelection"))
|
812 |
|
|
{
|
813 |
|
|
int target;
|
814 |
|
|
if (colLead == getFirstVisibleColumnIndex(table))
|
815 |
|
|
target = Math.max(0, colLead - (getLastVisibleColumnIndex(table)
|
816 |
|
|
- getFirstVisibleColumnIndex(table) + 1));
|
817 |
|
|
else
|
818 |
|
|
target = getFirstVisibleColumnIndex(table);
|
819 |
|
|
|
820 |
|
|
colModel.setLeadSelectionIndex(target);
|
821 |
|
|
rowModel.setLeadSelectionIndex(rowLead);
|
822 |
|
|
}
|
823 |
|
|
else if (command.equals("scrollDownChangeSelection"))
|
824 |
|
|
{
|
825 |
|
|
int target;
|
826 |
|
|
if (rowLead == getLastVisibleRowIndex(table))
|
827 |
|
|
target = Math.min(rowMax, rowLead + (getLastVisibleRowIndex(table)
|
828 |
|
|
- getFirstVisibleRowIndex(table) + 1));
|
829 |
|
|
else
|
830 |
|
|
target = getLastVisibleRowIndex(table);
|
831 |
|
|
|
832 |
|
|
rowModel.setSelectionInterval(target, target);
|
833 |
|
|
colModel.setSelectionInterval(colLead, colLead);
|
834 |
|
|
}
|
835 |
|
|
else if (command.equals("scrollRightExtendSelection"))
|
836 |
|
|
{
|
837 |
|
|
int target;
|
838 |
|
|
if (colLead == getLastVisibleColumnIndex(table))
|
839 |
|
|
target = Math.min(colMax, colLead + (getLastVisibleColumnIndex(table)
|
840 |
|
|
- getFirstVisibleColumnIndex(table) + 1));
|
841 |
|
|
else
|
842 |
|
|
target = getLastVisibleColumnIndex(table);
|
843 |
|
|
|
844 |
|
|
colModel.setLeadSelectionIndex(target);
|
845 |
|
|
rowModel.setLeadSelectionIndex(rowLead);
|
846 |
|
|
}
|
847 |
|
|
else if (command.equals("selectAll"))
|
848 |
|
|
{
|
849 |
|
|
table.selectAll();
|
850 |
|
|
}
|
851 |
|
|
else if (command.equals("selectLastRowExtendSelection"))
|
852 |
|
|
{
|
853 |
|
|
rowModel.setLeadSelectionIndex(rowMax);
|
854 |
|
|
colModel.setLeadSelectionIndex(colLead);
|
855 |
|
|
}
|
856 |
|
|
else if (command.equals("scrollDownExtendSelection"))
|
857 |
|
|
{
|
858 |
|
|
int target;
|
859 |
|
|
if (rowLead == getLastVisibleRowIndex(table))
|
860 |
|
|
target = Math.min(rowMax, rowLead + (getLastVisibleRowIndex(table)
|
861 |
|
|
- getFirstVisibleRowIndex(table) + 1));
|
862 |
|
|
else
|
863 |
|
|
target = getLastVisibleRowIndex(table);
|
864 |
|
|
|
865 |
|
|
rowModel.setLeadSelectionIndex(target);
|
866 |
|
|
colModel.setLeadSelectionIndex(colLead);
|
867 |
|
|
}
|
868 |
|
|
else if (command.equals("scrollUpChangeSelection"))
|
869 |
|
|
{
|
870 |
|
|
int target;
|
871 |
|
|
if (rowLead == getFirstVisibleRowIndex(table))
|
872 |
|
|
target = Math.max(0, rowLead - (getLastVisibleRowIndex(table)
|
873 |
|
|
- getFirstVisibleRowIndex(table) + 1));
|
874 |
|
|
else
|
875 |
|
|
target = getFirstVisibleRowIndex(table);
|
876 |
|
|
|
877 |
|
|
rowModel.setSelectionInterval(target, target);
|
878 |
|
|
colModel.setSelectionInterval(colLead, colLead);
|
879 |
|
|
}
|
880 |
|
|
else if (command.equals("selectNextRowChangeLead"))
|
881 |
|
|
{
|
882 |
|
|
if (rowModel.getSelectionMode()
|
883 |
|
|
!= ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
|
884 |
|
|
{
|
885 |
|
|
// just "selectNextRow"
|
886 |
|
|
rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax),
|
887 |
|
|
Math.min(rowLead + 1, rowMax));
|
888 |
|
|
colModel.setSelectionInterval(colLead, colLead);
|
889 |
|
|
}
|
890 |
|
|
else
|
891 |
|
|
rowModel.moveLeadSelectionIndex(Math.min(rowLead + 1, rowMax));
|
892 |
|
|
}
|
893 |
|
|
else if (command.equals("selectPreviousRowChangeLead"))
|
894 |
|
|
{
|
895 |
|
|
if (rowModel.getSelectionMode()
|
896 |
|
|
!= ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
|
897 |
|
|
{
|
898 |
|
|
// just selectPreviousRow
|
899 |
|
|
rowModel.setSelectionInterval(Math.max(rowLead - 1, 0),
|
900 |
|
|
Math.min(rowLead - 1, 0));
|
901 |
|
|
colModel.setSelectionInterval(colLead, colLead);
|
902 |
|
|
}
|
903 |
|
|
else
|
904 |
|
|
rowModel.moveLeadSelectionIndex(Math.max(rowLead - 1, 0));
|
905 |
|
|
}
|
906 |
|
|
else if (command.equals("selectNextColumnChangeLead"))
|
907 |
|
|
{
|
908 |
|
|
if (colModel.getSelectionMode()
|
909 |
|
|
!= ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
|
910 |
|
|
{
|
911 |
|
|
// just selectNextColumn
|
912 |
|
|
rowModel.setSelectionInterval(rowLead, rowLead);
|
913 |
|
|
colModel.setSelectionInterval(Math.min(colLead + 1, colMax),
|
914 |
|
|
Math.min(colLead + 1, colMax));
|
915 |
|
|
}
|
916 |
|
|
else
|
917 |
|
|
colModel.moveLeadSelectionIndex(Math.min(colLead + 1, colMax));
|
918 |
|
|
}
|
919 |
|
|
else if (command.equals("selectPreviousColumnChangeLead"))
|
920 |
|
|
{
|
921 |
|
|
if (colModel.getSelectionMode()
|
922 |
|
|
!= ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
|
923 |
|
|
{
|
924 |
|
|
// just selectPreviousColumn
|
925 |
|
|
rowModel.setSelectionInterval(rowLead, rowLead);
|
926 |
|
|
colModel.setSelectionInterval(Math.max(colLead - 1, 0),
|
927 |
|
|
Math.max(colLead - 1, 0));
|
928 |
|
|
|
929 |
|
|
}
|
930 |
|
|
else
|
931 |
|
|
colModel.moveLeadSelectionIndex(Math.max(colLead - 1, 0));
|
932 |
|
|
}
|
933 |
|
|
else if (command.equals("addToSelection"))
|
934 |
|
|
{
|
935 |
|
|
if (!table.isEditing())
|
936 |
|
|
{
|
937 |
|
|
int oldRowAnchor = rowModel.getAnchorSelectionIndex();
|
938 |
|
|
int oldColAnchor = colModel.getAnchorSelectionIndex();
|
939 |
|
|
rowModel.addSelectionInterval(rowLead, rowLead);
|
940 |
|
|
colModel.addSelectionInterval(colLead, colLead);
|
941 |
|
|
rowModel.setAnchorSelectionIndex(oldRowAnchor);
|
942 |
|
|
colModel.setAnchorSelectionIndex(oldColAnchor);
|
943 |
|
|
}
|
944 |
|
|
}
|
945 |
|
|
else if (command.equals("extendTo"))
|
946 |
|
|
{
|
947 |
|
|
rowModel.setSelectionInterval(rowModel.getAnchorSelectionIndex(),
|
948 |
|
|
rowLead);
|
949 |
|
|
colModel.setSelectionInterval(colModel.getAnchorSelectionIndex(),
|
950 |
|
|
colLead);
|
951 |
|
|
}
|
952 |
|
|
else if (command.equals("toggleAndAnchor"))
|
953 |
|
|
{
|
954 |
|
|
if (rowModel.isSelectedIndex(rowLead))
|
955 |
|
|
rowModel.removeSelectionInterval(rowLead, rowLead);
|
956 |
|
|
else
|
957 |
|
|
rowModel.addSelectionInterval(rowLead, rowLead);
|
958 |
|
|
|
959 |
|
|
if (colModel.isSelectedIndex(colLead))
|
960 |
|
|
colModel.removeSelectionInterval(colLead, colLead);
|
961 |
|
|
else
|
962 |
|
|
colModel.addSelectionInterval(colLead, colLead);
|
963 |
|
|
|
964 |
|
|
rowModel.setAnchorSelectionIndex(rowLead);
|
965 |
|
|
colModel.setAnchorSelectionIndex(colLead);
|
966 |
|
|
}
|
967 |
|
|
else if (command.equals("stopEditing"))
|
968 |
|
|
{
|
969 |
|
|
table.editingStopped(new ChangeEvent(command));
|
970 |
|
|
}
|
971 |
|
|
else
|
972 |
|
|
{
|
973 |
|
|
// If we're here that means we bound this TableAction class
|
974 |
|
|
// to a keyboard input but we either want to ignore that input
|
975 |
|
|
// or we just haven't implemented its action yet.
|
976 |
|
|
|
977 |
|
|
// Uncomment the following line to print the names of unused bindings
|
978 |
|
|
// when their keys are pressed
|
979 |
|
|
|
980 |
|
|
// System.out.println ("not implemented: "+e.getActionCommand());
|
981 |
|
|
}
|
982 |
|
|
|
983 |
|
|
// Any commands whose keyStrokes should be used by the Editor should not
|
984 |
|
|
// cause editing to be stopped: ie, the SPACE sends "addToSelection" but
|
985 |
|
|
// if the table is in editing mode, the space should not cause us to stop
|
986 |
|
|
// editing because it should be used by the Editor.
|
987 |
|
|
if (table.isEditing() && command != "startEditing"
|
988 |
|
|
&& command != "addToSelection")
|
989 |
|
|
table.editingStopped(new ChangeEvent("update"));
|
990 |
|
|
|
991 |
|
|
table.scrollRectToVisible(table.getCellRect(
|
992 |
|
|
rowModel.getLeadSelectionIndex(), colModel.getLeadSelectionIndex(),
|
993 |
|
|
false));
|
994 |
|
|
}
|
995 |
|
|
|
996 |
|
|
/**
|
997 |
|
|
* Returns the column index of the first visible column.
|
998 |
|
|
* @return the column index of the first visible column.
|
999 |
|
|
*/
|
1000 |
|
|
int getFirstVisibleColumnIndex(JTable table)
|
1001 |
|
|
{
|
1002 |
|
|
ComponentOrientation or = table.getComponentOrientation();
|
1003 |
|
|
Rectangle r = table.getVisibleRect();
|
1004 |
|
|
if (!or.isLeftToRight())
|
1005 |
|
|
r.translate((int) r.getWidth() - 1, 0);
|
1006 |
|
|
return table.columnAtPoint(r.getLocation());
|
1007 |
|
|
}
|
1008 |
|
|
|
1009 |
|
|
/**
|
1010 |
|
|
* Returns the column index of the last visible column.
|
1011 |
|
|
*
|
1012 |
|
|
*/
|
1013 |
|
|
int getLastVisibleColumnIndex(JTable table)
|
1014 |
|
|
{
|
1015 |
|
|
ComponentOrientation or = table.getComponentOrientation();
|
1016 |
|
|
Rectangle r = table.getVisibleRect();
|
1017 |
|
|
if (or.isLeftToRight())
|
1018 |
|
|
r.translate((int) r.getWidth() - 1, 0);
|
1019 |
|
|
return table.columnAtPoint(r.getLocation());
|
1020 |
|
|
}
|
1021 |
|
|
|
1022 |
|
|
/**
|
1023 |
|
|
* Returns the row index of the first visible row.
|
1024 |
|
|
*
|
1025 |
|
|
*/
|
1026 |
|
|
int getFirstVisibleRowIndex(JTable table)
|
1027 |
|
|
{
|
1028 |
|
|
ComponentOrientation or = table.getComponentOrientation();
|
1029 |
|
|
Rectangle r = table.getVisibleRect();
|
1030 |
|
|
if (!or.isLeftToRight())
|
1031 |
|
|
r.translate((int) r.getWidth() - 1, 0);
|
1032 |
|
|
return table.rowAtPoint(r.getLocation());
|
1033 |
|
|
}
|
1034 |
|
|
|
1035 |
|
|
/**
|
1036 |
|
|
* Returns the row index of the last visible row.
|
1037 |
|
|
*
|
1038 |
|
|
*/
|
1039 |
|
|
int getLastVisibleRowIndex(JTable table)
|
1040 |
|
|
{
|
1041 |
|
|
ComponentOrientation or = table.getComponentOrientation();
|
1042 |
|
|
Rectangle r = table.getVisibleRect();
|
1043 |
|
|
r.translate(0, (int) r.getHeight() - 1);
|
1044 |
|
|
if (or.isLeftToRight())
|
1045 |
|
|
r.translate((int) r.getWidth() - 1, 0);
|
1046 |
|
|
// The next if makes sure that we don't return -1 simply because
|
1047 |
|
|
// there is white space at the bottom of the table (ie, the display
|
1048 |
|
|
// area is larger than the table)
|
1049 |
|
|
if (table.rowAtPoint(r.getLocation()) == -1)
|
1050 |
|
|
{
|
1051 |
|
|
if (getFirstVisibleRowIndex(table) == -1)
|
1052 |
|
|
return -1;
|
1053 |
|
|
else
|
1054 |
|
|
return table.getModel().getRowCount() - 1;
|
1055 |
|
|
}
|
1056 |
|
|
return table.rowAtPoint(r.getLocation());
|
1057 |
|
|
}
|
1058 |
|
|
|
1059 |
|
|
/**
|
1060 |
|
|
* A helper method for the key bindings. Used because the actions
|
1061 |
|
|
* for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar.
|
1062 |
|
|
*
|
1063 |
|
|
* Selects the next (previous if SHIFT pressed) column for TAB, or row for
|
1064 |
|
|
* ENTER from within the currently selected cells.
|
1065 |
|
|
*
|
1066 |
|
|
* @param firstModel the ListSelectionModel for columns (TAB) or
|
1067 |
|
|
* rows (ENTER)
|
1068 |
|
|
* @param firstMin the first selected index in firstModel
|
1069 |
|
|
* @param firstMax the last selected index in firstModel
|
1070 |
|
|
* @param secondModel the ListSelectionModel for rows (TAB) or
|
1071 |
|
|
* columns (ENTER)
|
1072 |
|
|
* @param secondMin the first selected index in secondModel
|
1073 |
|
|
* @param secondMax the last selected index in secondModel
|
1074 |
|
|
* @param reverse true if shift was held for the event
|
1075 |
|
|
* @param eventIsTab true if TAB was pressed, false if ENTER pressed
|
1076 |
|
|
*/
|
1077 |
|
|
void advanceMultipleSelection(JTable table, ListSelectionModel firstModel,
|
1078 |
|
|
int firstMin,
|
1079 |
|
|
int firstMax, ListSelectionModel secondModel,
|
1080 |
|
|
int secondMin, int secondMax, boolean reverse,
|
1081 |
|
|
boolean eventIsTab)
|
1082 |
|
|
{
|
1083 |
|
|
// If eventIsTab, all the "firsts" correspond to columns, otherwise, to
|
1084 |
|
|
// rows "seconds" correspond to the opposite
|
1085 |
|
|
int firstLead = firstModel.getLeadSelectionIndex();
|
1086 |
|
|
int secondLead = secondModel.getLeadSelectionIndex();
|
1087 |
|
|
int numFirsts = eventIsTab ?
|
1088 |
|
|
table.getModel().getColumnCount() : table.getModel().getRowCount();
|
1089 |
|
|
int numSeconds = eventIsTab ?
|
1090 |
|
|
table.getModel().getRowCount() : table.getModel().getColumnCount();
|
1091 |
|
|
|
1092 |
|
|
// check if we have to wrap the "firsts" around, going to the other side
|
1093 |
|
|
if ((firstLead == firstMax && !reverse) ||
|
1094 |
|
|
(reverse && firstLead == firstMin))
|
1095 |
|
|
{
|
1096 |
|
|
firstModel.addSelectionInterval(reverse ? firstMax : firstMin,
|
1097 |
|
|
reverse ? firstMax : firstMin);
|
1098 |
|
|
|
1099 |
|
|
// check if we have to wrap the "seconds"
|
1100 |
|
|
if ((secondLead == secondMax && !reverse) ||
|
1101 |
|
|
(reverse && secondLead == secondMin))
|
1102 |
|
|
secondModel.addSelectionInterval(reverse ? secondMax : secondMin,
|
1103 |
|
|
reverse ? secondMax : secondMin);
|
1104 |
|
|
|
1105 |
|
|
// if we're not wrapping the seconds, we have to find out where we
|
1106 |
|
|
// are within the secondModel and advance to the next cell (or
|
1107 |
|
|
// go back to the previous cell if reverse == true)
|
1108 |
|
|
else
|
1109 |
|
|
{
|
1110 |
|
|
int[] secondsSelected;
|
1111 |
|
|
if (eventIsTab && table.getRowSelectionAllowed() ||
|
1112 |
|
|
!eventIsTab && table.getColumnSelectionAllowed())
|
1113 |
|
|
secondsSelected = eventIsTab ?
|
1114 |
|
|
table.getSelectedRows() : table.getSelectedColumns();
|
1115 |
|
|
else
|
1116 |
|
|
{
|
1117 |
|
|
// if row selection is not allowed, then the entire column gets
|
1118 |
|
|
// selected when you click on it, so consider ALL rows selected
|
1119 |
|
|
secondsSelected = new int[numSeconds];
|
1120 |
|
|
for (int i = 0; i < numSeconds; i++)
|
1121 |
|
|
secondsSelected[i] = i;
|
1122 |
|
|
}
|
1123 |
|
|
|
1124 |
|
|
// and now find the "next" index within the model
|
1125 |
|
|
int secondIndex = reverse ? secondsSelected.length - 1 : 0;
|
1126 |
|
|
if (!reverse)
|
1127 |
|
|
while (secondsSelected[secondIndex] <= secondLead)
|
1128 |
|
|
secondIndex++;
|
1129 |
|
|
else
|
1130 |
|
|
while (secondsSelected[secondIndex] >= secondLead)
|
1131 |
|
|
secondIndex--;
|
1132 |
|
|
|
1133 |
|
|
// and select it - updating the lead selection index
|
1134 |
|
|
secondModel.addSelectionInterval(secondsSelected[secondIndex],
|
1135 |
|
|
secondsSelected[secondIndex]);
|
1136 |
|
|
}
|
1137 |
|
|
}
|
1138 |
|
|
// We didn't have to wrap the firsts, so just find the "next" first
|
1139 |
|
|
// and select it, we don't have to change "seconds"
|
1140 |
|
|
else
|
1141 |
|
|
{
|
1142 |
|
|
int[] firstsSelected;
|
1143 |
|
|
if (eventIsTab && table.getColumnSelectionAllowed() ||
|
1144 |
|
|
!eventIsTab && table.getRowSelectionAllowed())
|
1145 |
|
|
firstsSelected = eventIsTab ?
|
1146 |
|
|
table.getSelectedColumns() : table.getSelectedRows();
|
1147 |
|
|
else
|
1148 |
|
|
{
|
1149 |
|
|
// if selection not allowed, consider ALL firsts to be selected
|
1150 |
|
|
firstsSelected = new int[numFirsts];
|
1151 |
|
|
for (int i = 0; i < numFirsts; i++)
|
1152 |
|
|
firstsSelected[i] = i;
|
1153 |
|
|
}
|
1154 |
|
|
int firstIndex = reverse ? firstsSelected.length - 1 : 0;
|
1155 |
|
|
if (!reverse)
|
1156 |
|
|
while (firstsSelected[firstIndex] <= firstLead)
|
1157 |
|
|
firstIndex++;
|
1158 |
|
|
else
|
1159 |
|
|
while (firstsSelected[firstIndex] >= firstLead)
|
1160 |
|
|
firstIndex--;
|
1161 |
|
|
firstModel.addSelectionInterval(firstsSelected[firstIndex],
|
1162 |
|
|
firstsSelected[firstIndex]);
|
1163 |
|
|
secondModel.addSelectionInterval(secondLead, secondLead);
|
1164 |
|
|
}
|
1165 |
|
|
}
|
1166 |
|
|
|
1167 |
|
|
/**
|
1168 |
|
|
* A helper method for the key bindings. Used because the actions
|
1169 |
|
|
* for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar.
|
1170 |
|
|
*
|
1171 |
|
|
* Selects the next (previous if SHIFT pressed) column (TAB) or row (ENTER)
|
1172 |
|
|
* in the table, changing the current selection. All cells in the table
|
1173 |
|
|
* are eligible, not just the ones that are currently selected.
|
1174 |
|
|
* @param firstModel the ListSelectionModel for columns (TAB) or rows
|
1175 |
|
|
* (ENTER)
|
1176 |
|
|
* @param firstMax the last index in firstModel
|
1177 |
|
|
* @param secondModel the ListSelectionModel for rows (TAB) or columns
|
1178 |
|
|
* (ENTER)
|
1179 |
|
|
* @param secondMax the last index in secondModel
|
1180 |
|
|
* @param reverse true if SHIFT was pressed for the event
|
1181 |
|
|
*/
|
1182 |
|
|
|
1183 |
|
|
void advanceSingleSelection(ListSelectionModel firstModel, int firstMax,
|
1184 |
|
|
ListSelectionModel secondModel, int secondMax,
|
1185 |
|
|
boolean reverse)
|
1186 |
|
|
{
|
1187 |
|
|
// for TABs, "first" corresponds to columns and "seconds" to rows.
|
1188 |
|
|
// the opposite is true for ENTERs
|
1189 |
|
|
int firstLead = firstModel.getLeadSelectionIndex();
|
1190 |
|
|
int secondLead = secondModel.getLeadSelectionIndex();
|
1191 |
|
|
|
1192 |
|
|
// if we are going backwards subtract 2 because we later add 1
|
1193 |
|
|
// for a net change of -1
|
1194 |
|
|
if (reverse && (firstLead == 0))
|
1195 |
|
|
{
|
1196 |
|
|
// check if we have to wrap around
|
1197 |
|
|
if (secondLead == 0)
|
1198 |
|
|
secondLead += secondMax + 1;
|
1199 |
|
|
secondLead -= 2;
|
1200 |
|
|
}
|
1201 |
|
|
|
1202 |
|
|
// do we have to wrap the "seconds"?
|
1203 |
|
|
if (reverse && (firstLead == 0) || !reverse && (firstLead == firstMax))
|
1204 |
|
|
secondModel.setSelectionInterval((secondLead + 1) % (secondMax + 1),
|
1205 |
|
|
(secondLead + 1) % (secondMax + 1));
|
1206 |
|
|
// if not, just reselect the current lead
|
1207 |
|
|
else
|
1208 |
|
|
secondModel.setSelectionInterval(secondLead, secondLead);
|
1209 |
|
|
|
1210 |
|
|
// if we are going backwards, subtract 2 because we add 1 later
|
1211 |
|
|
// for net change of -1
|
1212 |
|
|
if (reverse)
|
1213 |
|
|
{
|
1214 |
|
|
// check for wraparound
|
1215 |
|
|
if (firstLead == 0)
|
1216 |
|
|
firstLead += firstMax + 1;
|
1217 |
|
|
firstLead -= 2;
|
1218 |
|
|
}
|
1219 |
|
|
// select the next "first"
|
1220 |
|
|
firstModel.setSelectionInterval((firstLead + 1) % (firstMax + 1),
|
1221 |
|
|
(firstLead + 1) % (firstMax + 1));
|
1222 |
|
|
}
|
1223 |
|
|
}
|
1224 |
|
|
|
1225 |
|
|
protected void installListeners()
|
1226 |
|
|
{
|
1227 |
|
|
if (focusListener == null)
|
1228 |
|
|
focusListener = createFocusListener();
|
1229 |
|
|
table.addFocusListener(focusListener);
|
1230 |
|
|
if (keyListener == null)
|
1231 |
|
|
keyListener = createKeyListener();
|
1232 |
|
|
table.addKeyListener(keyListener);
|
1233 |
|
|
if (mouseInputListener == null)
|
1234 |
|
|
mouseInputListener = createMouseInputListener();
|
1235 |
|
|
table.addMouseListener(mouseInputListener);
|
1236 |
|
|
table.addMouseMotionListener(mouseInputListener);
|
1237 |
|
|
if (propertyChangeListener == null)
|
1238 |
|
|
propertyChangeListener = new PropertyChangeHandler();
|
1239 |
|
|
table.addPropertyChangeListener(propertyChangeListener);
|
1240 |
|
|
}
|
1241 |
|
|
|
1242 |
|
|
/**
|
1243 |
|
|
* Uninstalls UI defaults that have been installed by
|
1244 |
|
|
* {@link #installDefaults()}.
|
1245 |
|
|
*/
|
1246 |
|
|
protected void uninstallDefaults()
|
1247 |
|
|
{
|
1248 |
|
|
// Nothing to do here for now.
|
1249 |
|
|
}
|
1250 |
|
|
|
1251 |
|
|
/**
|
1252 |
|
|
* Uninstalls the keyboard actions that have been installed by
|
1253 |
|
|
* {@link #installKeyboardActions()}.
|
1254 |
|
|
*/
|
1255 |
|
|
protected void uninstallKeyboardActions()
|
1256 |
|
|
{
|
1257 |
|
|
SwingUtilities.replaceUIInputMap(table, JComponent.
|
1258 |
|
|
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
|
1259 |
|
|
SwingUtilities.replaceUIActionMap(table, null);
|
1260 |
|
|
}
|
1261 |
|
|
|
1262 |
|
|
protected void uninstallListeners()
|
1263 |
|
|
{
|
1264 |
|
|
table.removeFocusListener(focusListener);
|
1265 |
|
|
table.removeKeyListener(keyListener);
|
1266 |
|
|
table.removeMouseListener(mouseInputListener);
|
1267 |
|
|
table.removeMouseMotionListener(mouseInputListener);
|
1268 |
|
|
table.removePropertyChangeListener(propertyChangeListener);
|
1269 |
|
|
propertyChangeListener = null;
|
1270 |
|
|
}
|
1271 |
|
|
|
1272 |
|
|
public void installUI(JComponent comp)
|
1273 |
|
|
{
|
1274 |
|
|
table = (JTable) comp;
|
1275 |
|
|
rendererPane = new CellRendererPane();
|
1276 |
|
|
table.add(rendererPane);
|
1277 |
|
|
|
1278 |
|
|
installDefaults();
|
1279 |
|
|
installKeyboardActions();
|
1280 |
|
|
installListeners();
|
1281 |
|
|
}
|
1282 |
|
|
|
1283 |
|
|
public void uninstallUI(JComponent c)
|
1284 |
|
|
{
|
1285 |
|
|
uninstallListeners();
|
1286 |
|
|
uninstallKeyboardActions();
|
1287 |
|
|
uninstallDefaults();
|
1288 |
|
|
|
1289 |
|
|
table.remove(rendererPane);
|
1290 |
|
|
rendererPane = null;
|
1291 |
|
|
table = null;
|
1292 |
|
|
}
|
1293 |
|
|
|
1294 |
|
|
/**
|
1295 |
|
|
* Paints a single cell in the table.
|
1296 |
|
|
*
|
1297 |
|
|
* @param g The graphics context to paint in
|
1298 |
|
|
* @param row The row number to paint
|
1299 |
|
|
* @param col The column number to paint
|
1300 |
|
|
* @param bounds The bounds of the cell to paint, assuming a coordinate
|
1301 |
|
|
* system beginning at <code>(0,0)</code> in the upper left corner of the
|
1302 |
|
|
* table
|
1303 |
|
|
* @param rend A cell renderer to paint with
|
1304 |
|
|
*/
|
1305 |
|
|
void paintCell(Graphics g, int row, int col, Rectangle bounds,
|
1306 |
|
|
TableCellRenderer rend)
|
1307 |
|
|
{
|
1308 |
|
|
Component comp = table.prepareRenderer(rend, row, col);
|
1309 |
|
|
rendererPane.paintComponent(g, comp, table, bounds);
|
1310 |
|
|
}
|
1311 |
|
|
|
1312 |
|
|
/**
|
1313 |
|
|
* Paint the associated table.
|
1314 |
|
|
*/
|
1315 |
|
|
public void paint(Graphics gfx, JComponent ignored)
|
1316 |
|
|
{
|
1317 |
|
|
int ncols = table.getColumnCount();
|
1318 |
|
|
int nrows = table.getRowCount();
|
1319 |
|
|
if (nrows == 0 || ncols == 0)
|
1320 |
|
|
return;
|
1321 |
|
|
|
1322 |
|
|
Rectangle clip = gfx.getClipBounds();
|
1323 |
|
|
|
1324 |
|
|
// Determine the range of cells that are within the clip bounds.
|
1325 |
|
|
Point p1 = new Point(clip.x, clip.y);
|
1326 |
|
|
int c0 = table.columnAtPoint(p1);
|
1327 |
|
|
if (c0 == -1)
|
1328 |
|
|
c0 = 0;
|
1329 |
|
|
int r0 = table.rowAtPoint(p1);
|
1330 |
|
|
if (r0 == -1)
|
1331 |
|
|
r0 = 0;
|
1332 |
|
|
Point p2 = new Point(clip.x + clip.width, clip.y + clip.height);
|
1333 |
|
|
int cn = table.columnAtPoint(p2);
|
1334 |
|
|
if (cn == -1)
|
1335 |
|
|
cn = table.getColumnCount() - 1;
|
1336 |
|
|
int rn = table.rowAtPoint(p2);
|
1337 |
|
|
if (rn == -1)
|
1338 |
|
|
rn = table.getRowCount() - 1;
|
1339 |
|
|
|
1340 |
|
|
int columnMargin = table.getColumnModel().getColumnMargin();
|
1341 |
|
|
int rowMargin = table.getRowMargin();
|
1342 |
|
|
|
1343 |
|
|
TableColumnModel cmodel = table.getColumnModel();
|
1344 |
|
|
int[] widths = new int[cn + 1];
|
1345 |
|
|
for (int i = c0; i <= cn; i++)
|
1346 |
|
|
{
|
1347 |
|
|
widths[i] = cmodel.getColumn(i).getWidth() - columnMargin;
|
1348 |
|
|
}
|
1349 |
|
|
|
1350 |
|
|
Rectangle bounds = table.getCellRect(r0, c0, false);
|
1351 |
|
|
// The left boundary of the area being repainted.
|
1352 |
|
|
int left = bounds.x;
|
1353 |
|
|
|
1354 |
|
|
// The top boundary of the area being repainted.
|
1355 |
|
|
int top = bounds.y;
|
1356 |
|
|
|
1357 |
|
|
// The bottom boundary of the area being repainted.
|
1358 |
|
|
int bottom;
|
1359 |
|
|
|
1360 |
|
|
// paint the cell contents
|
1361 |
|
|
Color grid = table.getGridColor();
|
1362 |
|
|
for (int r = r0; r <= rn; ++r)
|
1363 |
|
|
{
|
1364 |
|
|
for (int c = c0; c <= cn; ++c)
|
1365 |
|
|
{
|
1366 |
|
|
bounds.width = widths[c];
|
1367 |
|
|
paintCell(gfx, r, c, bounds, table.getCellRenderer(r, c));
|
1368 |
|
|
bounds.x += widths[c] + columnMargin;
|
1369 |
|
|
}
|
1370 |
|
|
bounds.x = left;
|
1371 |
|
|
bounds.y += table.getRowHeight(r);
|
1372 |
|
|
// Update row height for tables with custom heights.
|
1373 |
|
|
bounds.height = table.getRowHeight(r + 1) - rowMargin;
|
1374 |
|
|
}
|
1375 |
|
|
|
1376 |
|
|
bottom = bounds.y - rowMargin;
|
1377 |
|
|
|
1378 |
|
|
// paint vertical grid lines
|
1379 |
|
|
if (grid != null && table.getShowVerticalLines())
|
1380 |
|
|
{
|
1381 |
|
|
Color save = gfx.getColor();
|
1382 |
|
|
gfx.setColor(grid);
|
1383 |
|
|
int x = left - columnMargin;
|
1384 |
|
|
for (int c = c0; c <= cn; ++c)
|
1385 |
|
|
{
|
1386 |
|
|
// The vertical grid is draw right from the cells, so we
|
1387 |
|
|
// add before drawing.
|
1388 |
|
|
x += widths[c] + columnMargin;
|
1389 |
|
|
gfx.drawLine(x, top, x, bottom);
|
1390 |
|
|
}
|
1391 |
|
|
gfx.setColor(save);
|
1392 |
|
|
}
|
1393 |
|
|
|
1394 |
|
|
// paint horizontal grid lines
|
1395 |
|
|
if (grid != null && table.getShowHorizontalLines())
|
1396 |
|
|
{
|
1397 |
|
|
Color save = gfx.getColor();
|
1398 |
|
|
gfx.setColor(grid);
|
1399 |
|
|
int y = top - rowMargin;
|
1400 |
|
|
for (int r = r0; r <= rn; ++r)
|
1401 |
|
|
{
|
1402 |
|
|
// The horizontal grid is draw below the cells, so we
|
1403 |
|
|
// add before drawing.
|
1404 |
|
|
y += table.getRowHeight(r);
|
1405 |
|
|
gfx.drawLine(left, y, p2.x, y);
|
1406 |
|
|
}
|
1407 |
|
|
gfx.setColor(save);
|
1408 |
|
|
}
|
1409 |
|
|
}
|
1410 |
|
|
}
|