001: /**
002: * L2FProd.com Common Components 7.3 License.
003: *
004: * Copyright 2005-2007 L2FProd.com
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */package com.l2fprod.common.propertysheet;
018:
019: import com.l2fprod.common.propertysheet.PropertySheetTableModel.Item;
020: import com.l2fprod.common.swing.HeaderlessColumnResizer;
021:
022: import java.awt.Color;
023: import java.awt.Component;
024: import java.awt.Dimension;
025: import java.awt.Graphics;
026: import java.awt.Insets;
027: import java.awt.event.ActionEvent;
028: import java.awt.event.KeyEvent;
029: import java.awt.event.MouseAdapter;
030: import java.awt.event.MouseEvent;
031: import java.beans.PropertyEditor;
032:
033: import javax.swing.AbstractAction;
034: import javax.swing.CellEditor;
035: import javax.swing.Icon;
036: import javax.swing.JTable;
037: import javax.swing.KeyStroke;
038: import javax.swing.ListSelectionModel;
039: import javax.swing.UIManager;
040: import javax.swing.border.Border;
041: import javax.swing.event.TableModelEvent;
042: import javax.swing.event.TableModelListener;
043: import javax.swing.table.DefaultTableCellRenderer;
044: import javax.swing.table.TableCellEditor;
045: import javax.swing.table.TableCellRenderer;
046: import javax.swing.table.TableModel;
047:
048: /**
049: * A table which allows the editing of Properties through
050: * PropertyEditors. The PropertyEditors can be changed by using the
051: * PropertyEditorRegistry.
052: */
053: public class PropertySheetTable extends JTable {
054:
055: private static final int HOTSPOT_SIZE = 18;
056:
057: private static final String TREE_EXPANDED_ICON_KEY = "Tree.expandedIcon";
058: private static final String TREE_COLLAPSED_ICON_KEY = "Tree.collapsedIcon";
059: private static final String TABLE_BACKGROUND_COLOR_KEY = "Table.background";
060: private static final String TABLE_FOREGROUND_COLOR_KEY = "Table.foreground";
061: private static final String TABLE_SELECTED_BACKGROUND_COLOR_KEY = "Table.selectionBackground";
062: private static final String TABLE_SELECTED_FOREGROUND_COLOR_KEY = "Table.selectionForeground";
063: private static final String PANEL_BACKGROUND_COLOR_KEY = "Panel.background";
064:
065: private PropertyEditorFactory editorFactory;
066: private PropertyRendererFactory rendererFactory;
067:
068: private TableCellRenderer nameRenderer;
069:
070: private boolean wantsExtraIndent = false;
071:
072: /**
073: * Cancel editing when editing row is changed
074: */
075: private TableModelListener cancelEditing;
076:
077: // Colors used by renderers
078: private Color categoryBackground;
079: private Color categoryForeground;
080: private Color propertyBackground;
081: private Color propertyForeground;
082: private Color selectedPropertyBackground;
083: private Color selectedPropertyForeground;
084: private Color selectedCategoryBackground;
085: private Color selectedCategoryForeground;
086:
087: public PropertySheetTable() {
088: this (new PropertySheetTableModel());
089: }
090:
091: public PropertySheetTable(PropertySheetTableModel dm) {
092: super (dm);
093: initDefaultColors();
094:
095: // select only one property at a time
096: getSelectionModel().setSelectionMode(
097: ListSelectionModel.SINGLE_SELECTION);
098:
099: // hide the table header, we do not need it
100: Dimension nullSize = new Dimension(0, 0);
101: getTableHeader().setPreferredSize(nullSize);
102: getTableHeader().setMinimumSize(nullSize);
103: getTableHeader().setMaximumSize(nullSize);
104: getTableHeader().setVisible(false);
105:
106: // table header not being visible, make sure we can still resize the columns
107: new HeaderlessColumnResizer(this );
108:
109: // default renderers and editors
110: setRendererFactory(new PropertyRendererRegistry());
111: setEditorFactory(new PropertyEditorRegistry());
112:
113: nameRenderer = new NameRenderer();
114:
115: // force the JTable to commit the edit when it losts focus
116: putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
117:
118: // only full rows can be selected
119: setColumnSelectionAllowed(false);
120: setRowSelectionAllowed(true);
121:
122: // replace the edit action to always trigger the editing of the value column
123: getActionMap().put("startEditing", new StartEditingAction());
124:
125: // ensure navigating with "TAB" moves to the next row
126: getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0),
127: "selectNextRowCell");
128: getInputMap().put(
129: KeyStroke.getKeyStroke(KeyEvent.VK_TAB,
130: KeyEvent.SHIFT_DOWN_MASK),
131: "selectPreviousRowCell");
132:
133: // allow category toggle with SPACE and mouse
134: getActionMap().put("toggle", new ToggleAction());
135: getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0),
136: "toggle");
137: addMouseListener(new ToggleMouseHandler());
138: }
139:
140: /**
141: * Initializes the default set of colors used by the PropertySheetTable.
142: *
143: * @see #categoryBackground
144: * @see #categoryForeground
145: * @see #selectedCategoryBackground
146: * @see #selectedCategoryForeground
147: * @see #propertyBackground
148: * @see #propertyForeground
149: * @see #selectedPropertyBackground
150: * @see #selectedPropertyForeground
151: */
152: private void initDefaultColors() {
153: this .categoryBackground = UIManager
154: .getColor(PANEL_BACKGROUND_COLOR_KEY);
155: this .categoryForeground = UIManager.getColor(
156: TABLE_FOREGROUND_COLOR_KEY).darker().darker().darker();
157:
158: this .selectedCategoryBackground = categoryBackground.darker();
159: this .selectedCategoryForeground = categoryForeground;
160:
161: this .propertyBackground = UIManager
162: .getColor(TABLE_BACKGROUND_COLOR_KEY);
163: this .propertyForeground = UIManager
164: .getColor(TABLE_FOREGROUND_COLOR_KEY);
165:
166: this .selectedPropertyBackground = UIManager
167: .getColor(TABLE_SELECTED_BACKGROUND_COLOR_KEY);
168: this .selectedPropertyForeground = UIManager
169: .getColor(TABLE_SELECTED_FOREGROUND_COLOR_KEY);
170:
171: setGridColor(categoryBackground);
172: }
173:
174: public Color getCategoryBackground() {
175: return categoryBackground;
176: }
177:
178: /**
179: * Sets the color used to paint a Category background.
180: *
181: * @param categoryBackground
182: */
183: public void setCategoryBackground(Color categoryBackground) {
184: this .categoryBackground = categoryBackground;
185: repaint();
186: }
187:
188: public Color getCategoryForeground() {
189: return categoryForeground;
190: }
191:
192: /**
193: * Sets the color used to paint a Category foreground.
194: *
195: * @param categoryForeground
196: */
197: public void setCategoryForeground(Color categoryForeground) {
198: this .categoryForeground = categoryForeground;
199: repaint();
200: }
201:
202: public Color getSelectedCategoryBackground() {
203: return selectedCategoryBackground;
204: }
205:
206: /**
207: * Sets the color used to paint a selected/focused Category background.
208: *
209: * @param selectedCategoryBackground
210: */
211: public void setSelectedCategoryBackground(
212: Color selectedCategoryBackground) {
213: this .selectedCategoryBackground = selectedCategoryBackground;
214: repaint();
215: }
216:
217: public Color getSelectedCategoryForeground() {
218: return selectedCategoryForeground;
219: }
220:
221: /**
222: * Sets the color used to paint a selected/focused Category foreground.
223: *
224: * @param selectedCategoryForeground
225: */
226: public void setSelectedCategoryForeground(
227: Color selectedCategoryForeground) {
228: this .selectedCategoryForeground = selectedCategoryForeground;
229: repaint();
230: }
231:
232: public Color getPropertyBackground() {
233: return propertyBackground;
234: }
235:
236: /**
237: * Sets the color used to paint a Property background.
238: *
239: * @param propertyBackground
240: */
241: public void setPropertyBackground(Color propertyBackground) {
242: this .propertyBackground = propertyBackground;
243: repaint();
244: }
245:
246: public Color getPropertyForeground() {
247: return propertyForeground;
248: }
249:
250: /**
251: * Sets the color used to paint a Property foreground.
252: *
253: * @param propertyForeground
254: */
255: public void setPropertyForeground(Color propertyForeground) {
256: this .propertyForeground = propertyForeground;
257: repaint();
258: }
259:
260: public Color getSelectedPropertyBackground() {
261: return selectedPropertyBackground;
262: }
263:
264: /**
265: * Sets the color used to paint a selected/focused Property background.
266: *
267: * @param selectedPropertyBackground
268: */
269: public void setSelectedPropertyBackground(
270: Color selectedPropertyBackground) {
271: this .selectedPropertyBackground = selectedPropertyBackground;
272: repaint();
273: }
274:
275: public Color getSelectedPropertyForeground() {
276: return selectedPropertyForeground;
277: }
278:
279: /**
280: * Sets the color used to paint a selected/focused Property foreground.
281: *
282: * @param selectedPropertyForeground
283: */
284: public void setSelectedPropertyForeground(
285: Color selectedPropertyForeground) {
286: this .selectedPropertyForeground = selectedPropertyForeground;
287: repaint();
288: }
289:
290: public void setEditorFactory(PropertyEditorFactory factory) {
291: editorFactory = factory;
292: }
293:
294: public final PropertyEditorFactory getEditorFactory() {
295: return editorFactory;
296: }
297:
298: /**
299: * @param registry
300: * @deprecated use {@link #setEditorFactory(PropertyEditorFactory)}
301: */
302: public void setEditorRegistry(PropertyEditorRegistry registry) {
303: setEditorFactory(registry);
304: }
305:
306: /**
307: * @deprecated use {@link #getEditorFactory()}
308: * @throws ClassCastException if the current editor factory is not a
309: * PropertyEditorRegistry
310: */
311: public PropertyEditorRegistry getEditorRegistry() {
312: return (PropertyEditorRegistry) editorFactory;
313: }
314:
315: public void setRendererFactory(PropertyRendererFactory factory) {
316: rendererFactory = factory;
317: }
318:
319: public PropertyRendererFactory getRendererFactory() {
320: return rendererFactory;
321: }
322:
323: /**
324: * @deprecated use {@link #setRendererFactory(PropertyRendererFactory)}
325: * @param registry
326: */
327: public void setRendererRegistry(PropertyRendererRegistry registry) {
328: setRendererFactory(registry);
329: }
330:
331: /**
332: * @deprecated use {@link #getRendererFactory()}
333: * @throws ClassCastException if the current renderer factory is not a
334: * PropertyRendererRegistry
335: */
336: public PropertyRendererRegistry getRendererRegistry() {
337: return (PropertyRendererRegistry) getRendererFactory();
338: }
339:
340: /* (non-Javadoc)
341: * @see javax.swing.JTable#isCellEditable(int, int)
342: */
343: public boolean isCellEditable(int row, int column) {
344: // names are not editable
345: if (column == 0) {
346: return false;
347: }
348:
349: PropertySheetTableModel.Item item = getSheetModel()
350: .getPropertySheetElement(row);
351: return item.isProperty() && item.getProperty().isEditable();
352: }
353:
354: /**
355: * Gets the CellEditor for the given row and column. It uses the
356: * editor registry to find a suitable editor for the property.
357: * @see javax.swing.JTable#getCellEditor(int, int)
358: */
359: public TableCellEditor getCellEditor(int row, int column) {
360: if (column == 0) {
361: return null;
362: }
363:
364: Item item = getSheetModel().getPropertySheetElement(row);
365: if (!item.isProperty())
366: return null;
367:
368: TableCellEditor result = null;
369: Property propery = item.getProperty();
370: PropertyEditor editor = getEditorFactory()
371: .createPropertyEditor(propery);
372: if (editor != null)
373: result = new CellEditorAdapter(editor);
374:
375: return result;
376: }
377:
378: /* (non-Javadoc)
379: * @see javax.swing.JTable#getCellRenderer(int, int)
380: */
381: public TableCellRenderer getCellRenderer(int row, int column) {
382: PropertySheetTableModel.Item item = getSheetModel()
383: .getPropertySheetElement(row);
384:
385: switch (column) {
386: case PropertySheetTableModel.NAME_COLUMN:
387: // name column gets a custom renderer
388: return nameRenderer;
389:
390: case PropertySheetTableModel.VALUE_COLUMN: {
391: if (!item.isProperty())
392: return nameRenderer;
393:
394: // property value column gets the renderer from the factory
395: Property property = item.getProperty();
396: TableCellRenderer renderer = getRendererFactory()
397: .createTableCellRenderer(property);
398: if (renderer == null)
399: renderer = getCellRenderer(property.getType());
400: return renderer;
401: }
402: default:
403: // when will this happen, given the above?
404: return super .getCellRenderer(row, column);
405: }
406: }
407:
408: /**
409: * Helper method to lookup a cell renderer based on type.
410: * @param type the type for which a renderer should be found
411: * @return a renderer for the given object type
412: */
413: private TableCellRenderer getCellRenderer(Class type) {
414: // try to create one from the factory
415: TableCellRenderer renderer = getRendererFactory()
416: .createTableCellRenderer(type);
417:
418: // if that fails, recursively try again with the superclass
419: if (renderer == null && type != null)
420: renderer = getCellRenderer(type.getSuperclass());
421:
422: // if that fails, just use the default Object renderer
423: if (renderer == null)
424: renderer = super .getDefaultRenderer(Object.class);
425:
426: return renderer;
427: }
428:
429: public final PropertySheetTableModel getSheetModel() {
430: return (PropertySheetTableModel) getModel();
431: }
432:
433: /**
434: * Overriden
435: * <li>to prevent the cell focus rect to be painted
436: * <li>to disable ({@link Component#setEnabled(boolean)} the renderer if the
437: * Property is not editable
438: */
439: public Component prepareRenderer(TableCellRenderer renderer,
440: int row, int column) {
441: Object value = getValueAt(row, column);
442: boolean isSelected = isCellSelected(row, column);
443: Component component = renderer.getTableCellRendererComponent(
444: this , value, isSelected, false, row, column);
445:
446: PropertySheetTableModel.Item item = getSheetModel()
447: .getPropertySheetElement(row);
448: if (item.isProperty()) {
449: component.setEnabled(item.getProperty().isEditable());
450: }
451: return component;
452: }
453:
454: /**
455: * Overriden to register a listener on the model. This listener ensures
456: * editing is cancelled when editing row is being changed.
457: *
458: * @see javax.swing.JTable#setModel(javax.swing.table.TableModel)
459: * @throws IllegalArgumentException
460: * if dataModel is not a {@link PropertySheetTableModel}
461: */
462: public void setModel(TableModel newModel) {
463: if (!(newModel instanceof PropertySheetTableModel)) {
464: throw new IllegalArgumentException(
465: "dataModel must be of type "
466: + PropertySheetTableModel.class.getName());
467: }
468:
469: if (cancelEditing == null) {
470: cancelEditing = new CancelEditing();
471: }
472:
473: TableModel oldModel = getModel();
474: if (oldModel != null) {
475: oldModel.removeTableModelListener(cancelEditing);
476: }
477: super .setModel(newModel);
478: newModel.addTableModelListener(cancelEditing);
479:
480: // ensure the "value" column can not be resized
481: getColumnModel().getColumn(1).setResizable(false);
482: }
483:
484: /**
485: * @see #setWantsExtraIndent(boolean)
486: */
487: public boolean getWantsExtraIndent() {
488: return wantsExtraIndent;
489: }
490:
491: /**
492: * By default, properties with children are painted with the same indent level
493: * as other properties and categories. When nested properties exist within the
494: * set of properties, the end-user might be confused by the category and
495: * property handles. Sets this property to true to add an extra indent level
496: * to properties.
497: *
498: * @param wantsExtraIndent
499: */
500: public void setWantsExtraIndent(boolean wantsExtraIndent) {
501: this .wantsExtraIndent = wantsExtraIndent;
502: repaint();
503: }
504:
505: /**
506: * Ensures the table uses the full height of its parent
507: * {@link javax.swing.JViewport}.
508: */
509: public boolean getScrollableTracksViewportHeight() {
510: return getPreferredSize().height < getParent().getHeight();
511: }
512:
513: /**
514: * Commits on-going cell editing
515: */
516: public void commitEditing() {
517: TableCellEditor editor = getCellEditor();
518: if (editor != null) {
519: editor.stopCellEditing();
520: }
521: }
522:
523: /**
524: * Cancels on-going cell editing
525: */
526: public void cancelEditing() {
527: TableCellEditor editor = getCellEditor();
528: if (editor != null) {
529: editor.cancelCellEditing();
530: }
531: }
532:
533: /**
534: * Cancels the cell editing if any update happens while modifying a value.
535: */
536: private class CancelEditing implements TableModelListener {
537: public void tableChanged(TableModelEvent e) {
538: // in case the table changes for the following reasons:
539: // * the editing row has changed
540: // * the editing row was removed
541: // * all rows were changed
542: // * rows were added
543: //
544: // it is better to cancel the editing of the row as our editor
545: // may no longer be the right one. It happens when you play with
546: // the sorting while having the focus in one editor.
547: if (e.getType() == TableModelEvent.UPDATE) {
548: int first = e.getFirstRow();
549: int last = e.getLastRow();
550: int editingRow = PropertySheetTable.this
551: .getEditingRow();
552:
553: TableCellEditor editor = PropertySheetTable.this
554: .getCellEditor();
555: if (editor != null && first <= editingRow
556: && editingRow <= last) {
557: editor.cancelCellEditing();
558: }
559: }
560: }
561: }
562:
563: /**
564: * Starts value cell editing even if value cell does not have the focus but
565: * only if row is selected.
566: */
567: private static class StartEditingAction extends AbstractAction {
568: public void actionPerformed(ActionEvent e) {
569: JTable table = (JTable) e.getSource();
570: if (!table.hasFocus()) {
571: CellEditor cellEditor = table.getCellEditor();
572: if (cellEditor != null && !cellEditor.stopCellEditing()) {
573: return;
574: }
575: table.requestFocus();
576: return;
577: }
578: ListSelectionModel rsm = table.getSelectionModel();
579: int anchorRow = rsm.getAnchorSelectionIndex();
580: table.editCellAt(anchorRow,
581: PropertySheetTableModel.VALUE_COLUMN);
582: Component editorComp = table.getEditorComponent();
583: if (editorComp != null) {
584: editorComp.requestFocus();
585: }
586: }
587: }
588:
589: /**
590: * Toggles the state of a row between expanded/collapsed. Works only for rows
591: * with "toggle" knob.
592: */
593: private class ToggleAction extends AbstractAction {
594: public void actionPerformed(ActionEvent e) {
595: int row = PropertySheetTable.this .getSelectedRow();
596: Item item = PropertySheetTable.this .getSheetModel()
597: .getPropertySheetElement(row);
598: item.toggle();
599: PropertySheetTable.this .addRowSelectionInterval(row, row);
600: }
601:
602: public boolean isEnabled() {
603: int row = PropertySheetTable.this .getSelectedRow();
604: if (row != -1) {
605: Item item = PropertySheetTable.this .getSheetModel()
606: .getPropertySheetElement(row);
607: return item.hasToggle();
608: } else {
609: return false;
610: }
611: }
612: }
613:
614: /**
615: * @see ToggleAction
616: */
617: private static class ToggleMouseHandler extends MouseAdapter {
618: public void mouseReleased(MouseEvent event) {
619: PropertySheetTable table = (PropertySheetTable) event
620: .getComponent();
621: int row = table.rowAtPoint(event.getPoint());
622: int column = table.columnAtPoint(event.getPoint());
623: if (row != -1 && column == 0) {
624: // if we clicked on an Item, see if we clicked on its hotspot
625: Item item = table.getSheetModel()
626: .getPropertySheetElement(row);
627: int x = event.getX() - getIndent(table, item);
628: if (x > 0 && x < HOTSPOT_SIZE)
629: item.toggle();
630: }
631: }
632: }
633:
634: /**
635: * Calculates the required left indent for a given item, given its type and
636: * its hierarchy level.
637: */
638: static int getIndent(PropertySheetTable table, Item item) {
639: int indent = 0;
640:
641: if (item.isProperty()) {
642: // it is a property, it has no parent or a category, and no child
643: if ((item.getParent() == null || !item.getParent()
644: .isProperty())
645: && !item.hasToggle()) {
646: indent = table.getWantsExtraIndent() ? HOTSPOT_SIZE : 0;
647: } else {
648: // it is a property with children
649: if (item.hasToggle()) {
650: indent = item.getDepth() * HOTSPOT_SIZE;
651: } else {
652: indent = (item.getDepth() + 1) * HOTSPOT_SIZE;
653: }
654: }
655:
656: if (table.getSheetModel().getMode() == PropertySheet.VIEW_AS_CATEGORIES
657: && table.getWantsExtraIndent()) {
658: indent += HOTSPOT_SIZE;
659: }
660:
661: } else {
662: // category has no indent
663: indent = 0;
664: }
665: return indent;
666: }
667:
668: /**
669: * Paints the border around the name cell. It handles the indent from the left
670: * side and the painting of the toggle knob.
671: */
672: private static class CellBorder implements Border {
673:
674: private int indentWidth; // space before hotspot
675: private boolean showToggle;
676: private boolean toggleState;
677: private Icon expandedIcon;
678: private Icon collapsedIcon;
679: private Insets insets = new Insets(1, 0, 1, 1);
680: private boolean isProperty;
681:
682: public CellBorder() {
683: expandedIcon = (Icon) UIManager.get(TREE_EXPANDED_ICON_KEY);
684: collapsedIcon = (Icon) UIManager
685: .get(TREE_COLLAPSED_ICON_KEY);
686: if (expandedIcon == null) {
687: expandedIcon = new ExpandedIcon();
688: }
689: if (collapsedIcon == null) {
690: collapsedIcon = new CollapsedIcon();
691: }
692: }
693:
694: public void configure(PropertySheetTable table, Item item) {
695: isProperty = item.isProperty();
696: toggleState = item.isVisible();
697: showToggle = item.hasToggle();
698:
699: indentWidth = getIndent(table, item);
700: insets.left = indentWidth + (showToggle ? HOTSPOT_SIZE : 0)
701: + 2;
702: ;
703: }
704:
705: public Insets getBorderInsets(Component c) {
706: return insets;
707: }
708:
709: public void paintBorder(Component c, Graphics g, int x, int y,
710: int width, int height) {
711: if (!isProperty) {
712: Color oldColor = g.getColor();
713: g.setColor(c.getBackground());
714: g.fillRect(x, y, x + HOTSPOT_SIZE - 2, y + height);
715: g.setColor(oldColor);
716: }
717:
718: if (showToggle) {
719: Icon drawIcon = (toggleState ? expandedIcon
720: : collapsedIcon);
721: drawIcon.paintIcon(c, g, x + indentWidth
722: + (HOTSPOT_SIZE - 2 - drawIcon.getIconWidth())
723: / 2, y + (height - drawIcon.getIconHeight())
724: / 2);
725: }
726: }
727:
728: public boolean isBorderOpaque() {
729: return true;
730: }
731:
732: }
733:
734: private static class ExpandedIcon implements Icon {
735: public void paintIcon(Component c, Graphics g, int x, int y) {
736: Color backgroundColor = c.getBackground();
737:
738: if (backgroundColor != null)
739: g.setColor(backgroundColor);
740: else
741: g.setColor(Color.white);
742: g.fillRect(x, y, 8, 8);
743: g.setColor(Color.gray);
744: g.drawRect(x, y, 8, 8);
745: g.setColor(Color.black);
746: g.drawLine(x + 2, y + 4, x + (6), y + 4);
747: }
748:
749: public int getIconWidth() {
750: return 9;
751: }
752:
753: public int getIconHeight() {
754: return 9;
755: }
756: }
757:
758: private static class CollapsedIcon extends ExpandedIcon {
759: public void paintIcon(Component c, Graphics g, int x, int y) {
760: super .paintIcon(c, g, x, y);
761: g.drawLine(x + 4, y + 2, x + 4, y + 6);
762: }
763: }
764:
765: /**
766: * A {@link TableCellRenderer} for property names.
767: */
768: private class NameRenderer extends DefaultTableCellRenderer {
769:
770: private CellBorder border;
771:
772: public NameRenderer() {
773: border = new CellBorder();
774: }
775:
776: private Color getForeground(boolean isProperty,
777: boolean isSelected) {
778: return (isProperty ? (isSelected ? selectedPropertyForeground
779: : propertyForeground)
780: : (isSelected ? selectedCategoryForeground
781: : categoryForeground));
782: }
783:
784: private Color getBackground(boolean isProperty,
785: boolean isSelected) {
786: return (isProperty ? (isSelected ? selectedPropertyBackground
787: : propertyBackground)
788: : (isSelected ? selectedCategoryBackground
789: : categoryBackground));
790: }
791:
792: public Component getTableCellRendererComponent(JTable table,
793: Object value, boolean isSelected, boolean hasFocus,
794: int row, int column) {
795: super .getTableCellRendererComponent(table, value,
796: isSelected, false, row, column);
797: PropertySheetTableModel.Item item = (Item) value;
798:
799: // shortcut if we are painting the category column
800: if (column == PropertySheetTableModel.VALUE_COLUMN
801: && !item.isProperty()) {
802: setBackground(getBackground(item.isProperty(),
803: isSelected));
804: setText("");
805: return this ;
806: }
807:
808: setBorder(border);
809:
810: // configure the border
811: border.configure((PropertySheetTable) table, item);
812:
813: setBackground(getBackground(item.isProperty(), isSelected));
814: setForeground(getForeground(item.isProperty(), isSelected));
815:
816: setEnabled(isSelected || !item.isProperty() ? true : item
817: .getProperty().isEditable());
818: setText(item.getName());
819:
820: return this;
821: }
822: }
823:
824: }
|