1. /*
  2. * @(#)BasicTableUI.java 1.84 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.plaf.basic;
  8. import javax.swing.table.*;
  9. import javax.swing.*;
  10. import javax.swing.event.*;
  11. import java.util.Enumeration;
  12. import java.util.Hashtable;
  13. import java.awt.event.*;
  14. import java.awt.*;
  15. import javax.swing.plaf.*;
  16. import java.util.EventObject;
  17. import javax.swing.text.*;
  18. /**
  19. * BasicTableUI implementation
  20. *
  21. * @version 1.84 11/29/01
  22. * @author Philip Milne
  23. */
  24. public class BasicTableUI extends TableUI
  25. {
  26. //
  27. // Instance Variables
  28. //
  29. // The JTable that is delegating the painting to this UI.
  30. protected JTable table;
  31. protected CellRendererPane rendererPane;
  32. // Listeners that are attached to the JTable
  33. protected KeyListener keyListener;
  34. protected FocusListener focusListener;
  35. protected MouseInputListener mouseInputListener;
  36. private Hashtable registeredKeyStrokes = new Hashtable();
  37. //
  38. // Helper class for keyboard actions
  39. //
  40. private static class NavigationalAction implements ActionListener {
  41. protected int dx;
  42. protected int dy;
  43. protected boolean toggle;
  44. protected boolean extend;
  45. protected boolean inSelection;
  46. protected int anchorRow;
  47. protected int anchorColumn;
  48. protected int leadRow;
  49. protected int leadColumn;
  50. protected NavigationalAction(int dx, int dy, boolean toggle, boolean extend,
  51. boolean inSelection) {
  52. this.dx = dx;
  53. this.dy = dy;
  54. this.toggle = toggle;
  55. this.extend = extend;
  56. this.inSelection = inSelection;
  57. }
  58. private int clipToRange(int i, int a, int b) {
  59. return Math.min(Math.max(i, a), b-1);
  60. }
  61. private void moveWithinTableRange(JTable table, int dx, int dy, boolean changeLead) {
  62. if (changeLead) {
  63. leadRow = clipToRange(leadRow+dy, 0, table.getRowCount());
  64. leadColumn = clipToRange(leadColumn+dx, 0, table.getColumnCount());
  65. }
  66. else {
  67. anchorRow = clipToRange(anchorRow+dy, 0, table.getRowCount());
  68. anchorColumn = clipToRange(anchorColumn+dx, 0, table.getColumnCount());
  69. }
  70. }
  71. private int selectionSpan(ListSelectionModel sm) {
  72. return sm.getMaxSelectionIndex() - sm.getMinSelectionIndex() + 1;
  73. }
  74. private int compare(int i, ListSelectionModel sm) {
  75. return compare(i, sm.getMinSelectionIndex(), sm.getMaxSelectionIndex()+1);
  76. }
  77. private int compare(int i, int a, int b) {
  78. return (i < a) ? -1 : (i >= b) ? 1 : 0 ;
  79. }
  80. private boolean moveWithinSelectedRange(JTable table, int dx, int dy, boolean ignoreCarry) {
  81. ListSelectionModel rsm = table.getSelectionModel();
  82. ListSelectionModel csm = table.getColumnModel().getSelectionModel();
  83. int newAnchorRow = anchorRow + dy;
  84. int newAnchorColumn = anchorColumn + dx;
  85. int rowSgn;
  86. int colSgn;
  87. int rowCount = selectionSpan(rsm);
  88. int columnCount = selectionSpan(csm);
  89. boolean canStayInSelection = (rowCount * columnCount > 1);
  90. if (canStayInSelection) {
  91. rowSgn = compare(newAnchorRow, rsm);
  92. colSgn = compare(newAnchorColumn, csm);
  93. }
  94. else {
  95. // If there is only one selected cell, there is no point
  96. // in trying to stay within the selected area. Move outside
  97. // the selection, wrapping at the table boundaries.
  98. rowCount = table.getRowCount();
  99. columnCount = table.getColumnCount();
  100. rowSgn = compare(newAnchorRow, 0, rowCount);
  101. colSgn = compare(newAnchorColumn, 0, columnCount);
  102. }
  103. anchorRow = newAnchorRow - rowCount * rowSgn;
  104. anchorColumn = newAnchorColumn - columnCount * colSgn;
  105. if (!ignoreCarry) {
  106. return moveWithinSelectedRange(table, rowSgn, colSgn, true);
  107. }
  108. return canStayInSelection;
  109. }
  110. public void actionPerformed(ActionEvent e) {
  111. JTable table = (JTable)e.getSource();
  112. ListSelectionModel rsm = table.getSelectionModel();
  113. anchorRow = rsm.getAnchorSelectionIndex();
  114. leadRow = rsm.getLeadSelectionIndex();
  115. ListSelectionModel csm = table.getColumnModel().getSelectionModel();
  116. anchorColumn = csm.getAnchorSelectionIndex();
  117. leadColumn = csm.getLeadSelectionIndex();
  118. int oldAnchorRow = anchorRow;
  119. int oldAnchorColumn = anchorColumn;
  120. if (!inSelection) {
  121. moveWithinTableRange(table, dx, dy, extend);
  122. if (!extend) {
  123. updateSelection(table, anchorRow, anchorColumn, false, extend);
  124. }
  125. else {
  126. updateSelection(table, leadRow, leadColumn, false, extend);
  127. }
  128. }
  129. else {
  130. if (moveWithinSelectedRange(table, dx, dy, false)) {
  131. rsm.setAnchorSelectionIndex(anchorRow);
  132. csm.setAnchorSelectionIndex(anchorColumn);
  133. }
  134. else {
  135. updateSelection(table, anchorRow, anchorColumn, false, false);
  136. }
  137. }
  138. if (table.isEditing() &&
  139. (oldAnchorRow != rsm.getAnchorSelectionIndex() ||
  140. oldAnchorColumn != csm.getAnchorSelectionIndex())) {
  141. table.getCellEditor().stopCellEditing();
  142. }
  143. }
  144. }
  145. private static class PagingAction extends NavigationalAction {
  146. private boolean forwards;
  147. private boolean vertically;
  148. private boolean toLimit;
  149. private PagingAction(boolean extend, boolean forwards,
  150. boolean vertically, boolean toLimit) {
  151. super(0, 0, false, extend, false);
  152. this.forwards = forwards;
  153. this.vertically = vertically;
  154. this.toLimit = toLimit;
  155. }
  156. public void actionPerformed(ActionEvent e) {
  157. JTable table = (JTable)e.getSource();
  158. if (toLimit) {
  159. if (vertically) {
  160. int rowCount = table.getRowCount();
  161. this.dx = 0;
  162. this.dy = forwards ? rowCount : -rowCount;
  163. }
  164. else {
  165. int colCount = table.getColumnCount();
  166. this.dx = forwards ? colCount : -colCount;
  167. this.dy = 0;
  168. }
  169. }
  170. else {
  171. if (!(table.getParent().getParent() instanceof JScrollPane)) {
  172. return;
  173. }
  174. Dimension delta = table.getParent().getSize();
  175. ListSelectionModel sm = (vertically)
  176. ? table.getSelectionModel()
  177. : table.getColumnModel().getSelectionModel();
  178. int start = (extend) ? sm.getLeadSelectionIndex()
  179. : sm.getAnchorSelectionIndex();
  180. if (vertically) {
  181. Rectangle r = table.getCellRect(start, 0, true);
  182. r.y += forwards ? delta.height : -delta.height;
  183. this.dx = 0;
  184. int newRow = table.rowAtPoint(r.getLocation());
  185. if (newRow == -1 && forwards) {
  186. newRow = table.getRowCount();
  187. }
  188. this.dy = newRow - start;
  189. }
  190. else {
  191. Rectangle r = table.getCellRect(0, start, true);
  192. r.x += forwards ? delta.width : -delta.width;
  193. int newColumn = table.columnAtPoint(r.getLocation());
  194. if (newColumn == -1 && forwards) {
  195. newColumn = table.getColumnCount();
  196. }
  197. this.dx = newColumn - start;
  198. this.dy = 0;
  199. }
  200. }
  201. super.actionPerformed(e);
  202. }
  203. }
  204. //
  205. // The Table's Key listener
  206. //
  207. /**
  208. * This inner class is marked "public" due to a compiler bug.
  209. * This class should be treated as a "protected" inner class.
  210. * Instantiate it only within subclasses of BasicTableUI.
  211. */
  212. public class KeyHandler implements KeyListener {
  213. public void keyPressed(KeyEvent e) { }
  214. public void keyReleased(KeyEvent e) { }
  215. public void keyTyped(KeyEvent e) {
  216. KeyStroke keyStroke = KeyStroke.getKeyStroke(e.getKeyChar(), e.getModifiers());
  217. // We register all actions using ANCESTOR_OF_FOCUSSED_COMPONENT
  218. // which means that we might perform the appropriate action
  219. // in the table and then forward it to the editor if the editor
  220. // had focus. Make sure this doesn't happen by checking our
  221. // private list of registered actions.
  222. if (registeredKeyStrokes.get(keyStroke) != null) {
  223. return;
  224. }
  225. // The AWT seems to generate an unconsumed \r event when
  226. // ENTER (\n) is pressed.
  227. if (e.getKeyChar() == '\r') {
  228. return;
  229. }
  230. int anchorRow = table.getSelectionModel().getAnchorSelectionIndex();
  231. int anchorColumn =
  232. table.getColumnModel().getSelectionModel().getAnchorSelectionIndex();
  233. if (anchorRow != -1 && anchorColumn != -1 && !table.isEditing()) {
  234. if (!table.editCellAt(anchorRow, anchorColumn)) {
  235. return;
  236. }
  237. }
  238. // Forwarding events this way seems to put the textfield
  239. // in a state where it believes it has focus. In reality
  240. // the table retains focus - though it is difficult for
  241. // a user to tell, since the caret is visible and flashing.
  242. // Calling table.requestFocus() here, to get the focus back to
  243. // the table, seems to have no effect.
  244. Component editorComp = table.getEditorComponent();
  245. if (table.isEditing() && editorComp != null) {
  246. if (editorComp instanceof JTextField) {
  247. JTextField textField = (JTextField)editorComp;
  248. Keymap keyMap = textField.getKeymap();
  249. Action action = keyMap.getAction(keyStroke);
  250. if (action == null) {
  251. action = keyMap.getDefaultAction();
  252. }
  253. if (action != null) {
  254. ActionEvent ae = new ActionEvent(textField,
  255. ActionEvent.ACTION_PERFORMED,
  256. String.valueOf(e.getKeyChar()));
  257. action.actionPerformed(ae);
  258. e.consume();
  259. }
  260. }
  261. }
  262. }
  263. }
  264. //
  265. // The Table's focus listener
  266. //
  267. /**
  268. * This inner class is marked "public" due to a compiler bug.
  269. * This class should be treated as a "protected" inner class.
  270. * Instantiate it only within subclasses of BasicTableUI.
  271. */
  272. public class FocusHandler implements FocusListener {
  273. private void repaintAnchorCell( ) {
  274. int anchorRow = table.getSelectionModel().getAnchorSelectionIndex();
  275. int anchorColumn =
  276. table.getColumnModel().getSelectionModel().getAnchorSelectionIndex();
  277. Rectangle dirtyRect = table.getCellRect(anchorRow, anchorColumn, false);
  278. table.repaint(dirtyRect);
  279. }
  280. public void focusGained(FocusEvent e) {
  281. repaintAnchorCell();
  282. }
  283. public void focusLost(FocusEvent e) {
  284. repaintAnchorCell();
  285. }
  286. }
  287. //
  288. // The Table's mouse and mouse motion listeners
  289. //
  290. /**
  291. * This inner class is marked "public" due to a compiler bug.
  292. * This class should be treated as a "protected" inner class.
  293. * Instantiate it only within subclasses of BasicTableUI.
  294. */
  295. public class MouseInputHandler implements MouseInputListener {
  296. // Component recieving mouse events during editing. May not be editorComponent.
  297. private Component dispatchComponent;
  298. // The Table's mouse listener methods.
  299. public void mouseClicked(MouseEvent e) {}
  300. private void setDispatchComponent(MouseEvent e) {
  301. Component editorComponent = table.getEditorComponent();
  302. Point p = e.getPoint();
  303. Point p2 = SwingUtilities.convertPoint(table, p, editorComponent);
  304. dispatchComponent = SwingUtilities.getDeepestComponentAt(editorComponent,
  305. p2.x, p2.y);
  306. }
  307. private boolean repostEvent(MouseEvent e) {
  308. if (dispatchComponent == null) {
  309. return false;
  310. }
  311. MouseEvent e2 = SwingUtilities.convertMouseEvent(table, e, dispatchComponent);
  312. dispatchComponent.dispatchEvent(e2);
  313. return true;
  314. }
  315. private void setValueIsAdjusting(boolean flag) {
  316. table.getSelectionModel().setValueIsAdjusting(flag);
  317. table.getColumnModel().getSelectionModel().setValueIsAdjusting(flag);
  318. }
  319. public void mousePressed(MouseEvent e) {
  320. if (!SwingUtilities.isLeftMouseButton(e)) {
  321. return;
  322. }
  323. Point p = e.getPoint();
  324. int row = table.rowAtPoint(p);
  325. int column = table.columnAtPoint(p);
  326. // The autoscroller can generate drag events outside the Table's range.
  327. if ((column == -1) || (row == -1)) {
  328. return;
  329. }
  330. if (table.editCellAt(row, column, e)) {
  331. setDispatchComponent(e);
  332. repostEvent(e);
  333. }
  334. else {
  335. table.requestFocus();
  336. }
  337. CellEditor editor = table.getCellEditor();
  338. if (editor == null || editor.shouldSelectCell(e)) {
  339. setValueIsAdjusting(true);
  340. updateSelection(table, row, column, e.isControlDown(), e.isShiftDown());
  341. }
  342. }
  343. public void mouseReleased(MouseEvent e) {
  344. if (!SwingUtilities.isLeftMouseButton(e)) {
  345. return;
  346. }
  347. repostEvent(e);
  348. dispatchComponent = null;
  349. setValueIsAdjusting(false);
  350. }
  351. public void mouseEntered(MouseEvent e) {}
  352. public void mouseExited(MouseEvent e) {}
  353. // The Table's mouse motion listener methods.
  354. public void mouseMoved(MouseEvent e) {}
  355. public void mouseDragged(MouseEvent e) {
  356. if (!SwingUtilities.isLeftMouseButton(e)) {
  357. return;
  358. }
  359. repostEvent(e);
  360. CellEditor editor = table.getCellEditor();
  361. if (editor == null || editor.shouldSelectCell(e)) {
  362. Point p = e.getPoint();
  363. int row = table.rowAtPoint(p);
  364. int column = table.columnAtPoint(p);
  365. // The autoscroller can generate drag events outside the Table's range.
  366. if ((column == -1) || (row == -1)) {
  367. return;
  368. }
  369. updateSelection(table, row, column, false, true);
  370. }
  371. }
  372. }
  373. //
  374. // Factory methods for the Listeners
  375. //
  376. /**
  377. * Creates the key listener for handling keyboard navigation in the JTable.
  378. */
  379. protected KeyListener createKeyListener() {
  380. return new KeyHandler();
  381. }
  382. /**
  383. * Creates the focus listener for handling keyboard navigation in the JTable.
  384. */
  385. protected FocusListener createFocusListener() {
  386. return new FocusHandler();
  387. }
  388. /**
  389. * Creates the mouse listener for the JTable.
  390. */
  391. protected MouseInputListener createMouseInputListener() {
  392. return new MouseInputHandler();
  393. }
  394. //
  395. // The installation/uninstall procedures and support
  396. //
  397. public static ComponentUI createUI(JComponent c) {
  398. return new BasicTableUI();
  399. }
  400. // Installation
  401. public void installUI(JComponent c) {
  402. table = (JTable)c;
  403. rendererPane = new CellRendererPane();
  404. table.add(rendererPane);
  405. installDefaults();
  406. installListeners();
  407. installKeyboardActions();
  408. }
  409. /**
  410. * Initialize JTable properties, e.g. font, foreground, and background.
  411. * The font, foreground, and background properties are only set if their
  412. * current value is either null or a UIResource, other properties are set
  413. * if the current value is null.
  414. *
  415. * @see #installUI
  416. */
  417. protected void installDefaults() {
  418. LookAndFeel.installColorsAndFont(table, "Table.background",
  419. "Table.foreground", "Table.font");
  420. Color sbg = table.getSelectionBackground();
  421. if (sbg == null || sbg instanceof UIResource) {
  422. table.setSelectionBackground(UIManager.getColor("Table.selectionBackground"));
  423. }
  424. Color sfg = table.getSelectionForeground();
  425. if (sfg == null || sfg instanceof UIResource) {
  426. table.setSelectionForeground(UIManager.getColor("Table.selectionForeground"));
  427. }
  428. Color gridColor = table.getGridColor();
  429. if (gridColor == null || gridColor instanceof UIResource) {
  430. table.setGridColor(UIManager.getColor("Table.gridColor"));
  431. }
  432. // install the scrollpane border
  433. Container parent = table.getParent(); // should be viewport
  434. if (parent != null) {
  435. parent = parent.getParent(); // should be the scrollpane
  436. if (parent != null && parent instanceof JScrollPane) {
  437. LookAndFeel.installBorder((JScrollPane)parent, "Table.scrollPaneBorder");
  438. }
  439. }
  440. }
  441. /**
  442. * Attaches listeners to the JTable.
  443. */
  444. protected void installListeners() {
  445. focusListener = createFocusListener();
  446. keyListener = createKeyListener();
  447. mouseInputListener = createMouseInputListener();
  448. table.addFocusListener(focusListener);
  449. table.addKeyListener(keyListener);
  450. table.addMouseListener(mouseInputListener);
  451. table.addMouseMotionListener(mouseInputListener);
  452. }
  453. private void registerKeyboardAction(ActionListener action, KeyStroke keyStroke) {
  454. registeredKeyStrokes.put(keyStroke, action);
  455. table.registerKeyboardAction(action, keyStroke, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
  456. }
  457. private void registerKey(int keyEvent, int mask, int dx, int dy) {
  458. boolean toggle = (mask & ActionEvent.CTRL_MASK) != 0;
  459. boolean extend = (mask & ActionEvent.SHIFT_MASK) != 0;
  460. registerKey(keyEvent, mask, dx, dy, toggle, extend, false);
  461. }
  462. private void registerKey(int keyEvent, int mask, int dx, int dy,
  463. boolean toggle, boolean extend, boolean inSelection) {
  464. registerKeyboardAction(
  465. new NavigationalAction(dx, dy, toggle, extend, inSelection),
  466. KeyStroke.getKeyStroke(keyEvent, mask));
  467. }
  468. private void registerScrollKey(int keyEvent, int mask, boolean forwards,
  469. boolean vertically, boolean toLimit) {
  470. boolean extend = (mask & ActionEvent.SHIFT_MASK) != 0;
  471. registerKeyboardAction(
  472. new PagingAction(extend, forwards, vertically, toLimit),
  473. KeyStroke.getKeyStroke(keyEvent, mask));
  474. }
  475. /* This version should only be used for events which are new
  476. * in JDK 1.2 like "shift KP_UP".
  477. */
  478. private void registerKey(String keyEvent, int dx, int dy) {
  479. // This is a less than perfect way of doing things:
  480. KeyStroke ks = KeyStroke.getKeyStroke(keyEvent);
  481. int mask = ks.getModifiers();
  482. boolean toggle = (mask & ActionEvent.CTRL_MASK) != 0;
  483. boolean extend = (mask & ActionEvent.SHIFT_MASK) != 0;
  484. registerKey(keyEvent, dx, dy, toggle, extend, false);
  485. }
  486. /* This version should only be used for events which are new
  487. * in JDK 1.2 like "shift KP_UP".
  488. */
  489. private void registerKey(String keyEvent, int dx, int dy,
  490. boolean toggle, boolean extend, boolean inSelection) {
  491. registerKeyboardAction(
  492. new NavigationalAction(dx, dy, toggle, extend, inSelection),
  493. KeyStroke.getKeyStroke(keyEvent));
  494. }
  495. /**
  496. * Register all keyboard actions on the JTable.
  497. */
  498. protected void installKeyboardActions()
  499. {
  500. int shift = ActionEvent.SHIFT_MASK;
  501. int ctrl = ActionEvent.CTRL_MASK;
  502. int cShft = shift | ctrl;
  503. registerKey(KeyEvent.VK_RIGHT, 0, 1, 0);
  504. registerKey(KeyEvent.VK_LEFT , 0, -1, 0);
  505. registerKey(KeyEvent.VK_DOWN , 0, 0, 1);
  506. registerKey(KeyEvent.VK_UP , 0, 0, -1);
  507. registerKey("KP_RIGHT", 1, 0);
  508. registerKey("KP_LEFT" , -1, 0);
  509. registerKey("KP_DOWN" , 0, 1);
  510. registerKey("KP_UP" , 0, -1);
  511. registerKey(KeyEvent.VK_RIGHT, shift, 1, 0);
  512. registerKey(KeyEvent.VK_LEFT , shift, -1, 0);
  513. registerKey(KeyEvent.VK_DOWN , shift, 0, 1);
  514. registerKey(KeyEvent.VK_UP , shift, 0, -1);
  515. registerKey("shift KP_RIGHT", 1, 0);
  516. registerKey("shift KP_LEFT" , -1, 0);
  517. registerKey("shift KP_DOWN" , 0, 1);
  518. registerKey("shift KP_UP" , 0, -1);
  519. registerScrollKey(KeyEvent.VK_PAGE_UP, 0, false, true, false);
  520. registerScrollKey(KeyEvent.VK_PAGE_DOWN, 0, true, true, false);
  521. registerScrollKey(KeyEvent.VK_HOME, 0, false, false, true);
  522. registerScrollKey(KeyEvent.VK_END, 0, true, false, true);
  523. registerScrollKey(KeyEvent.VK_PAGE_UP, shift, false, true, false);
  524. registerScrollKey(KeyEvent.VK_PAGE_DOWN, shift, true, true, false);
  525. registerScrollKey(KeyEvent.VK_HOME, shift, false, false, true);
  526. registerScrollKey(KeyEvent.VK_END, shift, true, false, true);
  527. registerScrollKey(KeyEvent.VK_PAGE_UP, ctrl, false, false, false);
  528. registerScrollKey(KeyEvent.VK_PAGE_DOWN, ctrl, true, false, false);
  529. registerScrollKey(KeyEvent.VK_HOME, ctrl, false, true, true);
  530. registerScrollKey(KeyEvent.VK_END, ctrl, true, true, true);
  531. registerScrollKey(KeyEvent.VK_PAGE_UP, cShft, false, false, false);
  532. registerScrollKey(KeyEvent.VK_PAGE_DOWN, cShft, true, false, false);
  533. registerScrollKey(KeyEvent.VK_HOME, cShft, false, true, true);
  534. registerScrollKey(KeyEvent.VK_END, cShft, true, true, true);
  535. registerKey(KeyEvent.VK_TAB , 0, 1, 0, true, false, true);
  536. registerKey(KeyEvent.VK_TAB , shift, -1, 0, true, false, true);
  537. registerKey(KeyEvent.VK_ENTER, 0, 0, 1, true, false, true);
  538. registerKey(KeyEvent.VK_ENTER, shift, 0, -1, true, false, true);
  539. registerKeyboardAction(new ActionListener() {
  540. public void actionPerformed(ActionEvent e) {
  541. table.selectAll();
  542. }
  543. }, KeyStroke.getKeyStroke(KeyEvent.VK_A, ctrl));
  544. registerKeyboardAction(new ActionListener() {
  545. public void actionPerformed(ActionEvent e) {
  546. table.removeEditor();
  547. }
  548. }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0));
  549. registerKeyboardAction(new ActionListener() {
  550. public void actionPerformed(ActionEvent e) {
  551. ListSelectionModel rsm = table.getSelectionModel();
  552. int anchorRow = rsm.getAnchorSelectionIndex();
  553. ListSelectionModel csm = table.getColumnModel().getSelectionModel();
  554. int anchorColumn = csm.getAnchorSelectionIndex();
  555. table.editCellAt(anchorRow, anchorColumn);
  556. Component other = table.hasFocus() ? table.getEditorComponent() : table;
  557. other.requestFocus();
  558. }
  559. },
  560. KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0));
  561. }
  562. // Uninstallation
  563. public void uninstallUI(JComponent c) {
  564. uninstallDefaults();
  565. uninstallListeners();
  566. uninstallKeyboardActions();
  567. table.remove(rendererPane);
  568. rendererPane = null;
  569. table = null;
  570. }
  571. protected void uninstallDefaults() {}
  572. protected void uninstallListeners() {
  573. table.removeFocusListener(focusListener);
  574. table.removeKeyListener(keyListener);
  575. table.removeMouseListener(mouseInputListener);
  576. table.removeMouseMotionListener(mouseInputListener);
  577. focusListener = null;
  578. keyListener = null;
  579. mouseInputListener = null;
  580. }
  581. protected void uninstallKeyboardActions() {
  582. Enumeration keyStrokes = registeredKeyStrokes.keys();
  583. while (keyStrokes.hasMoreElements()) {
  584. KeyStroke keyStroke = (KeyStroke)keyStrokes.nextElement();
  585. table.unregisterKeyboardAction(keyStroke);
  586. }
  587. }
  588. //
  589. // Size Methods
  590. //
  591. private Dimension createTableSize(long width) {
  592. int height = table.getRowCount() * (table.getRowHeight() +
  593. table.getRowMargin());
  594. int totalMarginWidth = table.getColumnModel().getColumnMargin() *
  595. table.getColumnCount();
  596. // Width is always positive. The call to abs() is a workaround for
  597. // a bug in the 1.1.6 JIT on Windows.
  598. long widthWithMargin = Math.abs(width) + totalMarginWidth;
  599. if (widthWithMargin > Integer.MAX_VALUE) {
  600. widthWithMargin = Integer.MAX_VALUE;
  601. }
  602. return new Dimension((int)widthWithMargin, height);
  603. }
  604. /**
  605. * Return the minimum size of the table. The minimum height is the
  606. * row height (plus inter-cell spacing) times the number of rows.
  607. * The minimum width is the sum of the minimum widths of each column
  608. * (plus inter-cell spacing).
  609. */
  610. public Dimension getMinimumSize(JComponent c) {
  611. long width = 0;
  612. Enumeration enumeration = table.getColumnModel().getColumns();
  613. while (enumeration.hasMoreElements()) {
  614. TableColumn aColumn = (TableColumn)enumeration.nextElement();
  615. width = width + aColumn.getMinWidth();
  616. }
  617. return createTableSize(width);
  618. }
  619. /**
  620. * Return the preferred size of the table. The preferred height is the
  621. * row height (plus inter-cell spacing) times the number of rows.
  622. * The preferred width is the sum of the preferred widths of each column
  623. * (plus inter-cell spacing).
  624. */
  625. public Dimension getPreferredSize(JComponent c) {
  626. long width = 0;
  627. Enumeration enumeration = table.getColumnModel().getColumns();
  628. while (enumeration.hasMoreElements()) {
  629. TableColumn aColumn = (TableColumn)enumeration.nextElement();
  630. width = width + aColumn.getPreferredWidth();
  631. }
  632. return createTableSize(width);
  633. }
  634. /**
  635. * Return the maximum size of the table. The maximum height is the
  636. * row height (plus inter-cell spacing) times the number of rows.
  637. * The maximum width is the sum of the maximum widths of each column
  638. * (plus inter-cell spacing).
  639. */
  640. public Dimension getMaximumSize(JComponent c) {
  641. long width = 0;
  642. Enumeration enumeration = table.getColumnModel().getColumns();
  643. while (enumeration.hasMoreElements()) {
  644. TableColumn aColumn = (TableColumn)enumeration.nextElement();
  645. width = width + aColumn.getMaxWidth();
  646. }
  647. return createTableSize(width);
  648. }
  649. //
  650. // Paint methods and support
  651. //
  652. /** Paint a representation of the <code>table</code> instance
  653. * that was set in installUI().
  654. */
  655. public void paint(Graphics g, JComponent c) {
  656. Rectangle oldClipBounds = g.getClipBounds();
  657. Rectangle clipBounds = new Rectangle(oldClipBounds);
  658. int tableWidth = table.getColumnModel().getTotalColumnWidth();
  659. clipBounds.width = Math.min(clipBounds.width, tableWidth);
  660. g.setClip(clipBounds);
  661. // Paint the grid
  662. paintGrid(g);
  663. // Paint the rows
  664. int firstIndex = table.rowAtPoint(new Point(0, clipBounds.y));
  665. int lastIndex = lastVisibleRow(clipBounds);
  666. Rectangle rowRect = new Rectangle(0, 0,
  667. tableWidth,
  668. table.getRowHeight() + table.getRowMargin());
  669. rowRect.y = firstIndex*rowRect.height;
  670. for (int index = firstIndex; index <= lastIndex; index++) {
  671. // Paint any rows that need to be painted
  672. if (rowRect.intersects(clipBounds)) {
  673. paintRow(g, index);
  674. }
  675. rowRect.y += rowRect.height;
  676. }
  677. g.setClip(oldClipBounds);
  678. }
  679. /*
  680. * Paints the grid lines within <I>aRect</I>, using the grid
  681. * color set with <I>setGridColor</I>. Paints vertical lines
  682. * if <code>getShowVerticalLines()</code> returns true and paints
  683. * horizontal lines if <code>getShowHorizontalLines()</code>
  684. * returns true.
  685. */
  686. private void paintGrid(Graphics g) {
  687. g.setColor(table.getGridColor());
  688. if (table.getShowHorizontalLines()) {
  689. paintHorizontalLines(g);
  690. }
  691. if (table.getShowVerticalLines()) {
  692. paintVerticalLines(g);
  693. }
  694. }
  695. /*
  696. * This method paints horizontal lines regardless of whether the
  697. * table is set to paint them automatically.
  698. */
  699. private void paintHorizontalLines(Graphics g) {
  700. Rectangle rect = g.getClipBounds();
  701. int firstIndex = table.rowAtPoint(new Point(0, rect.y));
  702. int lastIndex = lastVisibleRow(rect);
  703. int tableWidth = table.getColumnModel().getTotalColumnWidth();
  704. int delta = table.getRowHeight() + table.getRowMargin();
  705. int y = delta * firstIndex;
  706. for (int index = firstIndex; index <= lastIndex; index ++) {
  707. y += delta;
  708. g.drawLine(0, y - 1, tableWidth - 1, y - 1);
  709. }
  710. }
  711. /*
  712. * This method paints vertical lines regardless of whether the
  713. * table is set to paint them automatically.
  714. */
  715. private void paintVerticalLines(Graphics g) {
  716. int x = 0;
  717. int count = table.getColumnCount();
  718. TableColumnModel cm = table.getColumnModel();
  719. int columnMargin = cm.getColumnMargin();
  720. int rowHeight = table.getRowHeight() + table.getRowMargin();
  721. int tableHeight = rowHeight * table.getRowCount();
  722. for (int index = 0; index < count; index ++) {
  723. x += cm.getColumn(index).getWidth() + columnMargin;
  724. g.drawLine(x - 1, 0, x - 1, tableHeight - 1);
  725. }
  726. }
  727. private void paintRow(Graphics g, int row) {
  728. Rectangle rect = g.getClipBounds();
  729. int column = 0;
  730. boolean drawn = false;
  731. int draggedColumnIndex = -1;
  732. Rectangle draggedCellRect = null;
  733. Dimension spacing = table.getIntercellSpacing();
  734. JTableHeader header = table.getTableHeader();
  735. // Set up the cellRect
  736. Rectangle cellRect = new Rectangle();
  737. cellRect.height = table.getRowHeight() + spacing.height;
  738. cellRect.y = row * cellRect.height;
  739. Enumeration enumeration = table.getColumnModel().getColumns();
  740. // Paint the non-dragged table cells first
  741. while (enumeration.hasMoreElements()) {
  742. TableColumn aColumn = (TableColumn)enumeration.nextElement();
  743. cellRect.width = aColumn.getWidth() + spacing.width;
  744. if (cellRect.intersects(rect)) {
  745. drawn = true;
  746. if ((header == null) || (aColumn != header.getDraggedColumn())) {
  747. paintCell(g, cellRect, row, column);
  748. }
  749. else {
  750. // Paint a gray well in place of the moving column
  751. // This would be unnecessary if we drew the grid more cleverly
  752. g.setColor(table.getParent().getBackground());
  753. g.fillRect(cellRect.x, cellRect.y, cellRect.width, cellRect.height);
  754. draggedCellRect = new Rectangle(cellRect);
  755. draggedColumnIndex = column;
  756. }
  757. }
  758. else {
  759. if (drawn)
  760. // Don't need to iterate through the rest
  761. break;
  762. }
  763. cellRect.x += cellRect.width;
  764. column++;
  765. }
  766. // paint the dragged cell if we are dragging
  767. if (draggedColumnIndex != -1 && draggedCellRect != null) {
  768. draggedCellRect.x += header.getDraggedDistance();
  769. // Fill the background
  770. g.setColor(table.getBackground());
  771. g.fillRect(draggedCellRect.x, draggedCellRect.y,
  772. draggedCellRect.width, draggedCellRect.height);
  773. // paint grid if necessary.
  774. g.setColor(table.getGridColor());
  775. int x1 = draggedCellRect.x;
  776. int y1 = draggedCellRect.y;
  777. int x2 = x1 + draggedCellRect.width - 1;
  778. int y2 = y1 + draggedCellRect.height - 1;
  779. if (table.getShowVerticalLines()) {
  780. // Left
  781. // g.drawLine(x1-1, y1, x1-1, y2);
  782. // Right
  783. g.drawLine(x2, y1, x2, y2);
  784. }
  785. // Bottom
  786. if (table.getShowHorizontalLines()) {
  787. g.drawLine(x1, y2, x2, y2);
  788. }
  789. // Render the cell value
  790. paintCell(g, draggedCellRect, row, draggedColumnIndex);
  791. }
  792. }
  793. private void paintCell(Graphics g, Rectangle cellRect, int row, int column) {
  794. // The cellRect is inset by half the intercellSpacing before painted
  795. int spacingHeight = table.getRowMargin();
  796. int spacingWidth = table.getColumnModel().getColumnMargin();
  797. // Round so that when the spacing is 1 the cell does not paint obscure lines.
  798. cellRect.setBounds(cellRect.x + spacingWidth2, cellRect.y + spacingHeight2,
  799. cellRect.width - spacingWidth, cellRect.height - spacingHeight);
  800. if (table.isEditing() && table.getEditingRow()==row &&
  801. table.getEditingColumn()==column) {
  802. Component component = table.getEditorComponent();
  803. component.setBounds(cellRect);
  804. component.validate();
  805. }
  806. else {
  807. TableCellRenderer renderer = table.getCellRenderer(row, column);
  808. Component component = table.prepareRenderer(renderer, row, column);
  809. if (component.getParent() == null) {
  810. rendererPane.add(component);
  811. }
  812. rendererPane.paintComponent(g, component, table, cellRect.x, cellRect.y,
  813. cellRect.width, cellRect.height, true);
  814. }
  815. // Have to restore the cellRect back to it's orginial size
  816. cellRect.setBounds(cellRect.x - spacingWidth2, cellRect.y - spacingHeight2,
  817. cellRect.width + spacingWidth, cellRect.height + spacingHeight);
  818. }
  819. private int lastVisibleRow(Rectangle clip) {
  820. int lastIndex = table.rowAtPoint(new Point(0, clip.y + clip.height - 1));
  821. // If the table does not have enough rows to fill the view we'll get -1.
  822. // Replace this with the index of the last row.
  823. if (lastIndex == -1) {
  824. lastIndex = table.getRowCount() -1;
  825. }
  826. return lastIndex;
  827. }
  828. private static void updateSelectionModel(ListSelectionModel sm, int index,
  829. boolean toggle, boolean extend) {
  830. if (!extend) {
  831. if (!toggle) {
  832. sm.setSelectionInterval(index, index);
  833. }
  834. else {
  835. if (sm.isSelectedIndex(index)) {
  836. sm.removeSelectionInterval(index, index);
  837. }
  838. else {
  839. sm.addSelectionInterval(index, index);
  840. }
  841. }
  842. }
  843. else {
  844. sm.setLeadSelectionIndex(index);
  845. }
  846. }
  847. private static void updateSelection(JTable table, int rowIndex, int columnIndex,
  848. boolean toggle, boolean extend) {
  849. // Autoscrolling support.
  850. Rectangle cellRect = table.getCellRect(rowIndex, columnIndex, false);
  851. if (cellRect != null) {
  852. table.scrollRectToVisible(cellRect);
  853. }
  854. ListSelectionModel rsm = table.getSelectionModel();
  855. ListSelectionModel csm = table.getColumnModel().getSelectionModel();
  856. // Update column selection model
  857. updateSelectionModel(csm, columnIndex, toggle, extend);
  858. // Update row selection model
  859. updateSelectionModel(rsm, rowIndex, toggle, extend);
  860. }
  861. } // End of Class BasicTableUI