1. /*
  2. * @(#)TableColumn.java 1.35 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.table;
  8. import javax.swing.*;
  9. import javax.swing.border.*;
  10. import javax.swing.event.SwingPropertyChangeSupport;
  11. import java.lang.Integer;
  12. import java.awt.Color;
  13. import java.awt.Component;
  14. import java.io.Serializable;
  15. import java.beans.PropertyChangeEvent;
  16. import java.beans.PropertyChangeListener;
  17. /**
  18. * A <B>TableColumn</B> represents all the attributes of a column in a
  19. * <B>JTable</B>, such as width, resizibility, minimum and maximum width.
  20. * In addition, the <B>TableColumn</B> provides slots for a renderer and
  21. * editor that can be used to display and edit the values in this column.
  22. * <p>
  23. * It is also possible to specify renderers and editors on a per type basis
  24. * rather than a per column basis - see the <I>setDefaultRenderer(Class)</I> method
  25. * in the <B>JTable</B>. This default mechanism is only used when the renderer (or
  26. * editor) in the <B>TableColumn</B> is <I>null</I>.
  27. * <p>
  28. * The TableColumn stores the link between the columns in the <B>JTable</B>
  29. * and the columns in the <B>TableModel</B>. This, the <I>modelIndex</I>, is the
  30. * column in the TableModel which will be queried for the data values for the
  31. * cells in this column. As the column moves around in the view this
  32. * <I>modelIndex</I> does not change.
  33. * <p>
  34. * <strong>Warning:</strong>
  35. * Serialized objects of this class will not be compatible with
  36. * future Swing releases. The current serialization support is appropriate
  37. * for short term storage or RMI between applications running the same
  38. * version of Swing. A future release of Swing will provide support for
  39. * long term persistence.
  40. *
  41. * @version 1.35 11/29/01
  42. * @author Alan Chung
  43. * @author Philip Milne
  44. * @see TableColumnModel
  45. *
  46. * @see DefaultTableColumnModel
  47. * @see JTable#getDefaultRenderer(Class)
  48. * @see JTable#getDefaultEditor(Class)
  49. * @see JTable#getCellRenderer(int, int)
  50. * @see JTable#getCellEditor(int, int)
  51. */
  52. public class TableColumn extends Object implements Serializable {
  53. //
  54. // Static Constants
  55. //
  56. /** Bound property name. */
  57. public final static String COLUMN_WIDTH_PROPERTY = "columWidth";
  58. /** Bound property name. */
  59. public final static String HEADER_VALUE_PROPERTY = "headerValue";
  60. /** Bound property name. */
  61. public final static String HEADER_RENDERER_PROPERTY = "headerRenderer";
  62. /** Bound property name. */
  63. public final static String CELL_RENDERER_PROPERTY = "cellRenderer";
  64. //
  65. // Instance Variables
  66. //
  67. /**
  68. * The index of the column in the model which is to be displayed by
  69. * this TableColumn. As columns are moved around in the view the
  70. * model index remains constant.
  71. */
  72. protected int modelIndex;
  73. /**
  74. * This object is not used internally by the drawing machinery of
  75. * the JTable. Identifiers may be set in the TableColumn as as an
  76. * optional way to tag and locate TableColumns. The table package does
  77. * not modify or invoke any methods in these identifer objects other
  78. * than the <I>equals</I> method which is used in the
  79. * <I>getColumnIndex()</I> method in the <B>DefaultTableColumnModel</B>.
  80. */
  81. protected Object identifier;
  82. /** The width of the column */
  83. protected int width;
  84. /** The minimum width of the column */
  85. protected int minWidth;
  86. /** The minimum width of the column */
  87. private int preferredWidth;
  88. /** The maximum width of the column */
  89. protected int maxWidth;
  90. /** The renderer used to draw the header of the column */
  91. protected TableCellRenderer headerRenderer;
  92. /** The header value of the column */
  93. protected Object headerValue;
  94. /** The renderer used to draw the data cells of the column */
  95. protected TableCellRenderer cellRenderer;
  96. /** The editor used to edit the data cells of the column */
  97. protected TableCellEditor cellEditor;
  98. /** Resizable flag */
  99. protected boolean isResizable;
  100. /**
  101. * Counter used to disable posting of resizing notifications until the
  102. * end of the resize
  103. */
  104. transient protected int resizedPostingDisableCount;
  105. /**
  106. * If any PropertyChangeListeners have been registered, the
  107. * changeSupport field describes them.
  108. */
  109. private SwingPropertyChangeSupport changeSupport;
  110. //
  111. // Constructors
  112. //
  113. /**
  114. * Cover method, using a default model index of 0,
  115. * default width of 75, a null renderer and a null editor.
  116. * This methods is intended for serialization.
  117. * @see #TableColumn(int, int, TableCellRenderer, TableCellEditor)
  118. */
  119. public TableColumn() {
  120. this(0);
  121. }
  122. /**
  123. * Cover method, using a default width of 75, a null renderer and a null editor.
  124. * @see #TableColumn(int, int, TableCellRenderer, TableCellEditor)
  125. */
  126. public TableColumn(int modelIndex) {
  127. this(modelIndex, 75, null, null);
  128. }
  129. /**
  130. * Cover method, using a null renderer and a null editor.
  131. * @see #TableColumn(int, int, TableCellRenderer, TableCellEditor)
  132. */
  133. public TableColumn(int modelIndex, int width) {
  134. this(modelIndex, width, null, null);
  135. }
  136. /**
  137. * Creates and initializes an instance of <B>TableColumn</B> with
  138. * <I>modelIndex</I>. The <I>modelIndex</I> is the index of the column
  139. * in the model which will supply the data for this column in the table.
  140. * The modelIndex does not change as the columns are reordered in the view.
  141. * The width parameter is used to set both the preferredWidth for this
  142. * column and the intial width. The renderer and editor are the objects
  143. * used repsectively to render and edit values in this column. When
  144. * these are null, default values, provided by the getDefaultRenderer(Class)
  145. * and getDefaultEditor(Class) methods in the JTable are used to
  146. * provide defaults based on the type of the data in this column.
  147. * This column-centric rendering strategy can be circumvented by
  148. * overriding the getCellRenderer() methods in the JTable.
  149. * <p>
  150. *
  151. * @param modelIndex the column in the model which provides the values for this column
  152. * @see JTable#getDefaultRenderer(Class)
  153. * @see JTable#getDefaultEditor(Class)
  154. * @see JTable#getCellRenderer(int, int)
  155. * @see JTable#getCellEditor(int, int)
  156. */
  157. public TableColumn(int modelIndex, int width,
  158. TableCellRenderer cellRenderer,
  159. TableCellEditor cellEditor) {
  160. super();
  161. this.modelIndex = modelIndex;
  162. this.width = width;
  163. this.preferredWidth = width;
  164. this.cellRenderer = cellRenderer;
  165. this.cellEditor = cellEditor;
  166. // Set other instance variables to default values.
  167. minWidth = 15;
  168. maxWidth = Integer.MAX_VALUE;
  169. isResizable = true;
  170. resizedPostingDisableCount = 0;
  171. setHeaderRenderer(createDefaultHeaderRenderer());
  172. headerValue = null;
  173. }
  174. //
  175. // Modifying and Querying attributes
  176. //
  177. /**
  178. * Sets the model index for this column. The model index is the
  179. * index of the column in the model that will be displayed by this
  180. * TableColumn. As the TableColumn is moved around in the view
  181. * the model index remains constant.
  182. */
  183. public void setModelIndex(int anIndex)
  184. {
  185. modelIndex = anIndex;
  186. }
  187. /**
  188. * Gets the model index for this column.
  189. */
  190. public int getModelIndex()
  191. {
  192. return modelIndex;
  193. }
  194. /**
  195. * Sets the <B>TableColumn</B>'s identifier to <I>anIdentifier</I>.
  196. * Note identifiers are not used by the JTable, they are purely a
  197. * convenience for the external tagging and location of columns.
  198. *
  199. * @param anIdentifier an identifier for this column
  200. * @see #getIdentifier
  201. */
  202. public void setIdentifier(Object anIdentifier)
  203. {
  204. identifier = anIdentifier;
  205. }
  206. /**
  207. * Returns the identifier object for this column. Note identifiers are not
  208. * used by the JTable, they are purely a convenience for external use.
  209. * If the identifier is <I>null</I> <I>getIdentifier()</I> returns
  210. * <code>getHeaderValue()</code> as a default.
  211. *
  212. * @return the idenitifer object for this column
  213. * @see #setIdentifier
  214. */
  215. public Object getIdentifier()
  216. {
  217. return (identifier != null) ? identifier : getHeaderValue();
  218. }
  219. /**
  220. * Sets the <B>TableCellRenderer</B> used to draw the <B>TableColumn's</B>
  221. * header to <I>aRenderer</I>. Posts a bound property change notification
  222. * with the name HEADER_RENDERER_PROPERTY.
  223. *
  224. * @exception IllegalArgumentException if <I>aRenderer</I> is null.
  225. * @param aRenderer the new header renderer
  226. * @see #getHeaderRenderer
  227. */
  228. public void setHeaderRenderer(TableCellRenderer aRenderer)
  229. {
  230. TableCellRenderer oldRenderer = headerRenderer;
  231. if (aRenderer == null) {
  232. throw new IllegalArgumentException("Object is null");
  233. }
  234. headerRenderer = aRenderer;
  235. // Post header renderer changed event notification
  236. if (changeSupport != null) {
  237. changeSupport.firePropertyChange(HEADER_RENDERER_PROPERTY,
  238. oldRenderer, headerRenderer);
  239. }
  240. }
  241. /**
  242. * Returns the <B>TableCellRenderer</B> used to draw the header of the
  243. * <B>TableColumn</B>. The default header renderer is a
  244. * <B>JCellRenderer</B> initialized with a <B>JLabel</B>.
  245. *
  246. * @return the <B>TableCellRenderer</B> used to draw the header
  247. * @see #setHeaderRenderer
  248. * @see #setHeaderValue
  249. */
  250. public TableCellRenderer getHeaderRenderer()
  251. {
  252. return headerRenderer;
  253. }
  254. /**
  255. * Sets the <B>Object</B> used as the value for the headerRenderer
  256. * Posts a bound property change notification with the name
  257. * HEADER_VALUE_PROPERTY.
  258. *
  259. * @param aValue the new header value
  260. * @see #getHeaderValue
  261. */
  262. public void setHeaderValue(Object aValue)
  263. {
  264. Object oldValue = headerValue;
  265. headerValue = aValue;
  266. // Post header value changed event notification
  267. if (changeSupport != null) {
  268. changeSupport.firePropertyChange(HEADER_VALUE_PROPERTY,
  269. oldValue, headerValue);
  270. }
  271. }
  272. /**
  273. * Returns the <B>Object</B> used as the value for the header renderer.
  274. *
  275. * @return the <B>Object</B> used as the value for the header renderer
  276. * @see #setHeaderValue
  277. */
  278. public Object getHeaderValue() {
  279. return headerValue;
  280. }
  281. /**
  282. * Sets the <B>TableCellRenderer</B> used by <B>JTable</B> to draw
  283. * individual values for this column to <I>aRenderer</I>. Posts a
  284. * bound property change notification with the name CELL_RENDERER_PROPERTY.
  285. *
  286. * @param aRenderer the new data cell renderer
  287. * @see #getCellRenderer
  288. */
  289. public void setCellRenderer(TableCellRenderer aRenderer)
  290. {
  291. TableCellRenderer oldRenderer = cellRenderer;
  292. cellRenderer = aRenderer;
  293. // Post cell renderer changed event notification
  294. if (changeSupport != null) {
  295. changeSupport.firePropertyChange(CELL_RENDERER_PROPERTY,
  296. oldRenderer, cellRenderer);
  297. }
  298. }
  299. /**
  300. * Returns the <B>TableCellRenderer</B> used by the <B>JTable</B> to draw
  301. * values for this column. The <I>cellRenderer</I> of the column not
  302. * only controls the visual look for the column, but is also used to
  303. * interpret the value object supplied by the TableModel. When the
  304. * <I>cellRenderer</I> is null, the JTable uses a default renderer based on
  305. * class of the cells in that column. The default value for a
  306. * <I>cellRenderer</I> is null.
  307. *
  308. * @return the <B>TableCellRenderer</B> used by the <B>JTable</B> to
  309. * draw values for this column
  310. * @see #setCellRenderer
  311. * @see JTable#setDefaultRenderer
  312. */
  313. public TableCellRenderer getCellRenderer()
  314. {
  315. return cellRenderer;
  316. }
  317. /**
  318. * Sets the <B>TableCellEditor</B> used by <B>JTable</B> to draw individual
  319. * values for this column to <I>anEditor</I>.
  320. *
  321. * @param anEditor the new data cell editor
  322. * @see #getCellEditor
  323. */
  324. public void setCellEditor(TableCellEditor anEditor)
  325. {
  326. cellEditor = anEditor;
  327. }
  328. /**
  329. * Returns the <B>TableCellEditor</B> used by the <B>JTable</B> to draw
  330. * values for this column. The <I>cellEditor</I> of the column not
  331. * only controls the visual look for the column, but is also used to
  332. * interpret the value object supplied by the TableModel. When the
  333. * <I>cellEditor</I> is null, the JTable uses a default editor based on
  334. * class of the cells in that column. The default value for a
  335. * <I>cellEditor</I> is null.
  336. *
  337. *
  338. * @return the <B>TableCellEditor</B> used by the <B>JTable</B> to
  339. * draw values for this column
  340. * @see #setCellEditor
  341. * @see JTable#setDefaultEditor
  342. */
  343. public TableCellEditor getCellEditor()
  344. {
  345. return cellEditor;
  346. }
  347. /**
  348. * This method should not be used to set the widths of columns in the JTable -
  349. * use, setPreferredWidth() instead. Like a layout manager in the
  350. * AWT, the JTable adjusts a column's width automatically whenever the
  351. * table itself changes size, or a column's preferred width is changed.
  352. * Setting widths programmatically therefore has no long term effect.
  353. * <p>
  354. * This methods, sets this column's width to <I>newWidth</I>.
  355. * If <I>newWidth</I> exceeds the minimum or maximum width,
  356. * it's adjusted to the appropriate limiting value. Posts a bound property
  357. * change notification with the name COLUMN_WIDTH_PROPERTY.
  358. * <p>
  359. * @param newWidth The new width value
  360. * @see #getWidth
  361. * @see #setMinWidth
  362. * @see #setMaxWidth
  363. * @see #setPreferredWidth
  364. * @see JTable#sizeColumnsToFit(int)
  365. */
  366. public void setWidth(int width)
  367. {
  368. int oldWidth = this.width;
  369. // Set the width, and check min & max
  370. this.width = Math.min(Math.max(width, minWidth), maxWidth);
  371. // Post resize event notification
  372. if (changeSupport != null && this.width != oldWidth) {
  373. changeSupport.firePropertyChange(COLUMN_WIDTH_PROPERTY,
  374. new Integer(oldWidth), new Integer(this.width));
  375. }
  376. }
  377. /**
  378. * Returns the width of the <B>TableColumn</B>. The default width is
  379. * 75.
  380. *
  381. * @return the width of the <B>TableColumn</B>
  382. * @see #setWidth
  383. */
  384. public int getWidth()
  385. {
  386. return width;
  387. }
  388. /**
  389. * Sets this column's preferred width to <I>preferredWidth</I>.
  390. * If <I>preferredWidth</I> exceeds the minimum or maximum width,
  391. * it's adjusted to the appropriate limiting value.
  392. * <p>
  393. * For details on how the widths of columns in the JTable
  394. * (and JTableHeader) are calculated from the preferredWidth,
  395. * see the sizeColumnsToFit(int) method in the JTable.
  396. *
  397. * @param preferredWidth The new preferred width.
  398. * @see #getPreferredWidth
  399. * @see JTable#sizeColumnsToFit(int)
  400. */
  401. public void setPreferredWidth(int preferredWidth) {
  402. this.preferredWidth = Math.min(Math.max(preferredWidth, minWidth), maxWidth);
  403. }
  404. /**
  405. * Returns the preferred width of the <B>TableColumn</B>.
  406. * The default preferred width is 75.
  407. *
  408. * @return the width of the <B>TableColumn</B>
  409. * @see #setPreferredWidth
  410. */
  411. public int getPreferredWidth() {
  412. return preferredWidth;
  413. }
  414. /**
  415. * Sets the <B>TableColumn's</B> minimum width to <I>newMinWidth</I>,
  416. * also adjusting the current width if it's less than this value.
  417. *
  418. * @param newMinWidth the new minimum width value
  419. * @see #getMinWidth
  420. * @see #setPreferredWidth
  421. * @see #setMaxWidth
  422. */
  423. public void setMinWidth(int minWidth)
  424. {
  425. this.minWidth = Math.max(minWidth, 0);
  426. if (width < minWidth) {
  427. setWidth(minWidth);
  428. }
  429. }
  430. /**
  431. * Returns the minimum width for the <B>TableColumn</B>. The
  432. * <B>TableColumn's</B> width can't be made less than this either
  433. * by the user or programmatically. The default minWidth is 15.
  434. *
  435. * @return the minimum width for the <B>TableColumn</B>
  436. * @see #setMinWidth
  437. */
  438. public int getMinWidth()
  439. {
  440. return minWidth;
  441. }
  442. /**
  443. * Sets the <B>TableColumn's</B> maximum width to <I>newMaxWidth</I>,
  444. * also adjusting the current width if it's greater than this value.
  445. *
  446. * @param newMaxWidth the new maximum width value
  447. * @see #getMaxWidth
  448. * @see #setPreferredWidth
  449. * @see #setMinWidth
  450. */
  451. public void setMaxWidth(int maxWidth)
  452. {
  453. this.maxWidth = Math.max(minWidth, maxWidth);
  454. if (width > maxWidth) {
  455. this.setWidth(maxWidth);
  456. }
  457. }
  458. /**
  459. * Returns the maximum width for the <B>TableColumn</B>. The
  460. * <B>TableColumn's</B> width can't be made larger than this
  461. * either by the user or programmatically. The default maxWidth
  462. * is Integer.MAX_VALUE.
  463. *
  464. * @return the maximum width for the <B>TableColumn</B>.
  465. * @see #setMaxWidth
  466. */
  467. public int getMaxWidth()
  468. {
  469. return maxWidth;
  470. }
  471. /**
  472. * Sets whether the user can resize the receiver in its
  473. * <B>JTableView</B>.
  474. *
  475. * @param flag true if the column isResizable
  476. * @see #getResizable
  477. */
  478. public void setResizable(boolean flag)
  479. {
  480. isResizable = flag;
  481. }
  482. /**
  483. * Returns true if the user is allowed to resize the <B>TableColumn</B>
  484. * width, false otherwise. You can change the width programmatically
  485. * regardless of this setting. The default is true.
  486. *
  487. * @return true if the user is allowed to resize the <B>TableColumn</B>
  488. * width, false otherwise.
  489. * @see #setResizable
  490. */
  491. public boolean getResizable()
  492. {
  493. return isResizable;
  494. }
  495. /**
  496. * Resizes the <B>TableColumn</B> to fit the width of its header cell.
  497. * If the maximum width is less than the width of the header, the
  498. * maximum is increased to the header's width. Similarly, if the
  499. * minimum width is greater than the width of the header, the minimum
  500. * is reduced to the header's width.
  501. *
  502. * @see #setPreferredWidth
  503. */
  504. public void sizeWidthToFit() {
  505. // Get the preferred width of the header
  506. Component comp;
  507. comp = this.getHeaderRenderer().getTableCellRendererComponent(null,
  508. getHeaderValue(), false, false, 0, 0);
  509. int headerWidth = comp.getPreferredSize().width;
  510. // Have to adjust the max or min before setting the width
  511. if (headerWidth > this.getMaxWidth())
  512. this.setMaxWidth(headerWidth);
  513. if (headerWidth < this.getMinWidth())
  514. this.setMinWidth(headerWidth);
  515. // Set the width
  516. this.setWidth(headerWidth);
  517. }
  518. /**
  519. * Turns off listener-notifications would otherwise occur
  520. * when a column is resized.
  521. */
  522. public void disableResizedPosting() {
  523. resizedPostingDisableCount++;
  524. }
  525. /**
  526. * Turns on listener-notifications so that listeners are once
  527. * again informed when a column is resized.
  528. */
  529. public void enableResizedPosting() {
  530. resizedPostingDisableCount--;
  531. }
  532. //
  533. // Property Change Support
  534. //
  535. /**
  536. * Add a PropertyChangeListener to the listener list.
  537. * The listener is registered for all properties.
  538. * <p>
  539. * A PropertyChangeEvent will get fired in response to an
  540. * explicit setFont, setBackground, or SetForeground on the
  541. * current component. Note that if the current component is
  542. * inheriting its foreground, background, or font from its
  543. * container, then no event will be fired in response to a
  544. * change in the inherited property.
  545. *
  546. * @param listener The PropertyChangeListener to be added
  547. */
  548. public synchronized void addPropertyChangeListener(
  549. PropertyChangeListener listener) {
  550. if (changeSupport == null) {
  551. changeSupport = new SwingPropertyChangeSupport(this);
  552. }
  553. changeSupport.addPropertyChangeListener(listener);
  554. }
  555. /**
  556. * Remove a PropertyChangeListener from the listener list.
  557. * This removes a PropertyChangeListener that was registered
  558. * for all properties.
  559. *
  560. * @param listener The PropertyChangeListener to be removed
  561. */
  562. public synchronized void removePropertyChangeListener(
  563. PropertyChangeListener listener) {
  564. if (changeSupport != null) {
  565. changeSupport.removePropertyChangeListener(listener);
  566. }
  567. }
  568. //
  569. // Protected Methods
  570. //
  571. protected TableCellRenderer createDefaultHeaderRenderer() {
  572. DefaultTableCellRenderer label = new DefaultTableCellRenderer() {
  573. public Component getTableCellRendererComponent(JTable table, Object value,
  574. boolean isSelected, boolean hasFocus, int row, int column) {
  575. if (table != null) {
  576. JTableHeader header = table.getTableHeader();
  577. if (header != null) {
  578. setForeground(header.getForeground());
  579. setBackground(header.getBackground());
  580. setFont(header.getFont());
  581. }
  582. }
  583. setText((value == null) ? "" : value.toString());
  584. setBorder(UIManager.getBorder("TableHeader.cellBorder"));
  585. return this;
  586. }
  587. };
  588. label.setHorizontalAlignment(JLabel.CENTER);
  589. return label;
  590. }
  591. } // End of class TableColumn