Source for javax.swing.plaf.basic.BasicTableUI

   1: /* 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.ActionListener;
  50: import java.awt.event.FocusEvent;
  51: import java.awt.event.FocusListener;
  52: import java.awt.event.KeyEvent;
  53: import java.awt.event.KeyListener;
  54: import java.awt.event.MouseEvent;
  55: import java.beans.PropertyChangeEvent;
  56: import java.beans.PropertyChangeListener;
  57: 
  58: import javax.swing.AbstractAction;
  59: import javax.swing.ActionMap;
  60: import javax.swing.CellRendererPane;
  61: import javax.swing.DefaultListSelectionModel;
  62: import javax.swing.InputMap;
  63: import javax.swing.JComponent;
  64: import javax.swing.JTable;
  65: import javax.swing.JTextField;
  66: import javax.swing.KeyStroke;
  67: import javax.swing.ListSelectionModel;
  68: import javax.swing.LookAndFeel;
  69: import javax.swing.UIManager;
  70: import javax.swing.border.Border;
  71: import javax.swing.event.ChangeEvent;
  72: import javax.swing.event.MouseInputListener;
  73: import javax.swing.plaf.ActionMapUIResource;
  74: import javax.swing.plaf.ComponentUI;
  75: import javax.swing.plaf.InputMapUIResource;
  76: import javax.swing.plaf.TableUI;
  77: import javax.swing.table.TableCellRenderer;
  78: import javax.swing.table.TableColumn;
  79: import javax.swing.table.TableColumnModel;
  80: import javax.swing.table.TableModel;
  81: 
  82: public class BasicTableUI extends TableUI
  83: {
  84:   public static ComponentUI createUI(JComponent comp) 
  85:   {
  86:     return new BasicTableUI();
  87:   }
  88: 
  89:   protected FocusListener focusListener;  
  90:   protected KeyListener keyListener;   
  91:   protected MouseInputListener    mouseInputListener;   
  92:   protected CellRendererPane rendererPane;   
  93:   protected JTable table;
  94: 
  95:   /** The normal cell border. */
  96:   Border cellBorder;
  97: 
  98:   /** The action bound to KeyStrokes. */
  99:   TableAction action;
 100: 
 101:   /**
 102:    * Listens for changes to the tables properties.
 103:    */
 104:   private PropertyChangeListener propertyChangeListener;
 105: 
 106:   /**
 107:    * Handles key events for the JTable. Key events should be handled through
 108:    * the InputMap/ActionMap mechanism since JDK1.3. This class is only there
 109:    * for backwards compatibility.
 110:    * 
 111:    * @author Roman Kennke (kennke@aicas.com)
 112:    */
 113:   public class KeyHandler implements KeyListener
 114:   {
 115: 
 116:     /**
 117:      * Receives notification that a key has been pressed and released.
 118:      *
 119:      * @param event the key event
 120:      */
 121:     public void keyTyped(KeyEvent event)
 122:     {
 123:       // Key events should be handled through the InputMap/ActionMap mechanism
 124:       // since JDK1.3. This class is only there for backwards compatibility.
 125:     }
 126: 
 127:     /**
 128:      * Receives notification that a key has been pressed.
 129:      *
 130:      * @param event the key event
 131:      */
 132:     public void keyPressed(KeyEvent event)
 133:     {
 134:       // Key events should be handled through the InputMap/ActionMap mechanism
 135:       // since JDK1.3. This class is only there for backwards compatibility.
 136:     }
 137: 
 138:     /**
 139:      * Receives notification that a key has been released.
 140:      *
 141:      * @param event the key event
 142:      */
 143:     public void keyReleased(KeyEvent event)
 144:     {
 145:       // Key events should be handled through the InputMap/ActionMap mechanism
 146:       // since JDK1.3. This class is only there for backwards compatibility.
 147:     }
 148:   }
 149: 
 150:   public class FocusHandler implements FocusListener
 151:   {
 152:     public void focusGained(FocusEvent e) 
 153:     {
 154:       // TODO: Implement this properly.
 155:     }
 156: 
 157:     public void focusLost(FocusEvent e) 
 158:     {
 159:       // TODO: Implement this properly.
 160:     }
 161:   }
 162: 
 163:   public class MouseInputHandler implements MouseInputListener
 164:   {
 165:     Point begin, curr;
 166: 
 167:     private void updateSelection(boolean controlPressed)
 168:     {
 169:       // Update the rows
 170:       int lo_row = table.rowAtPoint(begin);
 171:       int hi_row  = table.rowAtPoint(curr);
 172:       ListSelectionModel rowModel = table.getSelectionModel();
 173:       if (lo_row != -1 && hi_row != -1)
 174:         {
 175:           if (controlPressed && rowModel.getSelectionMode() 
 176:               != ListSelectionModel.SINGLE_SELECTION)
 177:             rowModel.addSelectionInterval(lo_row, hi_row);
 178:           else
 179:             rowModel.setSelectionInterval(lo_row, hi_row);
 180:         }
 181:       
 182:       // Update the columns
 183:       int lo_col = table.columnAtPoint(begin);
 184:       int hi_col = table.columnAtPoint(curr);
 185:       ListSelectionModel colModel = table.getColumnModel().
 186:         getSelectionModel();
 187:       if (lo_col != -1 && hi_col != -1)
 188:         {
 189:           if (controlPressed && colModel.getSelectionMode() != 
 190:               ListSelectionModel.SINGLE_SELECTION)
 191:             colModel.addSelectionInterval(lo_col, hi_col);
 192:           else
 193:             colModel.setSelectionInterval(lo_col, hi_col);
 194:         }
 195:     }
 196: 
 197:     public void mouseClicked(MouseEvent e) 
 198:     {
 199:       // TODO: What should be done here, if anything?
 200:     }
 201: 
 202:     public void mouseDragged(MouseEvent e) 
 203:     {
 204:       if (table.isEnabled())
 205:         {
 206:           curr = new Point(e.getX(), e.getY());
 207:           updateSelection(e.isControlDown());
 208:         }
 209:     }
 210: 
 211:     public void mouseEntered(MouseEvent e) 
 212:     {
 213:       // TODO: What should be done here, if anything?
 214:     }
 215: 
 216:     public void mouseExited(MouseEvent e) 
 217:     {
 218:       // TODO: What should be done here, if anything?
 219:     }
 220: 
 221:     public void mouseMoved(MouseEvent e) 
 222:     {
 223:       // TODO: What should be done here, if anything?
 224:     }
 225: 
 226:     public void mousePressed(MouseEvent e) 
 227:     {
 228:       if (table.isEnabled())
 229:         {
 230:           ListSelectionModel rowModel = table.getSelectionModel();
 231:           ListSelectionModel colModel = table.getColumnModel().getSelectionModel();
 232:           int rowLead = rowModel.getLeadSelectionIndex();
 233:           int colLead = colModel.getLeadSelectionIndex();
 234: 
 235:           begin = new Point(e.getX(), e.getY());
 236:           curr = new Point(e.getX(), e.getY());
 237:           //if control is pressed and the cell is already selected, deselect it
 238:           if (e.isControlDown() && table.
 239:               isCellSelected(table.rowAtPoint(begin),table.columnAtPoint(begin)))
 240:             {                                       
 241:               table.getSelectionModel().
 242:               removeSelectionInterval(table.rowAtPoint(begin), 
 243:                                       table.rowAtPoint(begin));
 244:               table.getColumnModel().getSelectionModel().
 245:               removeSelectionInterval(table.columnAtPoint(begin), 
 246:                                       table.columnAtPoint(begin));
 247:             }
 248:           else
 249:             updateSelection(e.isControlDown());
 250: 
 251:           // If we were editing, but the moved to another cell, stop editing
 252:           if (rowLead != rowModel.getLeadSelectionIndex() ||
 253:               colLead != colModel.getLeadSelectionIndex())
 254:             if (table.isEditing())
 255:               table.editingStopped(new ChangeEvent(e));
 256:         }
 257:     }
 258: 
 259:     public void mouseReleased(MouseEvent e) 
 260:     {
 261:       if (table.isEnabled())
 262:         {
 263:           begin = null;
 264:           curr = null;
 265:         }
 266:     }
 267:   }
 268: 
 269:   /**
 270:    * Listens for changes to the model property of the JTable and adjusts some
 271:    * settings.
 272:    *
 273:    * @author Roman Kennke (kennke@aicas.com)
 274:    */
 275:   private class PropertyChangeHandler implements PropertyChangeListener
 276:   {
 277:     /**
 278:      * Receives notification if one of the JTable's properties changes.
 279:      *
 280:      * @param ev the property change event
 281:      */
 282:     public void propertyChange(PropertyChangeEvent ev)
 283:     {
 284:       String propName = ev.getPropertyName();
 285:       if (propName.equals("model"))
 286:         {
 287:           ListSelectionModel rowSel = table.getSelectionModel();
 288:           rowSel.clearSelection();
 289:           ListSelectionModel colSel = table.getColumnModel().getSelectionModel();
 290:           colSel.clearSelection();
 291:           TableModel model = table.getModel();
 292: 
 293:           // Adjust lead and anchor selection indices of the row and column
 294:           // selection models.
 295:           if (model.getRowCount() > 0)
 296:             {
 297:               rowSel.setAnchorSelectionIndex(0);
 298:               rowSel.setLeadSelectionIndex(0);
 299:             }
 300:           else
 301:             {
 302:               rowSel.setAnchorSelectionIndex(-1);
 303:               rowSel.setLeadSelectionIndex(-1);
 304:             }
 305:           if (model.getColumnCount() > 0)
 306:             {
 307:               colSel.setAnchorSelectionIndex(0);
 308:               colSel.setLeadSelectionIndex(0);
 309:             }
 310:           else
 311:             {
 312:               colSel.setAnchorSelectionIndex(-1);
 313:               colSel.setLeadSelectionIndex(-1);
 314:             }
 315:         }
 316:     }
 317:   }
 318: 
 319:   protected FocusListener createFocusListener() 
 320:   {
 321:     return new FocusHandler();
 322:   }
 323: 
 324:   protected MouseInputListener createMouseInputListener() 
 325:   {
 326:     return new MouseInputHandler();
 327:   }
 328: 
 329: 
 330:   /**
 331:    * Creates and returns a key listener for the JTable.
 332:    *
 333:    * @return a key listener for the JTable
 334:    */
 335:   protected KeyListener createKeyListener()
 336:   {
 337:     return new KeyHandler();
 338:   }
 339: 
 340:   /**
 341:    * Return the maximum size of the table. The maximum height is the row 
 342:     * height times the number of rows. The maximum width is the sum of 
 343:     * the maximum widths of each column.
 344:     * 
 345:     *  @param comp the component whose maximum size is being queried,
 346:     *  this is ignored.
 347:     *  @return a Dimension object representing the maximum size of the table,
 348:     *  or null if the table has no elements.
 349:    */
 350:   public Dimension getMaximumSize(JComponent comp) 
 351:   {
 352:     int maxTotalColumnWidth = 0;
 353:     for (int i = 0; i < table.getColumnCount(); i++)
 354:       maxTotalColumnWidth += table.getColumnModel().getColumn(i).getMaxWidth();
 355:     if (maxTotalColumnWidth == 0 || table.getRowCount() == 0)
 356:       return null;
 357:     return new Dimension(maxTotalColumnWidth, table.getRowCount()*table.getRowHeight());
 358:   }
 359: 
 360:   /**
 361:    * Return the minimum size of the table. The minimum height is the row 
 362:     * height times the number of rows. The minimum width is the sum of 
 363:     * the minimum widths of each column.
 364:     * 
 365:     *  @param comp the component whose minimum size is being queried,
 366:     *  this is ignored.
 367:     *  @return a Dimension object representing the minimum size of the table,
 368:     *  or null if the table has no elements.
 369:    */
 370:   public Dimension getMinimumSize(JComponent comp) 
 371:   {
 372:     int minTotalColumnWidth = 0;
 373:     for (int i = 0; i < table.getColumnCount(); i++)
 374:       minTotalColumnWidth += table.getColumnModel().getColumn(i).getMinWidth();
 375:     if (minTotalColumnWidth == 0 || table.getRowCount() == 0)
 376:       return null;
 377:     return new Dimension(minTotalColumnWidth, table.getRowCount()*table.getRowHeight());
 378:   }
 379: 
 380:   public Dimension getPreferredSize(JComponent comp) 
 381:   {
 382:     int width = table.getColumnModel().getTotalColumnWidth();
 383:     int height = table.getRowCount() * table.getRowHeight();
 384:     return new Dimension(width, height);
 385:   }
 386: 
 387:   protected void installDefaults() 
 388:   {
 389:     LookAndFeel.installColorsAndFont(table, "Table.background",
 390:                                      "Table.foreground", "Table.font");
 391:     table.setGridColor(UIManager.getColor("Table.gridColor"));
 392:     table.setSelectionForeground(UIManager.getColor("Table.selectionForeground"));
 393:     table.setSelectionBackground(UIManager.getColor("Table.selectionBackground"));
 394:     table.setOpaque(true);
 395:     rendererPane = new CellRendererPane();
 396:   }
 397: 
 398:   protected void installKeyboardActions() 
 399:   {
 400:     InputMap ancestorMap = (InputMap) UIManager.get("Table.ancestorInputMap");
 401:     InputMapUIResource parentInputMap = new InputMapUIResource();
 402:     // FIXME: The JDK uses a LazyActionMap for parentActionMap
 403:     ActionMap parentActionMap = new ActionMapUIResource();
 404:     action = new TableAction();
 405:     Object keys[] = ancestorMap.allKeys();
 406:     // Register key bindings in the UI InputMap-ActionMap pair
 407:     for (int i = 0; i < keys.length; i++)
 408:       {
 409:         KeyStroke stroke = (KeyStroke)keys[i];
 410:         String actionString = (String) ancestorMap.get(stroke);
 411: 
 412:         parentInputMap.put(KeyStroke.getKeyStroke(stroke.getKeyCode(),
 413:                                                   stroke.getModifiers()),
 414:                            actionString);
 415: 
 416:         parentActionMap.put (actionString, 
 417:                              new ActionListenerProxy (action, actionString));
 418: 
 419:       }
 420:     // Set the UI InputMap-ActionMap pair to be the parents of the
 421:     // JTable's InputMap-ActionMap pair
 422:     parentInputMap.setParent
 423:       (table.getInputMap
 424:        (JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).getParent());
 425:     parentActionMap.setParent(table.getActionMap().getParent());
 426:     table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
 427:       setParent(parentInputMap);
 428:     table.getActionMap().setParent(parentActionMap);
 429:   }
 430: 
 431:   /**
 432:    * This class is used to mimmic the behaviour of the JDK when registering
 433:    * keyboard actions.  It is the same as the private class used in JComponent
 434:    * for the same reason.  This class receives an action event and dispatches
 435:    * it to the true receiver after altering the actionCommand property of the
 436:    * event.
 437:    */
 438:   private static class ActionListenerProxy
 439:     extends AbstractAction
 440:   {
 441:     ActionListener target;
 442:     String bindingCommandName;
 443: 
 444:     public ActionListenerProxy(ActionListener li, 
 445:                                String cmd)
 446:     {
 447:       target = li;
 448:       bindingCommandName = cmd;
 449:     }
 450: 
 451:     public void actionPerformed(ActionEvent e)
 452:     {
 453:       ActionEvent derivedEvent = new ActionEvent(e.getSource(),
 454:                                                  e.getID(),
 455:                                                  bindingCommandName,
 456:                                                  e.getModifiers());
 457:       target.actionPerformed(derivedEvent);
 458:     }
 459:   }
 460: 
 461:   /**
 462:    * This class implements the actions that we want to happen
 463:    * when specific keys are pressed for the JTable.  The actionPerformed
 464:    * method is called when a key that has been registered for the JTable
 465:    * is received.
 466:    */
 467:   class TableAction extends AbstractAction
 468:   {
 469:     /**
 470:      * What to do when this action is called.
 471:      *
 472:      * @param e the ActionEvent that caused this action.
 473:      */
 474:     public void actionPerformed (ActionEvent e)
 475:     {
 476:       DefaultListSelectionModel rowModel = (DefaultListSelectionModel) table.getSelectionModel();
 477:       DefaultListSelectionModel colModel = (DefaultListSelectionModel) table.getColumnModel().getSelectionModel();
 478: 
 479:       int rowLead = rowModel.getLeadSelectionIndex();
 480:       int rowMax = table.getModel().getRowCount() - 1;
 481:       
 482:       int colLead = colModel.getLeadSelectionIndex();
 483:       int colMax = table.getModel().getColumnCount() - 1;
 484:       
 485:       String command = e.getActionCommand();
 486:       
 487:       if (command.equals("selectPreviousRowExtendSelection"))
 488:         {
 489:           rowModel.setLeadSelectionIndex(Math.max(rowLead - 1, 0));
 490:           colModel.setLeadSelectionIndex(colLead);
 491:         }
 492:       else if (command.equals("selectLastColumn"))
 493:         {
 494:           rowModel.setSelectionInterval(rowLead, rowLead);
 495:           colModel.setSelectionInterval(colMax, colMax);
 496:         }
 497:       else if (command.equals("startEditing"))
 498:         {
 499:           if (table.isCellEditable(rowLead, colLead))
 500:             table.editCellAt(rowLead,colLead);
 501:         }
 502:       else if (command.equals("selectFirstRowExtendSelection"))
 503:         {              
 504:           rowModel.setLeadSelectionIndex(0);
 505:           colModel.setLeadSelectionIndex(colLead);
 506:         }
 507:       else if (command.equals("selectFirstColumn"))
 508:         {
 509:           rowModel.setSelectionInterval(rowLead, rowLead);
 510:           colModel.setSelectionInterval(0, 0);
 511:         }
 512:       else if (command.equals("selectFirstColumnExtendSelection"))
 513:         {
 514:           colModel.setLeadSelectionIndex(0);
 515:           rowModel.setLeadSelectionIndex(rowLead);
 516:         }      
 517:       else if (command.equals("selectLastRow"))
 518:         {
 519:           rowModel.setSelectionInterval(rowMax,rowMax);
 520:           colModel.setSelectionInterval(colLead, colLead);
 521:         }
 522:       else if (command.equals("selectNextRowExtendSelection"))
 523:         {
 524:           rowModel.setLeadSelectionIndex(Math.min(rowLead + 1, rowMax));
 525:           colModel.setLeadSelectionIndex(colLead);
 526:         }
 527:       else if (command.equals("selectFirstRow"))
 528:         {
 529:           rowModel.setSelectionInterval(0,0);
 530:           colModel.setSelectionInterval(colLead, colLead);
 531:         }
 532:       else if (command.equals("selectNextColumnExtendSelection"))
 533:         {
 534:           colModel.setLeadSelectionIndex(Math.min(colLead + 1, colMax));
 535:           rowModel.setLeadSelectionIndex(rowLead);
 536:         }
 537:       else if (command.equals("selectLastColumnExtendSelection"))
 538:         {
 539:           colModel.setLeadSelectionIndex(colMax);
 540:           rowModel.setLeadSelectionIndex(rowLead);
 541:         }
 542:       else if (command.equals("selectPreviousColumnExtendSelection"))
 543:         {
 544:           colModel.setLeadSelectionIndex(Math.max(colLead - 1, 0));
 545:           rowModel.setLeadSelectionIndex(rowLead);
 546:         }
 547:       else if (command.equals("selectNextRow"))
 548:         {
 549:           rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax),
 550:                                         Math.min(rowLead + 1, rowMax));
 551:           colModel.setSelectionInterval(colLead,colLead);
 552:         }
 553:       else if (command.equals("scrollUpExtendSelection"))
 554:         {
 555:           int target;
 556:           if (rowLead == getFirstVisibleRowIndex())
 557:             target = Math.max
 558:               (0, rowLead - (getLastVisibleRowIndex() - 
 559:                              getFirstVisibleRowIndex() + 1));
 560:           else
 561:             target = getFirstVisibleRowIndex();
 562:           
 563:           rowModel.setLeadSelectionIndex(target);
 564:           colModel.setLeadSelectionIndex(colLead);
 565:         }
 566:       else if (command.equals("selectPreviousRow"))
 567:         {
 568:           rowModel.setSelectionInterval(Math.max(rowLead - 1, 0),
 569:                                         Math.max(rowLead - 1, 0));
 570:           colModel.setSelectionInterval(colLead,colLead);
 571:         }
 572:       else if (command.equals("scrollRightChangeSelection"))
 573:         {
 574:           int target;
 575:           if (colLead == getLastVisibleColumnIndex())
 576:             target = Math.min
 577:               (colMax, colLead + (getLastVisibleColumnIndex() -
 578:                                   getFirstVisibleColumnIndex() + 1));
 579:           else
 580:             target = getLastVisibleColumnIndex();
 581:           
 582:           colModel.setSelectionInterval(target, target);
 583:           rowModel.setSelectionInterval(rowLead, rowLead);
 584:         }
 585:       else if (command.equals("selectPreviousColumn"))
 586:         {
 587:           rowModel.setSelectionInterval(rowLead,rowLead);
 588:           colModel.setSelectionInterval(Math.max(colLead - 1, 0),
 589:                                         Math.max(colLead - 1, 0));
 590:         }
 591:       else if (command.equals("scrollLeftChangeSelection"))
 592:         {
 593:           int target;
 594:           if (colLead == getFirstVisibleColumnIndex())
 595:             target = Math.max
 596:               (0, colLead - (getLastVisibleColumnIndex() -
 597:                              getFirstVisibleColumnIndex() + 1));
 598:           else
 599:             target = getFirstVisibleColumnIndex();
 600:           
 601:           colModel.setSelectionInterval(target, target);
 602:           rowModel.setSelectionInterval(rowLead, rowLead);
 603:         }
 604:       else if (command.equals("clearSelection"))
 605:         {
 606:           table.clearSelection();
 607:         }
 608:       else if (command.equals("cancel"))
 609:         {
 610:           // FIXME: implement other parts of "cancel" like undo-ing last
 611:           // selection.  Right now it just calls editingCancelled if
 612:           // we're currently editing.
 613:           if (table.isEditing())
 614:             table.editingCanceled(new ChangeEvent("cancel"));
 615:         }
 616:       else if (command.equals("selectNextRowCell")
 617:                || command.equals("selectPreviousRowCell")
 618:                || command.equals("selectNextColumnCell")
 619:                || command.equals("selectPreviousColumnCell"))
 620:         {
 621:           // If nothing is selected, select the first cell in the table
 622:           if (table.getSelectedRowCount() == 0 && 
 623:               table.getSelectedColumnCount() == 0)
 624:             {
 625:               rowModel.setSelectionInterval(0, 0);
 626:               colModel.setSelectionInterval(0, 0);
 627:               return;
 628:             }
 629:           
 630:           // If the lead selection index isn't selected (ie a remove operation
 631:           // happened, then set the lead to the first selected cell in the
 632:           // table
 633:           if (!table.isCellSelected(rowLead, colLead))
 634:             {
 635:               rowModel.addSelectionInterval(rowModel.getMinSelectionIndex(), 
 636:                                             rowModel.getMinSelectionIndex());
 637:               colModel.addSelectionInterval(colModel.getMinSelectionIndex(), 
 638:                                             colModel.getMinSelectionIndex());
 639:               return;
 640:             }
 641:           
 642:           // multRowsSelected and multColsSelected tell us if multiple rows or
 643:           // columns are selected, respectively
 644:           boolean multRowsSelected, multColsSelected;
 645:           multRowsSelected = table.getSelectedRowCount() > 1 &&
 646:             table.getRowSelectionAllowed();
 647:           
 648:           multColsSelected = table.getSelectedColumnCount() > 1 &&
 649:             table.getColumnSelectionAllowed();
 650:           
 651:           // If there is just one selection, select the next cell, and wrap
 652:           // when you get to the edges of the table.
 653:           if (!multColsSelected && !multRowsSelected)
 654:             {
 655:               if (command.indexOf("Column") != -1) 
 656:                 advanceSingleSelection(colModel, colMax, rowModel, rowMax, 
 657:                                        (command.equals
 658:                                         ("selectPreviousColumnCell")));
 659:               else
 660:                 advanceSingleSelection(rowModel, rowMax, colModel, colMax, 
 661:                                        (command.equals 
 662:                                         ("selectPreviousRowCell")));
 663:               return;
 664:             }
 665:           
 666:           
 667:           // rowMinSelected and rowMaxSelected are the minimum and maximum
 668:           // values respectively of selected cells in the row selection model
 669:           // Similarly for colMinSelected and colMaxSelected.
 670:           int rowMaxSelected = table.getRowSelectionAllowed() ? 
 671:             rowModel.getMaxSelectionIndex() : table.getModel().getRowCount() - 1;
 672:           int rowMinSelected = table.getRowSelectionAllowed() ? 
 673:             rowModel.getMinSelectionIndex() : 0; 
 674:           int colMaxSelected = table.getColumnSelectionAllowed() ? 
 675:             colModel.getMaxSelectionIndex() : 
 676:             table.getModel().getColumnCount() - 1;
 677:           int colMinSelected = table.getColumnSelectionAllowed() ? 
 678:             colModel.getMinSelectionIndex() : 0;
 679:           
 680:           // If there are multiple rows and columns selected, select the next
 681:           // cell and wrap at the edges of the selection.  
 682:           if (command.indexOf("Column") != -1) 
 683:             advanceMultipleSelection(colModel, colMinSelected, colMaxSelected, 
 684:                                      rowModel, rowMinSelected, rowMaxSelected, 
 685:                                      (command.equals
 686:                                       ("selectPreviousColumnCell")), true);
 687:           
 688:           else
 689:             advanceMultipleSelection(rowModel, rowMinSelected, rowMaxSelected, 
 690:                                      colModel, colMinSelected, colMaxSelected, 
 691:                                      (command.equals 
 692:                                       ("selectPreviousRowCell")), false);
 693:         }
 694:       else if (command.equals("selectNextColumn"))
 695:         {
 696:           rowModel.setSelectionInterval(rowLead,rowLead);
 697:           colModel.setSelectionInterval(Math.min(colLead + 1, colMax),
 698:                                         Math.min(colLead + 1, colMax));
 699:         }
 700:       else if (command.equals("scrollLeftExtendSelection"))
 701:         {
 702:           int target;
 703:           if (colLead == getFirstVisibleColumnIndex())
 704:             target = Math.max
 705:               (0, colLead - (getLastVisibleColumnIndex() -
 706:                              getFirstVisibleColumnIndex() + 1));
 707:           else
 708:             target = getFirstVisibleColumnIndex();
 709:           
 710:           colModel.setLeadSelectionIndex(target);
 711:           rowModel.setLeadSelectionIndex(rowLead);
 712:         }
 713:       else if (command.equals("scrollDownChangeSelection"))
 714:         {
 715:           int target;
 716:           if (rowLead == getLastVisibleRowIndex())
 717:             target = Math.min
 718:               (rowMax, rowLead + (getLastVisibleRowIndex() - 
 719:                                   getFirstVisibleRowIndex() + 1));
 720:           else
 721:             target = getLastVisibleRowIndex();
 722:           
 723:           rowModel.setSelectionInterval(target, target);
 724:           colModel.setSelectionInterval(colLead, colLead);
 725:         }
 726:       else if (command.equals("scrollRightExtendSelection"))
 727:         {
 728:           int target;
 729:           if (colLead == getLastVisibleColumnIndex())
 730:             target = Math.min
 731:               (colMax, colLead + (getLastVisibleColumnIndex() -
 732:                                   getFirstVisibleColumnIndex() + 1));
 733:           else
 734:             target = getLastVisibleColumnIndex();
 735:           
 736:           colModel.setLeadSelectionIndex(target);
 737:           rowModel.setLeadSelectionIndex(rowLead);
 738:         }
 739:       else if (command.equals("selectAll"))
 740:         {
 741:           table.selectAll();
 742:         }
 743:       else if (command.equals("selectLastRowExtendSelection"))
 744:         {
 745:           rowModel.setLeadSelectionIndex(rowMax);
 746:           colModel.setLeadSelectionIndex(colLead);
 747:         }
 748:       else if (command.equals("scrollDownExtendSelection"))
 749:         {
 750:           int target;
 751:           if (rowLead == getLastVisibleRowIndex())
 752:             target = Math.min
 753:               (rowMax, rowLead + (getLastVisibleRowIndex() - 
 754:                                   getFirstVisibleRowIndex() + 1));
 755:           else
 756:             target = getLastVisibleRowIndex();
 757:           
 758:           rowModel.setLeadSelectionIndex(target);
 759:           colModel.setLeadSelectionIndex(colLead);
 760:         }      
 761:       else if (command.equals("scrollUpChangeSelection"))
 762:         {
 763:           int target;
 764:           if (rowLead == getFirstVisibleRowIndex())
 765:             target = Math.max
 766:               (0, rowLead - (getLastVisibleRowIndex() - 
 767:                              getFirstVisibleRowIndex() + 1));
 768:           else
 769:             target = getFirstVisibleRowIndex();
 770:           
 771:           rowModel.setSelectionInterval(target, target);
 772:           colModel.setSelectionInterval(colLead, colLead);
 773:         }
 774:       else if (command.equals("selectNextRowChangeLead"))
 775:           {
 776:             if (rowModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
 777:               {
 778:                 // just "selectNextRow"
 779:                 rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax),
 780:                                               Math.min(rowLead + 1, rowMax));
 781:                 colModel.setSelectionInterval(colLead,colLead);
 782:               }
 783:             else
 784:               rowModel.moveLeadSelectionIndex(Math.min(rowLead + 1, rowMax));
 785:           }
 786:       else if (command.equals("selectPreviousRowChangeLead"))
 787:         {
 788:           if (rowModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
 789:             {
 790:               // just selectPreviousRow
 791:               rowModel.setSelectionInterval(Math.max(rowLead - 1, 0),
 792:                                             Math.min(rowLead -1, 0));
 793:               colModel.setSelectionInterval(colLead,colLead);
 794:             }
 795:           else
 796:             rowModel.moveLeadSelectionIndex(Math.max(rowLead - 1, 0));
 797:         }
 798:       else if (command.equals("selectNextColumnChangeLead"))
 799:         {
 800:           if (colModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)            
 801:             {
 802:               // just selectNextColumn
 803:               rowModel.setSelectionInterval(rowLead,rowLead);
 804:               colModel.setSelectionInterval(Math.min(colLead + 1, colMax),
 805:                                             Math.min(colLead + 1, colMax));
 806:             }
 807:           else
 808:             colModel.moveLeadSelectionIndex(Math.min(colLead + 1, colMax));
 809:         }
 810:       else if (command.equals("selectPreviousColumnChangeLead"))
 811:         {
 812:           if (colModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)            
 813:             {
 814:               // just selectPreviousColumn
 815:               rowModel.setSelectionInterval(rowLead,rowLead);
 816:               colModel.setSelectionInterval(Math.max(colLead - 1, 0),
 817:                                             Math.max(colLead - 1, 0));
 818:               
 819:             }
 820:           else
 821:             colModel.moveLeadSelectionIndex(Math.max(colLead - 1, 0));
 822:         }
 823:       else if (command.equals("addToSelection"))
 824:           {
 825:             if (!table.isEditing())
 826:               {
 827:                 int oldRowAnchor = rowModel.getAnchorSelectionIndex();
 828:                 int oldColAnchor = colModel.getAnchorSelectionIndex();
 829:                 rowModel.addSelectionInterval(rowLead, rowLead);
 830:                 colModel.addSelectionInterval(colLead, colLead);
 831:                 rowModel.setAnchorSelectionIndex(oldRowAnchor);
 832:                 colModel.setAnchorSelectionIndex(oldColAnchor);
 833:               }
 834:           }
 835:       else if (command.equals("extendTo"))
 836:         {
 837:           rowModel.setSelectionInterval(rowModel.getAnchorSelectionIndex(),
 838:                                         rowLead);
 839:           colModel.setSelectionInterval(colModel.getAnchorSelectionIndex(),
 840:                                         colLead);
 841:         }
 842:       else if (command.equals("toggleAndAnchor"))
 843:         {
 844:           if (rowModel.isSelectedIndex(rowLead))
 845:             rowModel.removeSelectionInterval(rowLead, rowLead);
 846:           else
 847:             rowModel.addSelectionInterval(rowLead, rowLead);
 848:           
 849:           if (colModel.isSelectedIndex(colLead))
 850:             colModel.removeSelectionInterval(colLead, colLead);
 851:           else
 852:             colModel.addSelectionInterval(colLead, colLead);
 853:           
 854:           rowModel.setAnchorSelectionIndex(rowLead);
 855:           colModel.setAnchorSelectionIndex(colLead);
 856:         }
 857:       else 
 858:         {
 859:           // If we're here that means we bound this TableAction class
 860:           // to a keyboard input but we either want to ignore that input
 861:           // or we just haven't implemented its action yet.
 862:           
 863:           // Uncomment the following line to print the names of unused bindings
 864:           // when their keys are pressed
 865:           
 866:           // System.out.println ("not implemented: "+e.getActionCommand());
 867:         }
 868: 
 869:       // Any commands whose keyStrokes should be used by the Editor should not
 870:       // cause editing to be stopped: ie, the SPACE sends "addToSelection" but 
 871:       // if the table is in editing mode, the space should not cause us to stop
 872:       // editing because it should be used by the Editor.
 873:       if (table.isEditing() && command != "startEditing"
 874:           && command != "addToSelection")
 875:         table.editingStopped(new ChangeEvent("update"));
 876:             
 877:       table.scrollRectToVisible
 878:         (table.getCellRect(rowModel.getLeadSelectionIndex(), 
 879:                            colModel.getLeadSelectionIndex(), false));
 880:       table.repaint();
 881:     }
 882:     
 883:     /**
 884:      * Returns the column index of the first visible column.
 885:      * @return the column index of the first visible column.
 886:      */
 887:     int getFirstVisibleColumnIndex()
 888:     {
 889:       ComponentOrientation or = table.getComponentOrientation();
 890:       Rectangle r = table.getVisibleRect();
 891:       if (!or.isLeftToRight())
 892:         r.translate((int) r.getWidth() - 1, 0);
 893:       return table.columnAtPoint(r.getLocation());
 894:     }
 895:     
 896:     /**
 897:      * Returns the column index of the last visible column.
 898:      *
 899:      */
 900:     int getLastVisibleColumnIndex()
 901:     {
 902:       ComponentOrientation or = table.getComponentOrientation();
 903:       Rectangle r = table.getVisibleRect();
 904:       if (or.isLeftToRight())
 905:         r.translate((int) r.getWidth() - 1, 0);
 906:       return table.columnAtPoint(r.getLocation());      
 907:     }
 908:     
 909:     /**
 910:      * Returns the row index of the first visible row.
 911:      *
 912:      */
 913:     int getFirstVisibleRowIndex()
 914:     {
 915:       ComponentOrientation or = table.getComponentOrientation();
 916:       Rectangle r = table.getVisibleRect();
 917:       if (!or.isLeftToRight())
 918:         r.translate((int) r.getWidth() - 1, 0);
 919:       return table.rowAtPoint(r.getLocation());
 920:     }
 921:     
 922:     /**
 923:      * Returns the row index of the last visible row.
 924:      *
 925:      */
 926:     int getLastVisibleRowIndex()
 927:     {
 928:       ComponentOrientation or = table.getComponentOrientation();
 929:       Rectangle r = table.getVisibleRect();
 930:       r.translate(0, (int) r.getHeight() - 1);
 931:       if (or.isLeftToRight())
 932:         r.translate((int) r.getWidth() - 1, 0);
 933:       // The next if makes sure that we don't return -1 simply because
 934:       // there is white space at the bottom of the table (ie, the display
 935:       // area is larger than the table)
 936:       if (table.rowAtPoint(r.getLocation()) == -1)
 937:         {
 938:           if (getFirstVisibleRowIndex() == -1)
 939:             return -1;
 940:           else
 941:             return table.getModel().getRowCount() - 1;
 942:         }
 943:       return table.rowAtPoint(r.getLocation());
 944:     }
 945: 
 946:     /**
 947:      * A helper method for the key bindings.  Used because the actions
 948:      * for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar.
 949:      *
 950:      * Selects the next (previous if SHIFT pressed) column for TAB, or row for
 951:      * ENTER from within the currently selected cells.
 952:      *
 953:      * @param firstModel the ListSelectionModel for columns (TAB) or
 954:      * rows (ENTER)
 955:      * @param firstMin the first selected index in firstModel
 956:      * @param firstMax the last selected index in firstModel
 957:      * @param secondModel the ListSelectionModel for rows (TAB) or 
 958:      * columns (ENTER)
 959:      * @param secondMin the first selected index in secondModel
 960:      * @param secondMax the last selected index in secondModel
 961:      * @param reverse true if shift was held for the event
 962:      * @param eventIsTab true if TAB was pressed, false if ENTER pressed
 963:      */
 964:     void advanceMultipleSelection (ListSelectionModel firstModel, int firstMin,
 965:                                    int firstMax, ListSelectionModel secondModel, 
 966:                                    int secondMin, int secondMax, boolean reverse,
 967:                                    boolean eventIsTab)
 968:     {
 969:       // If eventIsTab, all the "firsts" correspond to columns, otherwise, to rows
 970:       // "seconds" correspond to the opposite
 971:       int firstLead = firstModel.getLeadSelectionIndex();
 972:       int secondLead = secondModel.getLeadSelectionIndex();
 973:       int numFirsts = eventIsTab ? 
 974:         table.getModel().getColumnCount() : table.getModel().getRowCount();
 975:       int numSeconds = eventIsTab ? 
 976:         table.getModel().getRowCount() : table.getModel().getColumnCount();
 977: 
 978:       // check if we have to wrap the "firsts" around, going to the other side
 979:       if ((firstLead == firstMax && !reverse) || 
 980:           (reverse && firstLead == firstMin))
 981:         {
 982:           firstModel.addSelectionInterval(reverse ? firstMax : firstMin, 
 983:                                           reverse ? firstMax : firstMin);
 984:           
 985:           // check if we have to wrap the "seconds"
 986:           if ((secondLead == secondMax && !reverse) || 
 987:               (reverse && secondLead == secondMin))
 988:             secondModel.addSelectionInterval(reverse ? secondMax : secondMin, 
 989:                                              reverse ? secondMax : secondMin);
 990: 
 991:           // if we're not wrapping the seconds, we have to find out where we
 992:           // are within the secondModel and advance to the next cell (or 
 993:           // go back to the previous cell if reverse == true)
 994:           else
 995:             {
 996:               int[] secondsSelected;
 997:               if (eventIsTab && table.getRowSelectionAllowed() || 
 998:                   !eventIsTab && table.getColumnSelectionAllowed())
 999:                 secondsSelected = eventIsTab ? 
1000:                   table.getSelectedRows() : table.getSelectedColumns();
1001:               else
1002:                 {
1003:                   // if row selection is not allowed, then the entire column gets
1004:                   // selected when you click on it, so consider ALL rows selected
1005:                   secondsSelected = new int[numSeconds];
1006:                   for (int i = 0; i < numSeconds; i++)
1007:                   secondsSelected[i] = i;
1008:                 }
1009: 
1010:               // and now find the "next" index within the model
1011:               int secondIndex = reverse ? secondsSelected.length - 1 : 0;
1012:               if (!reverse)
1013:                 while (secondsSelected[secondIndex] <= secondLead)
1014:                   secondIndex++;
1015:               else
1016:                 while (secondsSelected[secondIndex] >= secondLead)
1017:                   secondIndex--;
1018:               
1019:               // and select it - updating the lead selection index
1020:               secondModel.addSelectionInterval(secondsSelected[secondIndex], 
1021:                                                secondsSelected[secondIndex]);
1022:             }
1023:         }
1024:       // We didn't have to wrap the firsts, so just find the "next" first
1025:       // and select it, we don't have to change "seconds"
1026:       else
1027:         {
1028:           int[] firstsSelected;
1029:           if (eventIsTab && table.getColumnSelectionAllowed() || 
1030:               !eventIsTab && table.getRowSelectionAllowed())
1031:             firstsSelected = eventIsTab ? 
1032:               table.getSelectedColumns() : table.getSelectedRows();
1033:           else
1034:             {
1035:               // if selection not allowed, consider ALL firsts to be selected
1036:               firstsSelected = new int[numFirsts];
1037:               for (int i = 0; i < numFirsts; i++)
1038:                 firstsSelected[i] = i;
1039:             }
1040:           int firstIndex = reverse ? firstsSelected.length - 1 : 0;
1041:           if (!reverse)
1042:             while (firstsSelected[firstIndex] <= firstLead)
1043:               firstIndex++;
1044:           else 
1045:             while (firstsSelected[firstIndex] >= firstLead)
1046:               firstIndex--;
1047:           firstModel.addSelectionInterval(firstsSelected[firstIndex], 
1048:                                           firstsSelected[firstIndex]);
1049:           secondModel.addSelectionInterval(secondLead, secondLead);
1050:         }
1051:     }
1052:     
1053:     /** 
1054:      * A helper method for the key  bindings. Used because the actions
1055:      * for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar.
1056:      *
1057:      * Selects the next (previous if SHIFT pressed) column (TAB) or row (ENTER)
1058:      * in the table, changing the current selection.  All cells in the table
1059:      * are eligible, not just the ones that are currently selected.
1060:      * @param firstModel the ListSelectionModel for columns (TAB) or rows
1061:      * (ENTER)
1062:      * @param firstMax the last index in firstModel
1063:      * @param secondModel the ListSelectionModel for rows (TAB) or columns
1064:      * (ENTER)
1065:      * @param secondMax the last index in secondModel
1066:      * @param reverse true if SHIFT was pressed for the event
1067:      */
1068: 
1069:     void advanceSingleSelection (ListSelectionModel firstModel, int firstMax, 
1070:                                  ListSelectionModel secondModel, int secondMax, 
1071:                                  boolean reverse)
1072:     {
1073:       // for TABs, "first" corresponds to columns and "seconds" to rows.
1074:       // the opposite is true for ENTERs
1075:       int firstLead = firstModel.getLeadSelectionIndex();
1076:       int secondLead = secondModel.getLeadSelectionIndex();
1077:       
1078:       // if we are going backwards subtract 2 because we later add 1
1079:       // for a net change of -1
1080:       if (reverse && (firstLead == 0))
1081:         {
1082:           // check if we have to wrap around
1083:           if (secondLead == 0)
1084:             secondLead += secondMax + 1;
1085:           secondLead -= 2;
1086:         }
1087:       
1088:       // do we have to wrap the "seconds"?
1089:       if (reverse && (firstLead == 0) || !reverse && (firstLead == firstMax))
1090:         secondModel.setSelectionInterval((secondLead + 1)%(secondMax + 1), 
1091:                                          (secondLead + 1)%(secondMax + 1));
1092:       // if not, just reselect the current lead
1093:       else
1094:         secondModel.setSelectionInterval(secondLead, secondLead);
1095:       
1096:       // if we are going backwards, subtract 2  because we add 1 later
1097:       // for net change of -1
1098:       if (reverse)
1099:         {
1100:           // check for wraparound
1101:           if (firstLead == 0)
1102:             firstLead += firstMax + 1;
1103:           firstLead -= 2;
1104:         }
1105:       // select the next "first"
1106:       firstModel.setSelectionInterval ((firstLead + 1)%(firstMax + 1), 
1107:                                        (firstLead + 1)%(firstMax + 1));
1108:     }
1109:   }
1110: 
1111:   protected void installListeners() 
1112:   {
1113:     if (focusListener == null)
1114:       focusListener = createFocusListener();
1115:     table.addFocusListener(focusListener);
1116:     if (keyListener == null)
1117:       keyListener = createKeyListener();
1118:     table.addKeyListener(keyListener);
1119:     if (mouseInputListener == null)
1120:       mouseInputListener = createMouseInputListener();
1121:     table.addMouseListener(mouseInputListener);    
1122:     table.addMouseMotionListener(mouseInputListener);
1123:     if (propertyChangeListener == null)
1124:       propertyChangeListener = new PropertyChangeHandler();
1125:     table.addPropertyChangeListener(propertyChangeListener);
1126:   }
1127: 
1128:   protected void uninstallDefaults() 
1129:   {
1130:     // TODO: this method used to do the following which is not
1131:     // quite right (at least it breaks apps that run fine with the
1132:     // JDK):
1133:     //
1134:     // table.setFont(null);
1135:     // table.setGridColor(null);
1136:     // table.setForeground(null);
1137:     // table.setBackground(null);
1138:     // table.setSelectionForeground(null);
1139:     // table.setSelectionBackground(null);
1140:     //
1141:     // This would leave the component in a corrupt state, which is
1142:     // not acceptable. A possible solution would be to have component
1143:     // level defaults installed, that get overridden by the UI defaults
1144:     // and get restored in this method. I am not quite sure about this
1145:     // though. / Roman Kennke
1146:   }
1147: 
1148:   protected void uninstallKeyboardActions() 
1149:   {
1150:     // TODO: Implement this properly.
1151:   }
1152: 
1153:   protected void uninstallListeners() 
1154:   {
1155:     table.removeFocusListener(focusListener);  
1156:     table.removeKeyListener(keyListener);
1157:     table.removeMouseListener(mouseInputListener);    
1158:     table.removeMouseMotionListener(mouseInputListener);
1159:     table.removePropertyChangeListener(propertyChangeListener);
1160:     propertyChangeListener = null;
1161:   }
1162: 
1163:   public void installUI(JComponent comp) 
1164:   {
1165:     table = (JTable)comp;
1166:     installDefaults();
1167:     installKeyboardActions();
1168:     installListeners();
1169:   }
1170: 
1171:   public void uninstallUI(JComponent c) 
1172:   {
1173:     uninstallListeners();
1174:     uninstallKeyboardActions();
1175:     uninstallDefaults();    
1176:   }
1177: 
1178:   /**
1179:    * Paints a single cell in the table.
1180:    *
1181:    * @param g The graphics context to paint in
1182:    * @param row The row number to paint
1183:    * @param col The column number to paint
1184:    * @param bounds The bounds of the cell to paint, assuming a coordinate
1185:    * system beginning at <code>(0,0)</code> in the upper left corner of the
1186:    * table
1187:    * @param rend A cell renderer to paint with
1188:    * @param data The data to provide to the cell renderer
1189:    * @param rowLead The lead selection for the rows of the table.
1190:    * @param colLead The lead selection for the columns of the table.
1191:    */
1192:   void paintCell(Graphics g, int row, int col, Rectangle bounds,
1193:                  TableCellRenderer rend, TableModel data,
1194:                  int rowLead, int colLead)
1195:   {
1196:     Component comp = table.prepareRenderer(rend, row, col);
1197:     rendererPane.paintComponent(g, comp, table, bounds);
1198: 
1199:     // FIXME: this is manual painting of the Caret, why doesn't the 
1200:     // JTextField take care of this itself?
1201:     if (comp instanceof JTextField)
1202:       {
1203:         Rectangle oldClip = g.getClipBounds();
1204:         g.translate(bounds.x, bounds.y);
1205:         g.clipRect(0, 0, bounds.width, bounds.height);
1206:         ((JTextField)comp).getCaret().paint(g);
1207:         g.translate(-bounds.x, -bounds.y);
1208:         g.setClip(oldClip);
1209:       }
1210:   }
1211:   
1212:   public void paint(Graphics gfx, JComponent ignored) 
1213:   {
1214:     int ncols = table.getColumnCount();
1215:     int nrows = table.getRowCount();
1216:     if (nrows == 0 || ncols == 0)
1217:       return;
1218: 
1219:     Rectangle clip = gfx.getClipBounds();
1220:     TableColumnModel cols = table.getColumnModel();
1221: 
1222:     int height = table.getRowHeight();
1223:     int x0 = 0, y0 = 0;
1224:     int x = x0;
1225:     int y = y0;
1226: 
1227:     Dimension gap = table.getIntercellSpacing();
1228:     int ymax = clip.y + clip.height;
1229:     int xmax = clip.x + clip.width;
1230: 
1231:     // paint the cell contents
1232:     for (int c = 0; c < ncols && x < xmax; ++c)
1233:       {
1234:         y = y0;
1235:         TableColumn col = cols.getColumn(c);
1236:         int width = col.getWidth();
1237:         int halfGapWidth = gap.width / 2;
1238:         int halfGapHeight = gap.height / 2;
1239:         for (int r = 0; r < nrows && y < ymax; ++r)
1240:           {
1241:             Rectangle bounds = new Rectangle(x + halfGapWidth,
1242:                                              y + halfGapHeight + 1,
1243:                                              width - gap.width + 1,
1244:                                              height - gap.height);
1245:             if (bounds.intersects(clip))
1246:               {           
1247:                 paintCell(gfx, r, c, bounds, table.getCellRenderer(r, c),
1248:                           table.getModel(),
1249:                           table.getSelectionModel().getLeadSelectionIndex(),
1250:                           table.getColumnModel().getSelectionModel().getLeadSelectionIndex());
1251:               }
1252:             y += height;
1253:           }
1254:         x += width;
1255:       }
1256: 
1257:     // tighten up the x and y max bounds
1258:     ymax = y;
1259:     xmax = x;
1260: 
1261:     Color grid = table.getGridColor();    
1262: 
1263:     // paint vertical grid lines
1264:     if (grid != null && table.getShowVerticalLines())
1265:       {    
1266:         x = x0;
1267:         Color save = gfx.getColor();
1268:         gfx.setColor(grid);
1269:         for (int c = 0; c < ncols && x < xmax; ++c)
1270:           {
1271:             x += cols.getColumn(c).getWidth();
1272:             gfx.drawLine(x, y0, x, ymax);
1273:           }
1274:         gfx.setColor(save);
1275:       }
1276: 
1277:     // paint horizontal grid lines    
1278:     if (grid != null && table.getShowHorizontalLines())
1279:       {    
1280:         y = y0;
1281:         Color save = gfx.getColor();
1282:         gfx.setColor(grid);
1283:         for (int r = 0; r < nrows && y < ymax; ++r)
1284:           {
1285:             y += height;
1286:             gfx.drawLine(x0, y, xmax, y);
1287:           }
1288:         gfx.setColor(save);
1289:       }
1290:   }
1291: }