Source for javax.swing.table.DefaultTableColumnModel

   1: /* DefaultTableColumnModel.java --
   2:    Copyright (C) 2002, 2004, 2005  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.table;
  40: 
  41: import java.beans.PropertyChangeEvent;
  42: import java.beans.PropertyChangeListener;
  43: import java.io.Serializable;
  44: import java.util.Enumeration;
  45: import java.util.EventListener;
  46: import java.util.Vector;
  47: 
  48: import javax.swing.DefaultListSelectionModel;
  49: import javax.swing.ListSelectionModel;
  50: import javax.swing.event.ChangeEvent;
  51: import javax.swing.event.EventListenerList;
  52: import javax.swing.event.ListSelectionEvent;
  53: import javax.swing.event.ListSelectionListener;
  54: import javax.swing.event.TableColumnModelEvent;
  55: import javax.swing.event.TableColumnModelListener;
  56: 
  57: /**
  58:  * DefaultTableColumnModel
  59:  * @author    Andrew Selkirk
  60:  * @version    1.0
  61:  */
  62: public class DefaultTableColumnModel
  63:   implements TableColumnModel, PropertyChangeListener, ListSelectionListener,
  64:              Serializable
  65: {
  66:   private static final long serialVersionUID = 6580012493508960512L;
  67: 
  68:   /**
  69:    * Columns that this model keeps track of.
  70:    */
  71:   protected Vector tableColumns;
  72: 
  73:   /**
  74:    * Selection Model that keeps track of columns selection
  75:    */
  76:   protected ListSelectionModel selectionModel;
  77: 
  78:   /**
  79:    * Space between two columns. By default it is set to 1
  80:    */
  81:   protected int columnMargin;
  82: 
  83:   /**
  84:    * listenerList keeps track of all listeners registered with this model
  85:    */
  86:   protected EventListenerList listenerList = new EventListenerList();
  87: 
  88:   /**
  89:    * changeEvent is fired when change occurs in one of the columns properties
  90:    */
  91:   protected transient ChangeEvent changeEvent = new ChangeEvent(this);
  92: 
  93:   /**
  94:    * Indicates whether columns can be selected 
  95:    */
  96:   protected boolean columnSelectionAllowed;
  97: 
  98:   /**
  99:    * Total width of all the columns in this model
 100:    */
 101:   protected int totalColumnWidth;
 102: 
 103:   /**
 104:    * Constructor DefaultTableColumnModel
 105:    */
 106:   public DefaultTableColumnModel()
 107:   {
 108:     tableColumns = new Vector();
 109:     setSelectionModel(createSelectionModel());
 110:     columnMargin = 1;
 111:     columnSelectionAllowed = false;
 112:   }
 113: 
 114:   /**
 115:    * addColumn adds column to the model. This method fires ColumnAdded 
 116:    * event to model's registered TableColumnModelListeners.
 117:    *
 118:    * @param col column to add
 119:    */
 120:   public void addColumn(TableColumn col)
 121:   {
 122:     if (col == null)
 123:       throw new IllegalArgumentException("Null 'col' argument.");
 124:     tableColumns.add(col);
 125:     invalidateWidthCache();
 126:     fireColumnAdded(new TableColumnModelEvent(this, 0, tableColumns.size() - 1));
 127:   }
 128: 
 129:   /**
 130:    * removeColumn removes table column from the model. This method fires 
 131:    * ColumnRemoved event to model's registered TableColumnModelListeners.
 132:    *
 133:    * @param col column to be removed
 134:    */
 135:   public void removeColumn(TableColumn col)
 136:   {
 137:     int index = this.tableColumns.indexOf(col);
 138:     if (index < 0)
 139:       return;
 140:     fireColumnRemoved(new TableColumnModelEvent(this, index, 0));    
 141:     tableColumns.remove(col);
 142:     invalidateWidthCache();
 143:   }
 144: 
 145:   /**
 146:    * moveColumn moves column at index i to index j. This method fires
 147:    * ColumnMoved event to model's registered TableColumnModelListeners.
 148:    *
 149:    * @param i index of the column that will be moved
 150:    * @param j index of column's new location
 151:    */
 152:   public void moveColumn(int i, int j)
 153:   {
 154:     int columnCount = getColumnCount();
 155:     if (i < 0 || i >= columnCount)
 156:       throw new IllegalArgumentException("Index 'i' out of range.");
 157:     if (j < 0 || j >= columnCount)
 158:       throw new IllegalArgumentException("Index 'j' out of range.");
 159:     Object column = tableColumns.remove(i);
 160:     tableColumns.add(j, column);
 161:     fireColumnAdded(new TableColumnModelEvent(this, i, j));
 162:   }
 163: 
 164:   /**
 165:    * setColumnMargin sets margin of the columns.
 166:    * @param m new column margin
 167:    */
 168:   public void setColumnMargin(int m)
 169:   {
 170:     columnMargin = m;
 171:     fireColumnMarginChanged();
 172:   }
 173: 
 174:   /**
 175:    * getColumnCount returns number of columns in the model
 176:    * @return int number of columns in the model
 177:    */
 178:   public int getColumnCount()
 179:   {
 180:     return tableColumns.size();
 181:   }
 182: 
 183:   /**
 184:    * getColumns
 185:    * @return Enumeration
 186:    */
 187:   public Enumeration getColumns()
 188:   {
 189:     return tableColumns.elements();
 190:   }
 191: 
 192:   /**
 193:    * Returns the index of the {@link TableColumn} with the given identifier.
 194:    *
 195:    * @param identifier  the identifier (<code>null</code> not permitted).
 196:    * 
 197:    * @return The index of the {@link TableColumn} with the given identifier.
 198:    * 
 199:    * @throws IllegalArgumentException if <code>identifier</code> is 
 200:    *         <code>null</code> or there is no column with that identifier.
 201:    */
 202:   public int getColumnIndex(Object identifier)
 203:   {
 204:     if (identifier == null)
 205:       throw new IllegalArgumentException("Null identifier.");
 206:     int columnCount = tableColumns.size();
 207:     for (int i = 0; i < columnCount; i++) 
 208:     {
 209:       TableColumn tc = (TableColumn) tableColumns.get(i);
 210:       if (identifier.equals(tc.getIdentifier()))
 211:         return i;
 212:     }
 213:     throw new IllegalArgumentException("No TableColumn with that identifier.");
 214:   }
 215: 
 216:   /**
 217:    * getColumn returns column at the specified index
 218:    * @param i index of the column 
 219:    * @return TableColumn column at the specified index
 220:    */
 221:   public TableColumn getColumn(int i)
 222:   {
 223:     return (TableColumn) tableColumns.get(i);
 224:   }
 225: 
 226:   /**
 227:    * getColumnMargin returns column margin
 228:    * @return int column margin
 229:    */
 230:   public int getColumnMargin()
 231:   {
 232:     return columnMargin;
 233:   }
 234: 
 235:   /**
 236:    * getColumnIndexAtX returns column that contains specified x-coordinate.
 237:    * @param x x-coordinate that column should contain
 238:    * @return int index of the column that contains specified x-coordinate relative
 239:    * to this column model
 240:    */
 241:   public int getColumnIndexAtX(int x)
 242:   {    
 243:     for (int i = 0; i < tableColumns.size(); ++i)
 244:       {
 245:         int w = ((TableColumn)tableColumns.get(i)).getWidth();
 246:         if (0 <= x && x < w)
 247:           return i;
 248:         else
 249:           x -= w;
 250:       }
 251:     return -1;
 252:   }
 253: 
 254:   /**
 255:    * getTotalColumnWidth returns total width of all the columns including
 256:    * column's margins.
 257:    *
 258:    * @return total width of all the columns
 259:    */
 260:   public int getTotalColumnWidth()
 261:   {
 262:     if (totalColumnWidth == -1)
 263:       recalcWidthCache();
 264:     return totalColumnWidth;
 265:   }
 266: 
 267:   /**
 268:    * setSelectionModel sets selection model that will be used by this ColumnTableModel
 269:    * to keep track of currently selected columns
 270:    *
 271:    * @param model new selection model
 272:    * @exception IllegalArgumentException if model is null
 273:    */
 274:   public void setSelectionModel(ListSelectionModel model)
 275:   {
 276:     if (model == null)
 277:       throw new IllegalArgumentException();
 278:     
 279:     selectionModel = model;
 280:     selectionModel.addListSelectionListener(this);
 281:   }
 282: 
 283:   /**
 284:    * getSelectionModel returns selection model
 285:    * @return ListSelectionModel selection model
 286:    */
 287:   public ListSelectionModel getSelectionModel()
 288:   {
 289:     return selectionModel;
 290:   }
 291: 
 292:   /**
 293:    * setColumnSelectionAllowed sets whether column selection is allowed
 294:    * or not.
 295:    *
 296:    * @param flag true if column selection is allowed and false otherwise
 297:    */
 298:   public void setColumnSelectionAllowed(boolean flag)
 299:   {
 300:     columnSelectionAllowed = flag;
 301:   }
 302: 
 303:   /**
 304:    * getColumnSelectionAllowed indicates whether column selection is 
 305:    * allowed or not.
 306:    *
 307:    * @return boolean true if column selection is allowed and false otherwise.
 308:    */
 309:   public boolean getColumnSelectionAllowed()
 310:   {
 311:     return columnSelectionAllowed;
 312:   }
 313: 
 314:   /**
 315:    * getSelectedColumns returns array containing indexes of currently 
 316:    * selected columns
 317:    *
 318:    * @return int[] array containing indexes of currently selected columns
 319:    */
 320:   public int[] getSelectedColumns()
 321:   {
 322:     // FIXME: Implementation of this method was taken from private method 
 323:     // JTable.getSelections(), which is used in various places in JTable
 324:     // including selected row calculations and cannot be simply removed.
 325:     // This design should be improved to illuminate duplication of code.
 326:     
 327:     ListSelectionModel lsm = this.selectionModel;    
 328:     int sz = getSelectedColumnCount();
 329:     int [] ret = new int[sz];
 330: 
 331:     int lo = lsm.getMinSelectionIndex();
 332:     int hi = lsm.getMaxSelectionIndex();
 333:     int j = 0;
 334:     java.util.ArrayList ls = new java.util.ArrayList();
 335:     if (lo != -1 && hi != -1)
 336:       {
 337:         switch (lsm.getSelectionMode())
 338:           {
 339:           case ListSelectionModel.SINGLE_SELECTION:
 340:             ret[0] = lo;
 341:             break;      
 342:       
 343:           case ListSelectionModel.SINGLE_INTERVAL_SELECTION:            
 344:             for (int i = lo; i <= hi; ++i)
 345:               ret[j++] = i;
 346:             break;
 347:             
 348:           case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION:        
 349:             for (int i = lo; i <= hi; ++i)
 350:               if (lsm.isSelectedIndex(i))        
 351:                 ret[j++] = i;
 352:             break;
 353:           }
 354:       }
 355:     return ret;
 356:   }
 357: 
 358:   /**
 359:    * getSelectedColumnCount returns number of currently selected columns
 360:    * @return int number of currently selected columns
 361:    */
 362:   public int getSelectedColumnCount()
 363:   {
 364:     // FIXME: Implementation of this method was taken from private method 
 365:     // JTable.countSelections(), which is used in various places in JTable
 366:     // including selected row calculations and cannot be simply removed.
 367:     // This design should be improved to illuminate duplication of code.
 368:    
 369:     ListSelectionModel lsm = this.selectionModel;
 370:     int lo = lsm.getMinSelectionIndex();
 371:     int hi = lsm.getMaxSelectionIndex();
 372:     int sum = 0;
 373:     
 374:     if (lo != -1 && hi != -1)
 375:       {
 376:         switch (lsm.getSelectionMode())
 377:           {
 378:           case ListSelectionModel.SINGLE_SELECTION:
 379:             sum = 1;
 380:             break;
 381:             
 382:           case ListSelectionModel.SINGLE_INTERVAL_SELECTION:
 383:             sum = hi - lo + 1;
 384:             break;
 385:             
 386:           case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION:        
 387:             for (int i = lo; i <= hi; ++i)
 388:               if (lsm.isSelectedIndex(i))        
 389:                 ++sum;
 390:             break;
 391:           }
 392:       }
 393:      
 394:      return sum;
 395:   }
 396: 
 397:   /**
 398:    * addColumnModelListener adds specified listener to the model's
 399:    * listener list
 400:    *
 401:    * @param listener the listener to add
 402:    */
 403:   public void addColumnModelListener(TableColumnModelListener listener)
 404:   {
 405:     listenerList.add(TableColumnModelListener.class, listener);
 406:   }
 407: 
 408:   /**
 409:    * removeColumnModelListener removes specified listener from the model's 
 410:    * listener list.
 411:    *
 412:    * @param listener the listener to remove
 413:    */
 414:   public void removeColumnModelListener(TableColumnModelListener listener)
 415:   {
 416:     listenerList.remove(TableColumnModelListener.class, listener);
 417:   }
 418: 
 419:   /**
 420:    * @since 1.4
 421:    */
 422:   public TableColumnModelListener[] getColumnModelListeners()
 423:   {
 424:     return (TableColumnModelListener[])
 425:       listenerList.getListeners(TableColumnModelListener.class);
 426:   }      
 427: 
 428:   /**
 429:    * fireColumnAdded fires TableColumnModelEvent to registered 
 430:    * TableColumnModelListeners to indicate that column was added
 431:    *
 432:    * @param e TableColumnModelEvent
 433:    */
 434:   protected void fireColumnAdded(TableColumnModelEvent e)
 435:   {    
 436:     TableColumnModelListener[] listeners = getColumnModelListeners();
 437: 
 438:     for (int i=0; i< listeners.length; i++)
 439:       listeners[i].columnAdded(e);        
 440:   }
 441: 
 442:   /**
 443:    * fireColumnAdded fires TableColumnModelEvent to registered 
 444:    * TableColumnModelListeners to indicate that column was removed
 445:    *
 446:    * @param e TableColumnModelEvent
 447:    */
 448:   protected void fireColumnRemoved(TableColumnModelEvent e)
 449:   {
 450:     TableColumnModelListener[] listeners = getColumnModelListeners();
 451: 
 452:     for (int i=0; i< listeners.length; i++)
 453:       listeners[i].columnRemoved(e);        
 454:   }
 455: 
 456:   /**
 457:    * fireColumnAdded fires TableColumnModelEvent to registered 
 458:    * TableColumnModelListeners to indicate that column was moved
 459:    *
 460:    * @param e TableColumnModelEvent
 461:    */
 462:   protected void fireColumnMoved(TableColumnModelEvent e)
 463:   {
 464:     TableColumnModelListener[] listeners = getColumnModelListeners();
 465: 
 466:     for (int i=0; i< listeners.length; i++)
 467:       listeners[i].columnMoved(e);        
 468:   }
 469: 
 470:   /**
 471:    * fireColumnSelectionChanged fires TableColumnModelEvent to model's
 472:    * registered TableColumnModelListeners to indicate that different column 
 473:    * was selected.
 474:    *
 475:    * @param evt ListSelectionEvent
 476:    */
 477:   protected void fireColumnSelectionChanged(ListSelectionEvent evt)
 478:   {
 479:     EventListener [] listeners = getListeners(TableColumnModelListener.class);
 480:     for (int i = 0; i < listeners.length; ++i)
 481:       ((TableColumnModelListener)listeners[i]).columnSelectionChanged(evt);
 482:   }
 483: 
 484:   /**
 485:    * fireColumnMarginChanged fires TableColumnModelEvent to model's
 486:    * registered TableColumnModelListeners to indicate that column margin
 487:    * was changed.
 488:    */
 489:   protected void fireColumnMarginChanged()
 490:   {
 491:     EventListener [] listeners = getListeners(TableColumnModelListener.class);
 492:     for (int i = 0; i < listeners.length; ++i)
 493:       ((TableColumnModelListener)listeners[i]).columnMarginChanged(changeEvent);
 494:   }
 495: 
 496:   /**
 497:    * getListeners returns currently registered listeners with this model.
 498:    * @param listenerType type of listeners to return
 499:    *
 500:    * @return EventListener[] array of model's listeners of the specified type
 501:    */
 502:   public EventListener[] getListeners(Class listenerType)
 503:   {
 504:     return listenerList.getListeners(listenerType);
 505:   }
 506: 
 507:   /**
 508:    * propertyChange handles changes occuring in the properties of the
 509:    * model's columns. 
 510:    *
 511:    * @param evt PropertyChangeEvent
 512:    */
 513:   public void propertyChange(PropertyChangeEvent evt)
 514:   {
 515:     if (evt.getPropertyName().equals(TableColumn.COLUMN_WIDTH_PROPERTY))
 516:     invalidateWidthCache(); 
 517:   }
 518: 
 519:   /**
 520:    * valueChanged handles changes in the selectionModel.
 521:    * @param e ListSelectionEvent
 522:    */
 523:   public void valueChanged(ListSelectionEvent e)
 524:   {
 525:     fireColumnSelectionChanged(e);
 526:   }
 527: 
 528:   /**
 529:    * createSelectionModel creates selection model that will keep track
 530:    * of currently selected column(s)
 531:    *
 532:    * @return ListSelectionModel selection model of the columns
 533:    */
 534:   protected ListSelectionModel createSelectionModel()
 535:   {    
 536:     return new DefaultListSelectionModel();
 537:   }
 538: 
 539:   /**
 540:    * recalcWidthCache calculates total width of the columns.
 541:    * If the current cache of the total width is in invalidated state, 
 542:    * then width is recalculated. Otherwise nothing is done.
 543:    */
 544:   protected void recalcWidthCache()
 545:   {
 546:     if (totalColumnWidth == -1)
 547:       {
 548:         totalColumnWidth = 0;
 549:         for (int i = 0; i < tableColumns.size(); ++i)
 550:           {
 551:             totalColumnWidth += ((TableColumn)tableColumns.get(i)).getWidth();
 552:           }
 553:       }
 554:   }
 555: 
 556:   /**
 557:    * invalidateWidthCache
 558:    */
 559:   private void invalidateWidthCache()
 560:   {
 561:     totalColumnWidth = -1;
 562:   }
 563: }