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.UIDefaults;
  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.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:     UIDefaults defaults = UIManager.getLookAndFeelDefaults();
 401:     InputMap ancestorMap = (InputMap)defaults.get("Table.ancestorInputMap");
 402:     InputMapUIResource parentInputMap = new InputMapUIResource();
 403:     // FIXME: The JDK uses a LazyActionMap for parentActionMap
 404:     ActionMap parentActionMap = new ActionMap();
 405:     action = new TableAction();
 406:     Object keys[] = ancestorMap.allKeys();
 407:     // Register key bindings in the UI InputMap-ActionMap pair
 408:     for (int i = 0; i < keys.length; i++)
 409:       {
 410:         KeyStroke stroke = (KeyStroke)keys[i];
 411:         String actionString = (String) ancestorMap.get(stroke);
 412: 
 413:         parentInputMap.put(KeyStroke.getKeyStroke(stroke.getKeyCode(),
 414:                                                   stroke.getModifiers()),
 415:                            actionString);
 416: 
 417:         parentActionMap.put (actionString, 
 418:                              new ActionListenerProxy (action, actionString));
 419: 
 420:       }
 421:     // Set the UI InputMap-ActionMap pair to be the parents of the
 422:     // JTable's InputMap-ActionMap pair
 423:     parentInputMap.setParent
 424:       (table.getInputMap
 425:        (JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).getParent());
 426:     parentActionMap.setParent(table.getActionMap().getParent());
 427:     table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).
 428:       setParent(parentInputMap);
 429:     table.getActionMap().setParent(parentActionMap);
 430:   }
 431: 
 432:   /**
 433:    * This class is used to mimmic the behaviour of the JDK when registering
 434:    * keyboard actions.  It is the same as the private class used in JComponent
 435:    * for the same reason.  This class receives an action event and dispatches
 436:    * it to the true receiver after altering the actionCommand property of the
 437:    * event.
 438:    */
 439:   private static class ActionListenerProxy
 440:     extends AbstractAction
 441:   {
 442:     ActionListener target;
 443:     String bindingCommandName;
 444: 
 445:     public ActionListenerProxy(ActionListener li, 
 446:                                String cmd)
 447:     {
 448:       target = li;
 449:       bindingCommandName = cmd;
 450:     }
 451: 
 452:     public void actionPerformed(ActionEvent e)
 453:     {
 454:       ActionEvent derivedEvent = new ActionEvent(e.getSource(),
 455:                                                  e.getID(),
 456:                                                  bindingCommandName,
 457:                                                  e.getModifiers());
 458:       target.actionPerformed(derivedEvent);
 459:     }
 460:   }
 461: 
 462:   /**
 463:    * This class implements the actions that we want to happen
 464:    * when specific keys are pressed for the JTable.  The actionPerformed
 465:    * method is called when a key that has been registered for the JTable
 466:    * is received.
 467:    */
 468:   class TableAction extends AbstractAction
 469:   {
 470:     /**
 471:      * What to do when this action is called.
 472:      *
 473:      * @param e the ActionEvent that caused this action.
 474:      */
 475:     public void actionPerformed (ActionEvent e)
 476:     {
 477:       DefaultListSelectionModel rowModel = (DefaultListSelectionModel) table.getSelectionModel();
 478:       DefaultListSelectionModel colModel = (DefaultListSelectionModel) table.getColumnModel().getSelectionModel();
 479: 
 480:       int rowLead = rowModel.getLeadSelectionIndex();
 481:       int rowMax = table.getModel().getRowCount() - 1;
 482:       
 483:       int colLead = colModel.getLeadSelectionIndex();
 484:       int colMax = table.getModel().getColumnCount() - 1;
 485:       
 486:       String command = e.getActionCommand();
 487:       
 488:       if (command.equals("selectPreviousRowExtendSelection"))
 489:         {
 490:           rowModel.setLeadSelectionIndex(Math.max(rowLead - 1, 0));
 491:           colModel.setLeadSelectionIndex(colLead);
 492:         }
 493:       else if (command.equals("selectLastColumn"))
 494:         {
 495:           rowModel.setSelectionInterval(rowLead, rowLead);
 496:           colModel.setSelectionInterval(colMax, colMax);
 497:         }
 498:       else if (command.equals("startEditing"))
 499:         {
 500:           if (table.isCellEditable(rowLead, colLead))
 501:             table.editCellAt(rowLead,colLead);
 502:         }
 503:       else if (command.equals("selectFirstRowExtendSelection"))
 504:         {              
 505:           rowModel.setLeadSelectionIndex(0);
 506:           colModel.setLeadSelectionIndex(colLead);
 507:         }
 508:       else if (command.equals("selectFirstColumn"))
 509:         {
 510:           rowModel.setSelectionInterval(rowLead, rowLead);
 511:           colModel.setSelectionInterval(0, 0);
 512:         }
 513:       else if (command.equals("selectFirstColumnExtendSelection"))
 514:         {
 515:           colModel.setLeadSelectionIndex(0);
 516:           rowModel.setLeadSelectionIndex(rowLead);
 517:         }      
 518:       else if (command.equals("selectLastRow"))
 519:         {
 520:           rowModel.setSelectionInterval(rowMax,rowMax);
 521:           colModel.setSelectionInterval(colLead, colLead);
 522:         }
 523:       else if (command.equals("selectNextRowExtendSelection"))
 524:         {
 525:           rowModel.setLeadSelectionIndex(Math.min(rowLead + 1, rowMax));
 526:           colModel.setLeadSelectionIndex(colLead);
 527:         }
 528:       else if (command.equals("selectFirstRow"))
 529:         {
 530:           rowModel.setSelectionInterval(0,0);
 531:           colModel.setSelectionInterval(colLead, colLead);
 532:         }
 533:       else if (command.equals("selectNextColumnExtendSelection"))
 534:         {
 535:           colModel.setLeadSelectionIndex(Math.min(colLead + 1, colMax));
 536:           rowModel.setLeadSelectionIndex(rowLead);
 537:         }
 538:       else if (command.equals("selectLastColumnExtendSelection"))
 539:         {
 540:           colModel.setLeadSelectionIndex(colMax);
 541:           rowModel.setLeadSelectionIndex(rowLead);
 542:         }
 543:       else if (command.equals("selectPreviousColumnExtendSelection"))
 544:         {
 545:           colModel.setLeadSelectionIndex(Math.max(colLead - 1, 0));
 546:           rowModel.setLeadSelectionIndex(rowLead);
 547:         }
 548:       else if (command.equals("selectNextRow"))
 549:         {
 550:           rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax),
 551:                                         Math.min(rowLead + 1, rowMax));
 552:           colModel.setSelectionInterval(colLead,colLead);
 553:         }
 554:       else if (command.equals("scrollUpExtendSelection"))
 555:         {
 556:           int target;
 557:           if (rowLead == getFirstVisibleRowIndex())
 558:             target = Math.max
 559:               (0, rowLead - (getLastVisibleRowIndex() - 
 560:                              getFirstVisibleRowIndex() + 1));
 561:           else
 562:             target = getFirstVisibleRowIndex();
 563:           
 564:           rowModel.setLeadSelectionIndex(target);
 565:           colModel.setLeadSelectionIndex(colLead);
 566:         }
 567:       else if (command.equals("selectPreviousRow"))
 568:         {
 569:           rowModel.setSelectionInterval(Math.max(rowLead - 1, 0),
 570:                                         Math.max(rowLead - 1, 0));
 571:           colModel.setSelectionInterval(colLead,colLead);
 572:         }
 573:       else if (command.equals("scrollRightChangeSelection"))
 574:         {
 575:           int target;
 576:           if (colLead == getLastVisibleColumnIndex())
 577:             target = Math.min
 578:               (colMax, colLead + (getLastVisibleColumnIndex() -
 579:                                   getFirstVisibleColumnIndex() + 1));
 580:           else
 581:             target = getLastVisibleColumnIndex();
 582:           
 583:           colModel.setSelectionInterval(target, target);
 584:           rowModel.setSelectionInterval(rowLead, rowLead);
 585:         }
 586:       else if (command.equals("selectPreviousColumn"))
 587:         {
 588:           rowModel.setSelectionInterval(rowLead,rowLead);
 589:           colModel.setSelectionInterval(Math.max(colLead - 1, 0),
 590:                                         Math.max(colLead - 1, 0));
 591:         }
 592:       else if (command.equals("scrollLeftChangeSelection"))
 593:         {
 594:           int target;
 595:           if (colLead == getFirstVisibleColumnIndex())
 596:             target = Math.max
 597:               (0, colLead - (getLastVisibleColumnIndex() -
 598:                              getFirstVisibleColumnIndex() + 1));
 599:           else
 600:             target = getFirstVisibleColumnIndex();
 601:           
 602:           colModel.setSelectionInterval(target, target);
 603:           rowModel.setSelectionInterval(rowLead, rowLead);
 604:         }
 605:       else if (command.equals("clearSelection"))
 606:         {
 607:           table.clearSelection();
 608:         }
 609:       else if (command.equals("cancel"))
 610:         {
 611:           // FIXME: implement other parts of "cancel" like undo-ing last
 612:           // selection.  Right now it just calls editingCancelled if
 613:           // we're currently editing.
 614:           if (table.isEditing())
 615:             table.editingCanceled(new ChangeEvent("cancel"));
 616:         }
 617:       else if (command.equals("selectNextRowCell")
 618:                || command.equals("selectPreviousRowCell")
 619:                || command.equals("selectNextColumnCell")
 620:                || command.equals("selectPreviousColumnCell"))
 621:         {
 622:           // If nothing is selected, select the first cell in the table
 623:           if (table.getSelectedRowCount() == 0 && 
 624:               table.getSelectedColumnCount() == 0)
 625:             {
 626:               rowModel.setSelectionInterval(0, 0);
 627:               colModel.setSelectionInterval(0, 0);
 628:               return;
 629:             }
 630:           
 631:           // If the lead selection index isn't selected (ie a remove operation
 632:           // happened, then set the lead to the first selected cell in the
 633:           // table
 634:           if (!table.isCellSelected(rowLead, colLead))
 635:             {
 636:               rowModel.addSelectionInterval(rowModel.getMinSelectionIndex(), 
 637:                                             rowModel.getMinSelectionIndex());
 638:               colModel.addSelectionInterval(colModel.getMinSelectionIndex(), 
 639:                                             colModel.getMinSelectionIndex());
 640:               return;
 641:             }
 642:           
 643:           // multRowsSelected and multColsSelected tell us if multiple rows or
 644:           // columns are selected, respectively
 645:           boolean multRowsSelected, multColsSelected;
 646:           multRowsSelected = table.getSelectedRowCount() > 1 &&
 647:             table.getRowSelectionAllowed();
 648:           
 649:           multColsSelected = table.getSelectedColumnCount() > 1 &&
 650:             table.getColumnSelectionAllowed();
 651:           
 652:           // If there is just one selection, select the next cell, and wrap
 653:           // when you get to the edges of the table.
 654:           if (!multColsSelected && !multRowsSelected)
 655:             {
 656:               if (command.indexOf("Column") != -1) 
 657:                 advanceSingleSelection(colModel, colMax, rowModel, rowMax, 
 658:                                        (command.equals
 659:                                         ("selectPreviousColumnCell")));
 660:               else
 661:                 advanceSingleSelection(rowModel, rowMax, colModel, colMax, 
 662:                                        (command.equals 
 663:                                         ("selectPreviousRowCell")));
 664:               return;
 665:             }
 666:           
 667:           
 668:           // rowMinSelected and rowMaxSelected are the minimum and maximum
 669:           // values respectively of selected cells in the row selection model
 670:           // Similarly for colMinSelected and colMaxSelected.
 671:           int rowMaxSelected = table.getRowSelectionAllowed() ? 
 672:             rowModel.getMaxSelectionIndex() : table.getModel().getRowCount() - 1;
 673:           int rowMinSelected = table.getRowSelectionAllowed() ? 
 674:             rowModel.getMinSelectionIndex() : 0; 
 675:           int colMaxSelected = table.getColumnSelectionAllowed() ? 
 676:             colModel.getMaxSelectionIndex() : 
 677:             table.getModel().getColumnCount() - 1;
 678:           int colMinSelected = table.getColumnSelectionAllowed() ? 
 679:             colModel.getMinSelectionIndex() : 0;
 680:           
 681:           // If there are multiple rows and columns selected, select the next
 682:           // cell and wrap at the edges of the selection.  
 683:           if (command.indexOf("Column") != -1) 
 684:             advanceMultipleSelection(colModel, colMinSelected, colMaxSelected, 
 685:                                      rowModel, rowMinSelected, rowMaxSelected, 
 686:                                      (command.equals
 687:                                       ("selectPreviousColumnCell")), true);
 688:           
 689:           else
 690:             advanceMultipleSelection(rowModel, rowMinSelected, rowMaxSelected, 
 691:                                      colModel, colMinSelected, colMaxSelected, 
 692:                                      (command.equals 
 693:                                       ("selectPreviousRowCell")), false);
 694:         }
 695:       else if (command.equals("selectNextColumn"))
 696:         {
 697:           rowModel.setSelectionInterval(rowLead,rowLead);
 698:           colModel.setSelectionInterval(Math.min(colLead + 1, colMax),
 699:                                         Math.min(colLead + 1, colMax));
 700:         }
 701:       else if (command.equals("scrollLeftExtendSelection"))
 702:         {
 703:           int target;
 704:           if (colLead == getFirstVisibleColumnIndex())
 705:             target = Math.max
 706:               (0, colLead - (getLastVisibleColumnIndex() -
 707:                              getFirstVisibleColumnIndex() + 1));
 708:           else
 709:             target = getFirstVisibleColumnIndex();
 710:           
 711:           colModel.setLeadSelectionIndex(target);
 712:           rowModel.setLeadSelectionIndex(rowLead);
 713:         }
 714:       else if (command.equals("scrollDownChangeSelection"))
 715:         {
 716:           int target;
 717:           if (rowLead == getLastVisibleRowIndex())
 718:             target = Math.min
 719:               (rowMax, rowLead + (getLastVisibleRowIndex() - 
 720:                                   getFirstVisibleRowIndex() + 1));
 721:           else
 722:             target = getLastVisibleRowIndex();
 723:           
 724:           rowModel.setSelectionInterval(target, target);
 725:           colModel.setSelectionInterval(colLead, colLead);
 726:         }
 727:       else if (command.equals("scrollRightExtendSelection"))
 728:         {
 729:           int target;
 730:           if (colLead == getLastVisibleColumnIndex())
 731:             target = Math.min
 732:               (colMax, colLead + (getLastVisibleColumnIndex() -
 733:                                   getFirstVisibleColumnIndex() + 1));
 734:           else
 735:             target = getLastVisibleColumnIndex();
 736:           
 737:           colModel.setLeadSelectionIndex(target);
 738:           rowModel.setLeadSelectionIndex(rowLead);
 739:         }
 740:       else if (command.equals("selectAll"))
 741:         {
 742:           table.selectAll();
 743:         }
 744:       else if (command.equals("selectLastRowExtendSelection"))
 745:         {
 746:           rowModel.setLeadSelectionIndex(rowMax);
 747:           colModel.setLeadSelectionIndex(colLead);
 748:         }
 749:       else if (command.equals("scrollDownExtendSelection"))
 750:         {
 751:           int target;
 752:           if (rowLead == getLastVisibleRowIndex())
 753:             target = Math.min
 754:               (rowMax, rowLead + (getLastVisibleRowIndex() - 
 755:                                   getFirstVisibleRowIndex() + 1));
 756:           else
 757:             target = getLastVisibleRowIndex();
 758:           
 759:           rowModel.setLeadSelectionIndex(target);
 760:           colModel.setLeadSelectionIndex(colLead);
 761:         }      
 762:       else if (command.equals("scrollUpChangeSelection"))
 763:         {
 764:           int target;
 765:           if (rowLead == getFirstVisibleRowIndex())
 766:             target = Math.max
 767:               (0, rowLead - (getLastVisibleRowIndex() - 
 768:                              getFirstVisibleRowIndex() + 1));
 769:           else
 770:             target = getFirstVisibleRowIndex();
 771:           
 772:           rowModel.setSelectionInterval(target, target);
 773:           colModel.setSelectionInterval(colLead, colLead);
 774:         }
 775:       else if (command.equals("selectNextRowChangeLead"))
 776:           {
 777:             if (rowModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
 778:               {
 779:                 // just "selectNextRow"
 780:                 rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax),
 781:                                               Math.min(rowLead + 1, rowMax));
 782:                 colModel.setSelectionInterval(colLead,colLead);
 783:               }
 784:             else
 785:               rowModel.moveLeadSelectionIndex(Math.min(rowLead + 1, rowMax));
 786:           }
 787:       else if (command.equals("selectPreviousRowChangeLead"))
 788:         {
 789:           if (rowModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
 790:             {
 791:               // just selectPreviousRow
 792:               rowModel.setSelectionInterval(Math.max(rowLead - 1, 0),
 793:                                             Math.min(rowLead -1, 0));
 794:               colModel.setSelectionInterval(colLead,colLead);
 795:             }
 796:           else
 797:             rowModel.moveLeadSelectionIndex(Math.max(rowLead - 1, 0));
 798:         }
 799:       else if (command.equals("selectNextColumnChangeLead"))
 800:         {
 801:           if (colModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)            
 802:             {
 803:               // just selectNextColumn
 804:               rowModel.setSelectionInterval(rowLead,rowLead);
 805:               colModel.setSelectionInterval(Math.min(colLead + 1, colMax),
 806:                                             Math.min(colLead + 1, colMax));
 807:             }
 808:           else
 809:             colModel.moveLeadSelectionIndex(Math.min(colLead + 1, colMax));
 810:         }
 811:       else if (command.equals("selectPreviousColumnChangeLead"))
 812:         {
 813:           if (colModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)            
 814:             {
 815:               // just selectPreviousColumn
 816:               rowModel.setSelectionInterval(rowLead,rowLead);
 817:               colModel.setSelectionInterval(Math.max(colLead - 1, 0),
 818:                                             Math.max(colLead - 1, 0));
 819:               
 820:             }
 821:           else
 822:             colModel.moveLeadSelectionIndex(Math.max(colLead - 1, 0));
 823:         }
 824:       else if (command.equals("addToSelection"))
 825:           {
 826:             if (!table.isEditing())
 827:               {
 828:                 int oldRowAnchor = rowModel.getAnchorSelectionIndex();
 829:                 int oldColAnchor = colModel.getAnchorSelectionIndex();
 830:                 rowModel.addSelectionInterval(rowLead, rowLead);
 831:                 colModel.addSelectionInterval(colLead, colLead);
 832:                 rowModel.setAnchorSelectionIndex(oldRowAnchor);
 833:                 colModel.setAnchorSelectionIndex(oldColAnchor);
 834:               }
 835:           }
 836:       else if (command.equals("extendTo"))
 837:         {
 838:           rowModel.setSelectionInterval(rowModel.getAnchorSelectionIndex(),
 839:                                         rowLead);
 840:           colModel.setSelectionInterval(colModel.getAnchorSelectionIndex(),
 841:                                         colLead);
 842:         }
 843:       else if (command.equals("toggleAndAnchor"))
 844:         {
 845:           if (rowModel.isSelectedIndex(rowLead))
 846:             rowModel.removeSelectionInterval(rowLead, rowLead);
 847:           else
 848:             rowModel.addSelectionInterval(rowLead, rowLead);
 849:           
 850:           if (colModel.isSelectedIndex(colLead))
 851:             colModel.removeSelectionInterval(colLead, colLead);
 852:           else
 853:             colModel.addSelectionInterval(colLead, colLead);
 854:           
 855:           rowModel.setAnchorSelectionIndex(rowLead);
 856:           colModel.setAnchorSelectionIndex(colLead);
 857:         }
 858:       else 
 859:         {
 860:           // If we're here that means we bound this TableAction class
 861:           // to a keyboard input but we either want to ignore that input
 862:           // or we just haven't implemented its action yet.
 863:           
 864:           // Uncomment the following line to print the names of unused bindings
 865:           // when their keys are pressed
 866:           
 867:           // System.out.println ("not implemented: "+e.getActionCommand());
 868:         }
 869: 
 870:       // Any commands whose keyStrokes should be used by the Editor should not
 871:       // cause editing to be stopped: ie, the SPACE sends "addToSelection" but 
 872:       // if the table is in editing mode, the space should not cause us to stop
 873:       // editing because it should be used by the Editor.
 874:       if (table.isEditing() && command != "startEditing"
 875:           && command != "addToSelection")
 876:         table.editingStopped(new ChangeEvent("update"));
 877:             
 878:       table.scrollRectToVisible
 879:         (table.getCellRect(rowModel.getLeadSelectionIndex(), 
 880:                            colModel.getLeadSelectionIndex(), false));
 881:       table.repaint();
 882:     }
 883:     
 884:     /**
 885:      * Returns the column index of the first visible column.
 886:      * @return the column index of the first visible column.
 887:      */
 888:     int getFirstVisibleColumnIndex()
 889:     {
 890:       ComponentOrientation or = table.getComponentOrientation();
 891:       Rectangle r = table.getVisibleRect();
 892:       if (!or.isLeftToRight())
 893:         r.translate((int) r.getWidth() - 1, 0);
 894:       return table.columnAtPoint(r.getLocation());
 895:     }
 896:     
 897:     /**
 898:      * Returns the column index of the last visible column.
 899:      *
 900:      */
 901:     int getLastVisibleColumnIndex()
 902:     {
 903:       ComponentOrientation or = table.getComponentOrientation();
 904:       Rectangle r = table.getVisibleRect();
 905:       if (or.isLeftToRight())
 906:         r.translate((int) r.getWidth() - 1, 0);
 907:       return table.columnAtPoint(r.getLocation());      
 908:     }
 909:     
 910:     /**
 911:      * Returns the row index of the first visible row.
 912:      *
 913:      */
 914:     int getFirstVisibleRowIndex()
 915:     {
 916:       ComponentOrientation or = table.getComponentOrientation();
 917:       Rectangle r = table.getVisibleRect();
 918:       if (!or.isLeftToRight())
 919:         r.translate((int) r.getWidth() - 1, 0);
 920:       return table.rowAtPoint(r.getLocation());
 921:     }
 922:     
 923:     /**
 924:      * Returns the row index of the last visible row.
 925:      *
 926:      */
 927:     int getLastVisibleRowIndex()
 928:     {
 929:       ComponentOrientation or = table.getComponentOrientation();
 930:       Rectangle r = table.getVisibleRect();
 931:       r.translate(0, (int) r.getHeight() - 1);
 932:       if (or.isLeftToRight())
 933:         r.translate((int) r.getWidth() - 1, 0);
 934:       // The next if makes sure that we don't return -1 simply because
 935:       // there is white space at the bottom of the table (ie, the display
 936:       // area is larger than the table)
 937:       if (table.rowAtPoint(r.getLocation()) == -1)
 938:         {
 939:           if (getFirstVisibleRowIndex() == -1)
 940:             return -1;
 941:           else
 942:             return table.getModel().getRowCount() - 1;
 943:         }
 944:       return table.rowAtPoint(r.getLocation());
 945:     }
 946: 
 947:     /**
 948:      * A helper method for the key bindings.  Used because the actions
 949:      * for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar.
 950:      *
 951:      * Selects the next (previous if SHIFT pressed) column for TAB, or row for
 952:      * ENTER from within the currently selected cells.
 953:      *
 954:      * @param firstModel the ListSelectionModel for columns (TAB) or
 955:      * rows (ENTER)
 956:      * @param firstMin the first selected index in firstModel
 957:      * @param firstMax the last selected index in firstModel
 958:      * @param secondModel the ListSelectionModel for rows (TAB) or 
 959:      * columns (ENTER)
 960:      * @param secondMin the first selected index in secondModel
 961:      * @param secondMax the last selected index in secondModel
 962:      * @param reverse true if shift was held for the event
 963:      * @param eventIsTab true if TAB was pressed, false if ENTER pressed
 964:      */
 965:     void advanceMultipleSelection (ListSelectionModel firstModel, int firstMin,
 966:                                    int firstMax, ListSelectionModel secondModel, 
 967:                                    int secondMin, int secondMax, boolean reverse,
 968:                                    boolean eventIsTab)
 969:     {
 970:       // If eventIsTab, all the "firsts" correspond to columns, otherwise, to rows
 971:       // "seconds" correspond to the opposite
 972:       int firstLead = firstModel.getLeadSelectionIndex();
 973:       int secondLead = secondModel.getLeadSelectionIndex();
 974:       int numFirsts = eventIsTab ? 
 975:         table.getModel().getColumnCount() : table.getModel().getRowCount();
 976:       int numSeconds = eventIsTab ? 
 977:         table.getModel().getRowCount() : table.getModel().getColumnCount();
 978: 
 979:       // check if we have to wrap the "firsts" around, going to the other side
 980:       if ((firstLead == firstMax && !reverse) || 
 981:           (reverse && firstLead == firstMin))
 982:         {
 983:           firstModel.addSelectionInterval(reverse ? firstMax : firstMin, 
 984:                                           reverse ? firstMax : firstMin);
 985:           
 986:           // check if we have to wrap the "seconds"
 987:           if ((secondLead == secondMax && !reverse) || 
 988:               (reverse && secondLead == secondMin))
 989:             secondModel.addSelectionInterval(reverse ? secondMax : secondMin, 
 990:                                              reverse ? secondMax : secondMin);
 991: 
 992:           // if we're not wrapping the seconds, we have to find out where we
 993:           // are within the secondModel and advance to the next cell (or 
 994:           // go back to the previous cell if reverse == true)
 995:           else
 996:             {
 997:               int[] secondsSelected;
 998:               if (eventIsTab && table.getRowSelectionAllowed() || 
 999:                   !eventIsTab && table.getColumnSelectionAllowed())
1000:                 secondsSelected = eventIsTab ? 
1001:                   table.getSelectedRows() : table.getSelectedColumns();
1002:               else
1003:                 {
1004:                   // if row selection is not allowed, then the entire column gets
1005:                   // selected when you click on it, so consider ALL rows selected
1006:                   secondsSelected = new int[numSeconds];
1007:                   for (int i = 0; i < numSeconds; i++)
1008:                   secondsSelected[i] = i;
1009:                 }
1010: 
1011:               // and now find the "next" index within the model
1012:               int secondIndex = reverse ? secondsSelected.length - 1 : 0;
1013:               if (!reverse)
1014:                 while (secondsSelected[secondIndex] <= secondLead)
1015:                   secondIndex++;
1016:               else
1017:                 while (secondsSelected[secondIndex] >= secondLead)
1018:                   secondIndex--;
1019:               
1020:               // and select it - updating the lead selection index
1021:               secondModel.addSelectionInterval(secondsSelected[secondIndex], 
1022:                                                secondsSelected[secondIndex]);
1023:             }
1024:         }
1025:       // We didn't have to wrap the firsts, so just find the "next" first
1026:       // and select it, we don't have to change "seconds"
1027:       else
1028:         {
1029:           int[] firstsSelected;
1030:           if (eventIsTab && table.getColumnSelectionAllowed() || 
1031:               !eventIsTab && table.getRowSelectionAllowed())
1032:             firstsSelected = eventIsTab ? 
1033:               table.getSelectedColumns() : table.getSelectedRows();
1034:           else
1035:             {
1036:               // if selection not allowed, consider ALL firsts to be selected
1037:               firstsSelected = new int[numFirsts];
1038:               for (int i = 0; i < numFirsts; i++)
1039:                 firstsSelected[i] = i;
1040:             }
1041:           int firstIndex = reverse ? firstsSelected.length - 1 : 0;
1042:           if (!reverse)
1043:             while (firstsSelected[firstIndex] <= firstLead)
1044:               firstIndex++;
1045:           else 
1046:             while (firstsSelected[firstIndex] >= firstLead)
1047:               firstIndex--;
1048:           firstModel.addSelectionInterval(firstsSelected[firstIndex], 
1049:                                           firstsSelected[firstIndex]);
1050:           secondModel.addSelectionInterval(secondLead, secondLead);
1051:         }
1052:     }
1053:     
1054:     /** 
1055:      * A helper method for the key  bindings. Used because the actions
1056:      * for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar.
1057:      *
1058:      * Selects the next (previous if SHIFT pressed) column (TAB) or row (ENTER)
1059:      * in the table, changing the current selection.  All cells in the table
1060:      * are eligible, not just the ones that are currently selected.
1061:      * @param firstModel the ListSelectionModel for columns (TAB) or rows
1062:      * (ENTER)
1063:      * @param firstMax the last index in firstModel
1064:      * @param secondModel the ListSelectionModel for rows (TAB) or columns
1065:      * (ENTER)
1066:      * @param secondMax the last index in secondModel
1067:      * @param reverse true if SHIFT was pressed for the event
1068:      */
1069: 
1070:     void advanceSingleSelection (ListSelectionModel firstModel, int firstMax, 
1071:                                  ListSelectionModel secondModel, int secondMax, 
1072:                                  boolean reverse)
1073:     {
1074:       // for TABs, "first" corresponds to columns and "seconds" to rows.
1075:       // the opposite is true for ENTERs
1076:       int firstLead = firstModel.getLeadSelectionIndex();
1077:       int secondLead = secondModel.getLeadSelectionIndex();
1078:       
1079:       // if we are going backwards subtract 2 because we later add 1
1080:       // for a net change of -1
1081:       if (reverse && (firstLead == 0))
1082:         {
1083:           // check if we have to wrap around
1084:           if (secondLead == 0)
1085:             secondLead += secondMax + 1;
1086:           secondLead -= 2;
1087:         }
1088:       
1089:       // do we have to wrap the "seconds"?
1090:       if (reverse && (firstLead == 0) || !reverse && (firstLead == firstMax))
1091:         secondModel.setSelectionInterval((secondLead + 1)%(secondMax + 1), 
1092:                                          (secondLead + 1)%(secondMax + 1));
1093:       // if not, just reselect the current lead
1094:       else
1095:         secondModel.setSelectionInterval(secondLead, secondLead);
1096:       
1097:       // if we are going backwards, subtract 2  because we add 1 later
1098:       // for net change of -1
1099:       if (reverse)
1100:         {
1101:           // check for wraparound
1102:           if (firstLead == 0)
1103:             firstLead += firstMax + 1;
1104:           firstLead -= 2;
1105:         }
1106:       // select the next "first"
1107:       firstModel.setSelectionInterval ((firstLead + 1)%(firstMax + 1), 
1108:                                        (firstLead + 1)%(firstMax + 1));
1109:     }
1110:   }
1111: 
1112:   protected void installListeners() 
1113:   {
1114:     if (focusListener == null)
1115:       focusListener = createFocusListener();
1116:     table.addFocusListener(focusListener);
1117:     if (keyListener == null)
1118:       keyListener = createKeyListener();
1119:     table.addKeyListener(keyListener);
1120:     if (mouseInputListener == null)
1121:       mouseInputListener = createMouseInputListener();
1122:     table.addMouseListener(mouseInputListener);    
1123:     table.addMouseMotionListener(mouseInputListener);
1124:     if (propertyChangeListener == null)
1125:       propertyChangeListener = new PropertyChangeHandler();
1126:     table.addPropertyChangeListener(propertyChangeListener);
1127:   }
1128: 
1129:   protected void uninstallDefaults() 
1130:   {
1131:     // TODO: this method used to do the following which is not
1132:     // quite right (at least it breaks apps that run fine with the
1133:     // JDK):
1134:     //
1135:     // table.setFont(null);
1136:     // table.setGridColor(null);
1137:     // table.setForeground(null);
1138:     // table.setBackground(null);
1139:     // table.setSelectionForeground(null);
1140:     // table.setSelectionBackground(null);
1141:     //
1142:     // This would leave the component in a corrupt state, which is
1143:     // not acceptable. A possible solution would be to have component
1144:     // level defaults installed, that get overridden by the UI defaults
1145:     // and get restored in this method. I am not quite sure about this
1146:     // though. / Roman Kennke
1147:   }
1148: 
1149:   protected void uninstallKeyboardActions() 
1150:   {
1151:     // TODO: Implement this properly.
1152:   }
1153: 
1154:   protected void uninstallListeners() 
1155:   {
1156:     table.removeFocusListener(focusListener);  
1157:     table.removeKeyListener(keyListener);
1158:     table.removeMouseListener(mouseInputListener);    
1159:     table.removeMouseMotionListener(mouseInputListener);
1160:     table.removePropertyChangeListener(propertyChangeListener);
1161:     propertyChangeListener = null;
1162:   }
1163: 
1164:   public void installUI(JComponent comp) 
1165:   {
1166:     table = (JTable)comp;
1167:     installDefaults();
1168:     installKeyboardActions();
1169:     installListeners();
1170:   }
1171: 
1172:   public void uninstallUI(JComponent c) 
1173:   {
1174:     uninstallListeners();
1175:     uninstallKeyboardActions();
1176:     uninstallDefaults();    
1177:   }
1178: 
1179:   /**
1180:    * Paints a single cell in the table.
1181:    *
1182:    * @param g The graphics context to paint in
1183:    * @param row The row number to paint
1184:    * @param col The column number to paint
1185:    * @param bounds The bounds of the cell to paint, assuming a coordinate
1186:    * system beginning at <code>(0,0)</code> in the upper left corner of the
1187:    * table
1188:    * @param rend A cell renderer to paint with
1189:    * @param data The data to provide to the cell renderer
1190:    * @param rowLead The lead selection for the rows of the table.
1191:    * @param colLead The lead selection for the columns of the table.
1192:    */
1193:   void paintCell(Graphics g, int row, int col, Rectangle bounds,
1194:                  TableCellRenderer rend, TableModel data,
1195:                  int rowLead, int colLead)
1196:   {
1197:     boolean rowSelAllowed = table.getRowSelectionAllowed();
1198:     boolean colSelAllowed = table.getColumnSelectionAllowed();
1199:     boolean isSel = false;
1200:     if (rowSelAllowed && colSelAllowed || !rowSelAllowed && !colSelAllowed)
1201:       isSel = table.isCellSelected(row, col);
1202:     else
1203:       isSel = table.isRowSelected(row) && table.getRowSelectionAllowed()
1204:            || table.isColumnSelected(col) && table.getColumnSelectionAllowed();
1205: 
1206:     // Determine the focused cell. The focused cell is the cell at the
1207:     // leadSelectionIndices of the row and column selection model.
1208:     ListSelectionModel rowSel = table.getSelectionModel();
1209:     ListSelectionModel colSel = table.getColumnModel().getSelectionModel();
1210:     boolean hasFocus = table.hasFocus() && table.isEnabled()
1211:                        && rowSel.getLeadSelectionIndex() == row
1212:                        && colSel.getLeadSelectionIndex() == col;
1213: 
1214:     Component comp = rend.getTableCellRendererComponent(table,
1215:                                                        data.getValueAt(row, col),
1216:                                                        isSel, hasFocus, row, col);
1217:     
1218:     rendererPane.paintComponent(g, comp, table, bounds);
1219:     
1220:     // FIXME: this is manual painting of the Caret, why doesn't the 
1221:     // JTextField take care of this itself?
1222:     if (comp instanceof JTextField)
1223:       {
1224:         Rectangle oldClip = g.getClipBounds();
1225:         g.translate(bounds.x, bounds.y);
1226:         g.clipRect(0, 0, bounds.width, bounds.height);
1227:         ((JTextField)comp).getCaret().paint(g);
1228:         g.translate(-bounds.x, -bounds.y);
1229:         g.setClip(oldClip);
1230:       }
1231:   }
1232:   
1233:   public void paint(Graphics gfx, JComponent ignored) 
1234:   {
1235:     int ncols = table.getColumnCount();
1236:     int nrows = table.getRowCount();
1237:     if (nrows == 0 || ncols == 0)
1238:       return;
1239: 
1240:     Rectangle clip = gfx.getClipBounds();
1241:     TableColumnModel cols = table.getColumnModel();
1242: 
1243:     int height = table.getRowHeight();
1244:     int x0 = 0, y0 = 0;
1245:     int x = x0;
1246:     int y = y0;
1247: 
1248:     Dimension gap = table.getIntercellSpacing();
1249:     int ymax = clip.y + clip.height;
1250:     int xmax = clip.x + clip.width;
1251: 
1252:     // paint the cell contents
1253:     for (int c = 0; c < ncols && x < xmax; ++c)
1254:       {
1255:         y = y0;
1256:         TableColumn col = cols.getColumn(c);
1257:         int width = col.getWidth();
1258:         int halfGapWidth = gap.width / 2;
1259:         int halfGapHeight = gap.height / 2;
1260:         for (int r = 0; r < nrows && y < ymax; ++r)
1261:           {
1262:             Rectangle bounds = new Rectangle(x + halfGapWidth,
1263:                                              y + halfGapHeight + 1,
1264:                                              width - gap.width + 1,
1265:                                              height - gap.height);
1266:             if (bounds.intersects(clip))
1267:               {                                                     
1268:                 paintCell(gfx, r, c, bounds, table.getCellRenderer(r, c),
1269:                           table.getModel(),
1270:                           table.getSelectionModel().getLeadSelectionIndex(),
1271:                           table.getColumnModel().getSelectionModel().getLeadSelectionIndex());
1272:               }
1273:             y += height;
1274:           }
1275:         x += width;
1276:       }
1277: 
1278:     // tighten up the x and y max bounds
1279:     ymax = y;
1280:     xmax = x;
1281: 
1282:     Color grid = table.getGridColor();    
1283: 
1284:     // paint vertical grid lines
1285:     if (grid != null && table.getShowVerticalLines())
1286:       {    
1287:         x = x0;
1288:         Color save = gfx.getColor();
1289:         gfx.setColor(grid);
1290:         boolean paintedLine = false;
1291:         for (int c = 0; c < ncols && x < xmax; ++c)
1292:           {
1293:             x += cols.getColumn(c).getWidth();
1294:             gfx.drawLine(x, y0, x, ymax);
1295:             paintedLine = true;
1296:           }
1297:         gfx.setColor(save);
1298:       }
1299: 
1300:     // paint horizontal grid lines    
1301:     if (grid != null && table.getShowHorizontalLines())
1302:       {    
1303:         y = y0;
1304:         Color save = gfx.getColor();
1305:         gfx.setColor(grid);
1306:         boolean paintedLine = false;
1307:         for (int r = 0; r < nrows && y < ymax; ++r)
1308:           {
1309:             y += height;
1310:             gfx.drawLine(x0, y, xmax, y);
1311:             paintedLine = true;
1312:           }
1313:         gfx.setColor(save);
1314:       }
1315:   }
1316: }