0001: /*
0002: * Copyright (c) 2005-2008 Substance Kirill Grouchnikov. All Rights Reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without
0005: * modification, are permitted provided that the following conditions are met:
0006: *
0007: * o Redistributions of source code must retain the above copyright notice,
0008: * this list of conditions and the following disclaimer.
0009: *
0010: * o Redistributions in binary form must reproduce the above copyright notice,
0011: * this list of conditions and the following disclaimer in the documentation
0012: * and/or other materials provided with the distribution.
0013: *
0014: * o Neither the name of Substance Kirill Grouchnikov nor the names of
0015: * its contributors may be used to endorse or promote products derived
0016: * from this software without specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
0020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
0021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
0022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
0025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
0026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
0027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
0028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0029: */
0030: package org.jvnet.substance;
0031:
0032: import java.awt.*;
0033: import java.awt.event.*;
0034: import java.beans.PropertyChangeEvent;
0035: import java.beans.PropertyChangeListener;
0036: import java.util.*;
0037:
0038: import javax.swing.*;
0039: import javax.swing.event.*;
0040: import javax.swing.plaf.ComponentUI;
0041: import javax.swing.plaf.UIResource;
0042: import javax.swing.plaf.basic.BasicTableUI;
0043: import javax.swing.table.*;
0044:
0045: import org.jvnet.lafwidget.LafWidgetUtilities;
0046: import org.jvnet.lafwidget.animation.*;
0047: import org.jvnet.lafwidget.layout.TransitionLayout;
0048: import org.jvnet.substance.painter.highlight.SubstanceHighlightUtils;
0049: import org.jvnet.substance.painter.text.SubstanceTextPainter;
0050: import org.jvnet.substance.theme.SubstanceTheme;
0051: import org.jvnet.substance.utils.*;
0052:
0053: /**
0054: * UI for tables in <b>Substance</b> look and feel. Unfortunately, the entire
0055: * painting stack has been copied from {@link BasicTableUI} since the methods
0056: * are private. The animation effects are implemented in the
0057: * {@link #paintCell(Graphics, Rectangle, int, int)}.
0058: *
0059: * @author Kirill Grouchnikov
0060: */
0061: public class SubstanceTableUI extends BasicTableUI {
0062: /**
0063: * Holds the list of currently selected row-column indexes.
0064: */
0065: protected Map<TableCellId, Object> selectedIndices;
0066:
0067: /**
0068: * Holds the currently rolled-over row-column index, or <code>null</code>
0069: * if none such.
0070: */
0071: protected Comparable<?> rolledOverId;
0072:
0073: /**
0074: * Row index of the focused cell.
0075: */
0076: protected int focusedRow;
0077:
0078: /**
0079: * Column index of the focused cell.
0080: */
0081: protected int focusedColumn;
0082:
0083: /**
0084: * Holds the currently rolled-over column index, or <code>-1</code> if
0085: * none such. This is used for the table header animations.
0086: */
0087: protected int rolledOverColumn;
0088:
0089: /**
0090: * Map of default renderers.
0091: */
0092: protected Map<Class<?>, TableCellRenderer> defaultRenderers;
0093:
0094: /**
0095: * Listener that listens to changes on table properties.
0096: */
0097: protected PropertyChangeListener substancePropertyChangeListener;
0098:
0099: /**
0100: * Listener for fade animations on list selections.
0101: */
0102: protected TableStateListener substanceFadeSelectionListener;
0103:
0104: /**
0105: * Listener for fade animations on table rollovers.
0106: */
0107: protected RolloverFadeListener substanceFadeRolloverListener;
0108:
0109: // /**
0110: // * Delegate for painting the background of list cells.
0111: // */
0112: // private static SubstanceFillBackgroundDelegate backgroundDelegate = new
0113: // SubstanceFillBackgroundDelegate();
0114:
0115: /**
0116: * Map of previous fade states (for state-aware theme transitions).
0117: */
0118: private Map<TableCellId, ComponentState> prevStateMap;
0119:
0120: /**
0121: * Map of next fade states (for state-aware theme transitions).
0122: */
0123: private Map<TableCellId, ComponentState> nextStateMap;
0124:
0125: /*
0126: * (non-Javadoc)
0127: *
0128: * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
0129: */
0130: public static ComponentUI createUI(JComponent c) {
0131: return new SubstanceTableUI();
0132: }
0133:
0134: /**
0135: * Creates a UI delegate for table.
0136: */
0137: public SubstanceTableUI() {
0138: super ();
0139: this .selectedIndices = new HashMap<TableCellId, Object>();
0140: this .prevStateMap = new HashMap<TableCellId, ComponentState>();
0141: this .nextStateMap = new HashMap<TableCellId, ComponentState>();
0142: this .rolledOverColumn = -1;
0143: this .focusedRow = -1;
0144: this .focusedColumn = -1;
0145: }
0146:
0147: /*
0148: * (non-Javadoc)
0149: *
0150: * @see javax.swing.plaf.basic.BasicTableUI#installDefaults()
0151: */
0152: @Override
0153: protected void installDefaults() {
0154: super .installDefaults();
0155: if (SubstanceCoreUtilities.toBleedWatermark(this .table))
0156: this .table.setOpaque(false);
0157:
0158: // fix for defect 117 - need to restore default table cell
0159: // renderers when Substance is unset
0160: this .defaultRenderers = new HashMap<Class<?>, TableCellRenderer>();
0161:
0162: Class<?>[] defClasses = new Class[] { Object.class, Icon.class,
0163: ImageIcon.class, Number.class, Float.class,
0164: Double.class, Date.class, Boolean.class };
0165: for (Class<?> clazz : defClasses) {
0166: this .defaultRenderers.put(clazz, this .table
0167: .getDefaultRenderer(clazz));
0168: }
0169:
0170: // Override default renderers - note fix for issue 194
0171: // that doesn't override user-specific renderers (those that don't come
0172: // from JTable class).
0173: this .installRendererIfNecessary(Object.class,
0174: new SubstanceDefaultTableCellRenderer());
0175: // this.table.setDefaultRenderer(Object.class,
0176: // new SubstanceDefaultTableCellRenderer());
0177: this .installRendererIfNecessary(Icon.class,
0178: new SubstanceDefaultTableCellRenderer.IconRenderer());
0179: // this.table.setDefaultRenderer(Icon.class,
0180: // new SubstanceDefaultTableCellRenderer.IconRenderer());
0181: this .installRendererIfNecessary(ImageIcon.class,
0182: new SubstanceDefaultTableCellRenderer.IconRenderer());
0183: // this.table.setDefaultRenderer(ImageIcon.class,
0184: // new SubstanceDefaultTableCellRenderer.IconRenderer());
0185: this .installRendererIfNecessary(Number.class,
0186: new SubstanceDefaultTableCellRenderer.NumberRenderer());
0187: // this.table.setDefaultRenderer(Number.class,
0188: // new SubstanceDefaultTableCellRenderer.NumberRenderer());
0189: this .installRendererIfNecessary(Float.class,
0190: new SubstanceDefaultTableCellRenderer.DoubleRenderer());
0191: // this.table.setDefaultRenderer(Float.class,
0192: // new SubstanceDefaultTableCellRenderer.DoubleRenderer());
0193: this .installRendererIfNecessary(Double.class,
0194: new SubstanceDefaultTableCellRenderer.DoubleRenderer());
0195: // this.table.setDefaultRenderer(Double.class,
0196: // new SubstanceDefaultTableCellRenderer.DoubleRenderer());
0197: this .installRendererIfNecessary(Date.class,
0198: new SubstanceDefaultTableCellRenderer.DateRenderer());
0199: // this.table.setDefaultRenderer(Date.class,
0200: // new SubstanceDefaultTableCellRenderer.DateRenderer());
0201: // fix for bug 56 - making default renderer for Boolean a check box.
0202: this
0203: .installRendererIfNecessary(
0204: Boolean.class,
0205: new SubstanceDefaultTableCellRenderer.BooleanRenderer());
0206: // this.table.setDefaultRenderer(Boolean.class,
0207: // new SubstanceDefaultTableCellRenderer.BooleanRenderer());
0208:
0209: // Map<TableCellId, Object> selected = new HashMap<TableCellId,
0210: // Object>();
0211: int rows = this .table.getRowCount();
0212: int cols = this .table.getColumnCount();
0213: for (int i = 0; i < rows; i++) {
0214: for (int j = 0; j < cols; j++) {
0215: if (this .table.isCellSelected(i, j)) {
0216: TableCellId cellId = new TableCellId(i, j);
0217: this .selectedIndices.put(cellId, this .table
0218: .getValueAt(i, j));
0219: this .prevStateMap.put(cellId,
0220: ComponentState.SELECTED);
0221: }
0222: }
0223: }
0224:
0225: // This is a little tricky, and hopefully will not
0226: // interfere with existing applications. The row height in tables
0227: // is computed differently from trees and lists. While lists
0228: // trees respect the current renderers and their insets, the
0229: // JTable uses hard-code value of 16 pixels as the default
0230: // row height. This, obviously, doesn't sit well with the support
0231: // for custom fonts and high-DPI monitors.
0232: //
0233: // The current solution first checks whether all the renderers
0234: // come from Substance. If not, it does nothing. If they do:
0235: // 1. If the table is empty, create a dummy label, compute its
0236: // preferred height and apply insets.
0237: // 2. Otherwise, compute preferred heights for each cell and
0238: // take the biggest one.
0239: boolean areAllRenderersFromSubstance = true;
0240: TableColumnModel columnModel = table.getColumnModel();
0241: for (int i = 0; i < columnModel.getColumnCount(); i++) {
0242: TableColumn column = columnModel.getColumn(i);
0243: TableCellRenderer renderer = column.getCellRenderer();
0244: if (renderer == null) {
0245: renderer = table.getDefaultRenderer(table
0246: .getColumnClass(i));
0247: }
0248: if ((renderer instanceof SubstanceDefaultTableCellRenderer)
0249: || (renderer instanceof SubstanceDefaultTableCellRenderer.BooleanRenderer))
0250: continue;
0251: areAllRenderersFromSubstance = false;
0252: break;
0253: }
0254: if (areAllRenderersFromSubstance) {
0255: Insets rendererInsets = SubstanceSizeUtils
0256: .getTableCellRendererInsets(SubstanceSizeUtils
0257: .getComponentFontSize(table));
0258: JLabel dummy = new JLabel("dummy");
0259: dummy.setFont(table.getFont());
0260: int rowHeight = dummy.getPreferredSize().height
0261: + rendererInsets.bottom + rendererInsets.top;
0262: for (int i = 0; i < table.getRowCount(); i++) {
0263: for (int j = 0; j < table.getColumnCount(); j++) {
0264: TableCellRenderer renderer = table.getCellRenderer(
0265: i, j);
0266: try {
0267: rowHeight = Math.max(rowHeight,
0268: renderer.getTableCellRendererComponent(
0269: table, table.getValueAt(i, j),
0270: false, false, i, j)
0271: .getPreferredSize().height);
0272: } catch (Throwable t) {
0273: // Ignore - this happens when the renderer has a null
0274: // font, such as in NetBeans palette table (in the
0275: // getPreferredSize() call). Not much we can do since
0276: // setting the font on the table or renderer will
0277: // interfere with the application logic.
0278: }
0279: }
0280: }
0281: table.setRowHeight(rowHeight);
0282: }
0283: }
0284:
0285: /**
0286: * Installs Substance-specific renderers for column classes that don't have
0287: * application-specific renderers installed by the user code.
0288: *
0289: * @param clazz
0290: * Column class.
0291: * @param renderer
0292: * Default renderer for the specified column class.
0293: */
0294: protected void installRendererIfNecessary(Class<?> clazz,
0295: TableCellRenderer renderer) {
0296: TableCellRenderer currRenderer = this .table
0297: .getDefaultRenderer(clazz);
0298: if (currRenderer != null) {
0299: boolean isCore = (currRenderer instanceof DefaultTableCellRenderer.UIResource)
0300: || (currRenderer.getClass().getName()
0301: .startsWith("javax.swing.JTable"));
0302: if (!isCore)
0303: return;
0304: }
0305: // System.out.println(clazz.getSimpleName() + " : overriding "
0306: // + currRenderer.getClass().getName() + "["
0307: // + currRenderer.hashCode() + "] with "
0308: // + renderer.getClass().getName() + "[" + renderer.hashCode()
0309: // + "]");
0310: this .table.setDefaultRenderer(clazz, renderer);
0311: }
0312:
0313: /*
0314: * (non-Javadoc)
0315: *
0316: * @see javax.swing.plaf.basic.BasicTableUI#uninstallDefaults()
0317: */
0318: @Override
0319: protected void uninstallDefaults() {
0320: // fix for defect 117 - need to restore default table cell
0321: // renderers when Substance is unset
0322: for (Map.Entry<Class<?>, TableCellRenderer> entry : this .defaultRenderers
0323: .entrySet()) {
0324: // this.table.setDefaultRenderer(entry.getKey(), entry.getValue());
0325:
0326: // fix for issue 194 - restore only those renderers that were
0327: // overriden by Substance.
0328: this .uninstallRendererIfNecessary(entry.getKey(), entry
0329: .getValue());
0330: }
0331:
0332: this .selectedIndices.clear();
0333: // this.table.putClientProperty(SubstanceTableUI.SELECTED_INDICES,
0334: // null);
0335:
0336: super .uninstallDefaults();
0337: }
0338:
0339: /**
0340: * Uninstalls default Substance renderers that were installed in
0341: * {@link #installRendererIfNecessary(Class, TableCellRenderer)}.
0342: *
0343: * @param clazz
0344: * Column class.
0345: * @param renderer
0346: * Renderer to restore.
0347: */
0348: protected void uninstallRendererIfNecessary(Class<?> clazz,
0349: TableCellRenderer renderer) {
0350: TableCellRenderer currRenderer = this .table
0351: .getDefaultRenderer(clazz);
0352: if (currRenderer != null) {
0353: boolean isSubstanceRenderer = (currRenderer instanceof SubstanceDefaultTableCellRenderer)
0354: || (currRenderer instanceof SubstanceDefaultTableCellRenderer.BooleanRenderer);
0355: if (!isSubstanceRenderer)
0356: return;
0357: }
0358: if (renderer instanceof Component)
0359: SwingUtilities.updateComponentTreeUI((Component) renderer);
0360: this .table.setDefaultRenderer(clazz, renderer);
0361: }
0362:
0363: /*
0364: * (non-Javadoc)
0365: *
0366: * @see javax.swing.plaf.basic.BasicTableUI#installListeners()
0367: */
0368: @Override
0369: protected void installListeners() {
0370: super .installListeners();
0371: this .substancePropertyChangeListener = new PropertyChangeListener() {
0372: public void propertyChange(PropertyChangeEvent evt) {
0373: if (SubstanceLookAndFeel.WATERMARK_TO_BLEED.equals(evt
0374: .getPropertyName())) {
0375: SubstanceTableUI.this .table
0376: .setOpaque(!SubstanceCoreUtilities
0377: .toBleedWatermark(SubstanceTableUI.this .table));
0378: }
0379:
0380: if ("columnSelectionAllowed".equals(evt
0381: .getPropertyName())
0382: || "rowSelectionAllowed".equals(evt
0383: .getPropertyName())) {
0384: SubstanceTableUI.this .syncSelection();
0385: }
0386:
0387: if ("model".equals(evt.getPropertyName())) {
0388: TableModel old = (TableModel) evt.getOldValue();
0389: if (old != null) {
0390: old
0391: .removeTableModelListener(substanceFadeSelectionListener);
0392: }
0393: // fix for defect 291 - track changes to the table.
0394: table.getModel().addTableModelListener(
0395: substanceFadeSelectionListener);
0396: selectedIndices.clear();
0397: prevStateMap.clear();
0398: nextStateMap.clear();
0399: SubstanceTableUI.this .syncSelection();
0400: }
0401:
0402: if ("columnModel".equals(evt.getPropertyName())) {
0403: TableColumnModel old = (TableColumnModel) evt
0404: .getOldValue();
0405: if (old != null) {
0406: old.getSelectionModel()
0407: .removeListSelectionListener(
0408: substanceFadeSelectionListener);
0409: }
0410: table.getColumnModel().getSelectionModel()
0411: .addListSelectionListener(
0412: substanceFadeSelectionListener);
0413: selectedIndices.clear();
0414: prevStateMap.clear();
0415: nextStateMap.clear();
0416: SubstanceTableUI.this .syncSelection();
0417:
0418: // fix for issue 309 - syncing animations on tables
0419: // and table headers.
0420: SubstanceTableHeaderUI headerUI = (SubstanceTableHeaderUI) table
0421: .getTableHeader().getUI();
0422: headerUI.processColumnModelChangeEvent(
0423: (TableColumnModel) evt.getOldValue(),
0424: (TableColumnModel) evt.getNewValue());
0425: }
0426:
0427: // fix for defect 243 - not tracking changes to selection
0428: // model results in incorrect selection painting on JXTreeTable
0429: // component from SwingX.
0430: if ("selectionModel".equals(evt.getPropertyName())) {
0431: ListSelectionModel old = (ListSelectionModel) evt
0432: .getOldValue();
0433: if (old != null) {
0434: old
0435: .removeListSelectionListener(substanceFadeSelectionListener);
0436: }
0437: table.getSelectionModel().addListSelectionListener(
0438: substanceFadeSelectionListener);
0439: selectedIndices.clear();
0440: prevStateMap.clear();
0441: nextStateMap.clear();
0442: SubstanceTableUI.this .syncSelection();
0443: }
0444:
0445: if ("font".equals(evt.getPropertyName())) {
0446: SwingUtilities.invokeLater(new Runnable() {
0447: public void run() {
0448: table.updateUI();
0449: }
0450: });
0451: }
0452:
0453: if ("background".equals(evt.getPropertyName())) {
0454: // propagate application-specific background color to the
0455: // scroll bars.
0456: Color newBackgr = (Color) evt.getNewValue();
0457: if (!(newBackgr instanceof UIResource)) {
0458: JTableHeader header = table.getTableHeader();
0459: if (header != null) {
0460: if (header.getBackground() instanceof UIResource) {
0461: header.setBackground(newBackgr);
0462: }
0463: }
0464: }
0465: }
0466:
0467: }
0468: };
0469: this .table
0470: .addPropertyChangeListener(this .substancePropertyChangeListener);
0471:
0472: // Add listener for the selection animation
0473: this .substanceFadeSelectionListener = new TableStateListener();
0474: this .table.getSelectionModel().addListSelectionListener(
0475: this .substanceFadeSelectionListener);
0476: TableColumnModel columnModel = this .table.getColumnModel();
0477: columnModel.getSelectionModel().addListSelectionListener(
0478: this .substanceFadeSelectionListener);
0479: this .table.getModel().addTableModelListener(
0480: this .substanceFadeSelectionListener);
0481:
0482: // Add listener for the fade animation
0483: this .substanceFadeRolloverListener = new RolloverFadeListener();
0484: this .table
0485: .addMouseMotionListener(this .substanceFadeRolloverListener);
0486: this .table.addMouseListener(this .substanceFadeRolloverListener);
0487: }
0488:
0489: /*
0490: * (non-Javadoc)
0491: *
0492: * @see javax.swing.plaf.basic.BasicTableUI#uninstallListeners()
0493: */
0494: @Override
0495: protected void uninstallListeners() {
0496: this .table
0497: .removePropertyChangeListener(this .substancePropertyChangeListener);
0498: this .substancePropertyChangeListener = null;
0499:
0500: this .table.getSelectionModel().removeListSelectionListener(
0501: this .substanceFadeSelectionListener);
0502: this .table.getColumnModel().getSelectionModel()
0503: .removeListSelectionListener(
0504: this .substanceFadeSelectionListener);
0505: this .table.getModel().removeTableModelListener(
0506: this .substanceFadeSelectionListener);
0507: this .substanceFadeSelectionListener = null;
0508:
0509: // Remove listener for the fade animation
0510: this .table
0511: .removeMouseMotionListener(this .substanceFadeRolloverListener);
0512: this .table
0513: .removeMouseListener(this .substanceFadeRolloverListener);
0514: this .substanceFadeRolloverListener = null;
0515:
0516: super .uninstallListeners();
0517: }
0518:
0519: /**
0520: * Paint a representation of the <code>table</code> instance that was set
0521: * in installUI().
0522: */
0523: @Override
0524: public void paint(Graphics g, JComponent c) {
0525: Rectangle clip = g.getClipBounds();
0526:
0527: Rectangle bounds = this .table.getBounds();
0528: // account for the fact that the graphics has already been translated
0529: // into the table's bounds
0530: bounds.x = bounds.y = 0;
0531:
0532: if (this .table.getRowCount() <= 0
0533: || this .table.getColumnCount() <= 0 ||
0534: // this check prevents us from painting the entire table
0535: // when the clip doesn't intersect our bounds at all
0536: !bounds.intersects(clip)) {
0537:
0538: return;
0539: }
0540:
0541: Point upperLeft = clip.getLocation();
0542: Point lowerRight = new Point(clip.x + clip.width - 1, clip.y
0543: + clip.height - 1);
0544: int rMin = this .table.rowAtPoint(upperLeft);
0545: int rMax = this .table.rowAtPoint(lowerRight);
0546: // This should never happen (as long as our bounds intersect the clip,
0547: // which is why we bail above if that is the case).
0548: if (rMin == -1) {
0549: rMin = 0;
0550: }
0551: // If the table does not have enough rows to fill the view we'll get -1.
0552: // (We could also get -1 if our bounds don't intersect the clip,
0553: // which is why we bail above if that is the case).
0554: // Replace this with the index of the last row.
0555: if (rMax == -1) {
0556: rMax = this .table.getRowCount() - 1;
0557: }
0558:
0559: boolean ltr = this .table.getComponentOrientation()
0560: .isLeftToRight();
0561: int cMin = this .table.columnAtPoint(ltr ? upperLeft
0562: : lowerRight);
0563: int cMax = this .table.columnAtPoint(ltr ? lowerRight
0564: : upperLeft);
0565: // This should never happen.
0566: if (cMin == -1) {
0567: cMin = 0;
0568: }
0569: // If the table does not have enough columns to fill the view we'll get
0570: // -1.
0571: // Replace this with the index of the last column.
0572: if (cMax == -1) {
0573: cMax = this .table.getColumnCount() - 1;
0574: }
0575:
0576: // Paint the cells.
0577: this .paintCells(g, rMin, rMax, cMin, cMax);
0578:
0579: // Paint the grid.
0580: this .paintGrid(g, rMin, rMax, cMin, cMax);
0581: }
0582:
0583: /**
0584: * Paints the grid lines within <I>aRect</I>, using the grid color set with
0585: * <I>setGridColor</I>. Paints vertical lines if
0586: * <code>getShowVerticalLines()</code> returns true and paints horizontal
0587: * lines if <code>getShowHorizontalLines()</code> returns true.
0588: */
0589: private void paintGrid(Graphics g, int rMin, int rMax, int cMin,
0590: int cMax) {
0591: Graphics2D g2d = (Graphics2D) g.create();
0592: ComponentState currState = this .table.isEnabled() ? ComponentState.DEFAULT
0593: : ComponentState.DISABLED_UNSELECTED;
0594: float alpha = SubstanceThemeUtilities.getTheme(this .table)
0595: .getThemeAlpha(this .table, currState);
0596: g2d.setComposite(TransitionLayout.getAlphaComposite(this .table,
0597: alpha, g));
0598:
0599: Color gridColor = this .table.getGridColor();
0600: if (gridColor instanceof UIResource) {
0601: SubstanceTheme theme = SubstanceThemeUtilities.getTheme(
0602: this .table,
0603: this .table.isEnabled() ? ComponentState.DEFAULT
0604: : ComponentState.DISABLED_UNSELECTED);
0605: gridColor = theme.getLineColor();
0606:
0607: // // support for enhancement 256 - colorization of controls.
0608: // boolean hasColorization = SubstanceCoreUtilities
0609: // .hasColorization(this.table);
0610: // if (hasColorization) {
0611: // Color backgr = this.table.getBackground();
0612: // if (!(backgr instanceof UIResource)) {
0613: // double colorizationFactor = SubstanceCoreUtilities
0614: // .getColorizationFactor(this.table);
0615: // if (!table.isEnabled())
0616: // colorizationFactor /= 2.0;
0617: // Color toShiftTo = SubstanceColorUtilities
0618: // .deriveByBrightness(backgr, gridColor);
0619: // gridColor = SubstanceColorUtilities.getInterpolatedColor(
0620: // toShiftTo, gridColor, colorizationFactor);
0621: // }
0622: // }
0623: }
0624: g2d.setColor(gridColor);
0625:
0626: Rectangle minCell = this .table.getCellRect(rMin, cMin, true);
0627: Rectangle maxCell = this .table.getCellRect(rMax, cMax, true);
0628: Rectangle damagedArea = minCell.union(maxCell);
0629:
0630: float strokeWidth = SubstanceSizeUtils
0631: .getBorderStrokeWidth(SubstanceSizeUtils
0632: .getComponentFontSize(this .table));
0633: g2d.setStroke(new BasicStroke(strokeWidth,
0634: BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
0635: g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0636: RenderingHints.VALUE_ANTIALIAS_ON);
0637:
0638: if (this .table.getShowHorizontalLines()) {
0639: int tableWidth = damagedArea.x + damagedArea.width;
0640: int y = damagedArea.y;
0641: for (int row = rMin; row <= rMax; row++) {
0642: y += this .table.getRowHeight(row);
0643: g2d.drawLine(damagedArea.x, y - 1, tableWidth - 1,
0644: y - 1);
0645: }
0646: }
0647: if (this .table.getShowVerticalLines()) {
0648: TableColumnModel cm = this .table.getColumnModel();
0649: int tableHeight = damagedArea.y + damagedArea.height;
0650: int x;
0651: if (this .table.getComponentOrientation().isLeftToRight()) {
0652: x = damagedArea.x;
0653: for (int column = cMin; column <= cMax; column++) {
0654: int w = cm.getColumn(column).getWidth();
0655: x += w;
0656: if (column != (cm.getColumnCount() - 1)) {
0657: g2d.drawLine(x - 1, 0, x - 1, tableHeight - 1);
0658: }
0659: }
0660: } else {
0661: x = damagedArea.x + damagedArea.width;
0662: // fix for defect 196 - proper grid painting on RTL tables
0663: for (int column = cMin; column <= cMax; column++) {
0664: g2d.drawLine(x - 1, 0, x - 1, tableHeight - 1);
0665: int w = cm.getColumn(column).getWidth();
0666: x -= w;
0667: }
0668: // x -= cm.getColumn(cMax).getWidth();
0669: g2d.drawLine(x, 0, x, tableHeight - 1);
0670: }
0671: }
0672: g2d.dispose();
0673: }
0674:
0675: private int viewIndexForColumn(TableColumn aColumn) {
0676: TableColumnModel cm = this .table.getColumnModel();
0677: for (int column = 0; column < cm.getColumnCount(); column++) {
0678: if (cm.getColumn(column) == aColumn) {
0679: return column;
0680: }
0681: }
0682: return -1;
0683: }
0684:
0685: private void paintCells(Graphics g, int rMin, int rMax, int cMin,
0686: int cMax) {
0687: JTableHeader header = this .table.getTableHeader();
0688: TableColumn draggedColumn = (header == null) ? null : header
0689: .getDraggedColumn();
0690:
0691: TableColumnModel cm = this .table.getColumnModel();
0692: int columnMargin = cm.getColumnMargin();
0693:
0694: Rectangle cellRect;
0695: TableColumn aColumn;
0696: int columnWidth;
0697: if (this .table.getComponentOrientation().isLeftToRight()) {
0698: for (int row = rMin; row <= rMax; row++) {
0699: cellRect = this .table.getCellRect(row, cMin, false);
0700: if (!this .table.getShowHorizontalLines()) {
0701: cellRect.y -= this .table.getRowMargin() / 2;
0702: cellRect.height += this .table.getRowMargin();
0703: }
0704: for (int column = cMin; column <= cMax; column++) {
0705: aColumn = cm.getColumn(column);
0706: columnWidth = aColumn.getWidth();
0707:
0708: boolean toSubtractColumnMargin = this .table
0709: .getShowVerticalLines() ? (column < (cm
0710: .getColumnCount() - 1)) : false;
0711: cellRect.width = columnWidth;
0712: if (toSubtractColumnMargin)
0713: cellRect.width -= columnMargin;
0714:
0715: if (aColumn != draggedColumn) {
0716: this .paintCell(g, cellRect, row, column);
0717: }
0718: cellRect.x += columnWidth;
0719: }
0720: }
0721: } else {
0722: for (int row = rMin; row <= rMax; row++) {
0723: cellRect = this .table.getCellRect(row, cMin, false);
0724: if (!this .table.getShowHorizontalLines())
0725: cellRect.height += this .table.getRowMargin();
0726: aColumn = cm.getColumn(cMin);
0727: if (aColumn != draggedColumn) {
0728: columnWidth = aColumn.getWidth();
0729:
0730: cellRect.width = columnWidth;
0731: boolean toSubtractColumnMargin = this .table
0732: .getShowVerticalLines() ? (cMin == 0)
0733: : false;
0734: if (toSubtractColumnMargin)
0735: cellRect.width -= columnMargin;
0736:
0737: this .paintCell(g, cellRect, row, cMin);
0738: }
0739: for (int column = cMin + 1; column <= cMax; column++) {
0740: aColumn = cm.getColumn(column);
0741: columnWidth = aColumn.getWidth();
0742:
0743: // boolean toSubtractColumnMargin =
0744: // this.table.getShowVerticalLines() ?
0745: // (column < (cm.getColumnCount()-1)) : false;
0746: cellRect.width = columnWidth;
0747: if (this .table.getShowVerticalLines())
0748: cellRect.width -= columnMargin;
0749:
0750: cellRect.x -= columnWidth;
0751: if (aColumn != draggedColumn) {
0752: this .paintCell(g, cellRect, row, column);
0753: }
0754: }
0755: }
0756: }
0757:
0758: // Paint the dragged column if we are dragging.
0759: if (draggedColumn != null) {
0760: Graphics2D g2d = (Graphics2D) g.create();
0761: // enhancement 331 - translucent dragged column
0762: g2d.setComposite(TransitionLayout.getAlphaComposite(
0763: this .table, 0.65f, g));
0764: this .paintDraggedArea(g2d, rMin, rMax, draggedColumn,
0765: header.getDraggedDistance());
0766: g2d.dispose();
0767: }
0768:
0769: // Remove any renderers that may be left in the rendererPane.
0770: this .rendererPane.removeAll();
0771: }
0772:
0773: private void paintDraggedArea(Graphics g, int rMin, int rMax,
0774: TableColumn draggedColumn, int distance) {
0775: int draggedColumnIndex = this .viewIndexForColumn(draggedColumn);
0776:
0777: Rectangle minCell = this .table.getCellRect(rMin,
0778: draggedColumnIndex, true);
0779: Rectangle maxCell = this .table.getCellRect(rMax,
0780: draggedColumnIndex, true);
0781:
0782: Rectangle vacatedColumnRect = minCell.union(maxCell);
0783:
0784: // Paint a gray well in place of the moving column.
0785: g.setColor(this .table.getParent().getBackground());
0786: g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
0787: vacatedColumnRect.width, vacatedColumnRect.height);
0788:
0789: // Move to the where the cell has been dragged.
0790: vacatedColumnRect.x += distance;
0791:
0792: // Fill the background.
0793: g.setColor(this .table.getBackground());
0794: g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
0795: vacatedColumnRect.width, vacatedColumnRect.height);
0796:
0797: // Paint the vertical grid lines if necessary.
0798: if (this .table.getShowVerticalLines()) {
0799: g.setColor(this .table.getGridColor());
0800: int x1 = vacatedColumnRect.x;
0801: int y1 = vacatedColumnRect.y;
0802: int x2 = x1 + vacatedColumnRect.width - 1;
0803: int y2 = y1 + vacatedColumnRect.height - 1;
0804: // Left
0805: g.drawLine(x1 - 1, y1, x1 - 1, y2);
0806: // Right
0807: g.drawLine(x2, y1, x2, y2);
0808: }
0809:
0810: for (int row = rMin; row <= rMax; row++) {
0811: // Render the cell value
0812: Rectangle r = this .table.getCellRect(row,
0813: draggedColumnIndex, false);
0814: r.x += distance;
0815: this .paintCell(g, r, row, draggedColumnIndex);
0816:
0817: // Paint the (lower) horizontal grid line if necessary.
0818: if (this .table.getShowHorizontalLines()) {
0819: g.setColor(this .table.getGridColor());
0820: Rectangle rcr = this .table.getCellRect(row,
0821: draggedColumnIndex, true);
0822: rcr.x += distance;
0823: int x1 = rcr.x;
0824: int y1 = rcr.y;
0825: int x2 = x1 + rcr.width - 1;
0826: int y2 = y1 + rcr.height - 1;
0827: g.drawLine(x1, y2, x2, y2);
0828: }
0829: }
0830: }
0831:
0832: protected void paintCell(Graphics g, Rectangle cellRect, int row,
0833: int column) {
0834: Graphics2D g2d = (Graphics2D) g.create();
0835: // fix for issue 183 - passing the original Graphics context
0836: // to compute the alpha composite. If the table is in a JXPanel
0837: // (component from SwingX) and it has custom alpha value set,
0838: // then the original graphics context will have a SRC_OVER
0839: // alpha composite applied to it.
0840: g2d.setComposite(TransitionLayout.getAlphaComposite(this .table,
0841: g));
0842:
0843: TableCellId cellId = new TableCellId(row, column);
0844: boolean isRollover = ((this .rolledOverId != null) && this .rolledOverId
0845: .equals(cellId));
0846:
0847: final ComponentState prevState = this .getPrevCellState(cellId);
0848: final ComponentState currState = this .getCellState(cellId);
0849: // System.out.println(cellId + ":" + prevState.name() + "->"
0850: // + currState.name());
0851: // float alphaForPrevBackground = 0.0f;
0852:
0853: final SubstanceTheme prevTheme = SubstanceThemeUtilities
0854: .getHighlightTheme(this .table, prevState);
0855: final SubstanceTheme currTheme = SubstanceThemeUtilities
0856: .getHighlightTheme(this .table, currState);
0857:
0858: // Compute the alpha values for the animation.
0859: float startAlpha = SubstanceThemeUtilities.getHighlightAlpha(
0860: this .table, prevState);
0861: float endAlpha = SubstanceThemeUtilities.getHighlightAlpha(
0862: this .table, currState);
0863:
0864: FadeState state = SubstanceFadeUtilities.getFadeState(
0865: this .table, cellId, FadeKind.SELECTION,
0866: FadeKind.ROLLOVER);
0867: float totalAlpha = endAlpha;
0868: float fadeCoef = 0.0f;
0869: // System.out.println("-------- (" + System.currentTimeMillis() % 10000
0870: // + ") " + row + ":" + column + " [" + cellRect + "] --------");
0871: if (state != null) {
0872: // System.out.println("State not null on " + row + ":" + column +
0873: // ":"
0874: // + state.fadeKind + ":" + state.getFadePosition());
0875: // System.out.println("States : " + prevState + "->" + currState);
0876: fadeCoef = state.getFadePosition();
0877:
0878: // compute the total alpha of the overlays.
0879: if (state.isFadingIn()) {
0880: totalAlpha = startAlpha + (endAlpha - startAlpha)
0881: * fadeCoef / 10.0f;
0882: } else {
0883: totalAlpha = startAlpha + (endAlpha - startAlpha)
0884: * (10.0f - fadeCoef) / 10.0f;
0885: }
0886:
0887: if (state.isFadingIn())
0888: fadeCoef = 10.0f - fadeCoef;
0889:
0890: // System.out.println("prev alpha " + alphaForPrevBackground
0891: // + ", curr alpha " + alphaForCurrBackground);
0892: // System.out.println("from " + prevTheme.getDisplayName() + " to "
0893: // + currTheme.getDisplayName());
0894: }
0895:
0896: // System.out.println("[" + row + ":" + column + "] from "
0897: // + prevTheme.getDisplayName() + "[at " + alphaForPrevBackground
0898: // + "] to " + currTheme.getDisplayName() + "[at "
0899: // + alphaForCurrBackground + "]");
0900:
0901: if (!this .hasSelectionAnimations()
0902: && (prevState.isKindActive(FadeKind.SELECTION) || currState
0903: .isKindActive(FadeKind.SELECTION))) {
0904: // no animations on selected cells in big tables - bug 209
0905: fadeCoef = 0.0f;
0906: }
0907:
0908: final Set<SubstanceConstants.Side> highlightOpenSides = new HashSet<SubstanceConstants.Side>();
0909: // show highlight border only when the table grid is not shown
0910: final float highlightBorderAlpha = (table
0911: .getShowHorizontalLines() || table
0912: .getShowVerticalLines()) ? 0.0f : 0.8f;
0913: if (!table.getColumnSelectionAllowed()
0914: && table.getRowSelectionAllowed()) {
0915: // if row selection is on and column selection is off, we will
0916: // show the highlight for the entire row
0917:
0918: // all cells have open left side
0919: highlightOpenSides.add(SubstanceConstants.Side.LEFT);
0920: // all cells have open right side
0921: highlightOpenSides.add(SubstanceConstants.Side.RIGHT);
0922: }
0923: if (table.getColumnSelectionAllowed()
0924: && !table.getRowSelectionAllowed()) {
0925: // if row selection is off and column selection is on, we will
0926: // show the highlight for the entire column
0927:
0928: // if (table.getTableHeader().isVisible() || (row > 0)) {
0929: // the top side is open for all rows except the
0930: // first, or when the table header is visible
0931: highlightOpenSides.add(SubstanceConstants.Side.TOP);
0932: // }
0933: // if (row < (this.table.getRowCount() - 1)) {
0934: // all cells but the last have open bottom side
0935: highlightOpenSides.add(SubstanceConstants.Side.BOTTOM);
0936: // }
0937: }
0938: if (row > 1) {
0939: ComponentState upperNeighbourState = this
0940: .getCellState(new TableCellId(row - 1, column));
0941: if (currState == upperNeighbourState) {
0942: // the cell above it is in the same state
0943: highlightOpenSides.add(SubstanceConstants.Side.TOP);
0944: }
0945: }
0946: if (column > 1) {
0947: ComponentState leftNeighbourState = this
0948: .getCellState(new TableCellId(row, column - 1));
0949: if (currState == leftNeighbourState) {
0950: // the cell to the left is in the same state
0951: highlightOpenSides.add(SubstanceConstants.Side.LEFT);
0952: }
0953: }
0954: // if ((row == 0) && table.getTableHeader().isVisible()) {
0955: // // special case for a selected cell in first row when the
0956: // // table header is visible - open top side
0957: // highlightOpenSides.add(SubstanceConstants.Side.TOP);
0958: // }
0959: if (row == 0) {
0960: highlightOpenSides.add(SubstanceConstants.Side.TOP);
0961: }
0962: if (row == (table.getRowCount() - 1)) {
0963: highlightOpenSides.add(SubstanceConstants.Side.BOTTOM);
0964: }
0965: if (column == 0) {
0966: highlightOpenSides.add(SubstanceConstants.Side.LEFT);
0967: }
0968: if (column == (table.getColumnCount() - 1)) {
0969: highlightOpenSides.add(SubstanceConstants.Side.RIGHT);
0970: }
0971:
0972: if (this .table.isEditing() && this .table.getEditingRow() == row
0973: && this .table.getEditingColumn() == column) {
0974: Component component = this .table.getEditorComponent();
0975:
0976: if (totalAlpha > 0.0f) {
0977: g2d.setComposite(TransitionLayout.getAlphaComposite(
0978: this .table, totalAlpha, g));
0979: SubstanceHighlightUtils.paintHighlight(g2d, component,
0980: cellRect, highlightBorderAlpha,
0981: highlightOpenSides, currTheme.getColorScheme(),
0982: prevTheme.getColorScheme(), fadeCoef);
0983: g2d.setComposite(TransitionLayout.getAlphaComposite(
0984: this .table, g));
0985: }
0986:
0987: if (component instanceof JComponent) {
0988: // Play with opaqueness to make our own gradient background
0989: // on selected elements to show.
0990: JComponent jRenderer = (JComponent) component;
0991: synchronized (jRenderer) {
0992: // Compute the selection status to prevent flicker - JTable
0993: // registers a listener on selection changes and repaints
0994: // the relevant cell before our listener (in TableUI) gets
0995: // the chance to start the fade sequence. The result is that
0996: // the first frame uses full opacity, and the next frame
0997: // starts the fade sequence. So, we use the UI delegate to
0998: // compute the selection status.
0999: boolean newOpaque = !(this .selectedIndices
1000: .containsKey(cellId)
1001: || isRollover || (state != null));
1002: if (SubstanceCoreUtilities
1003: .toBleedWatermark(this .table))
1004: newOpaque = false;
1005:
1006: Map<Component, Boolean> opacity = new HashMap<Component, Boolean>();
1007: // System.out.println("Pre-painting at index " + row + " ["
1008: // +
1009: // value
1010: // + "] " + (rendererComponent.isOpaque() ? "opaque" :
1011: // "transparent")
1012: // + " with bg " + rendererComponent.getBackground());
1013: if (!newOpaque)
1014: SubstanceCoreUtilities.makeNonOpaque(component,
1015: opacity);
1016: // System.out.println("Painting "
1017: // + (newOpaque ? "opaque" : "transparent")
1018: // + " with bg " + component.getBackground());
1019: component.setBounds(cellRect);
1020: component.validate();
1021: // System.out.println("Painting at index " + row + " [" +
1022: // value
1023: // + "] " + (newOpaque ? "opaque" : "transparent")
1024: // + " with bg " + rendererComponent.getBackground());
1025: if (!newOpaque)
1026: SubstanceCoreUtilities.restoreOpaque(component,
1027: opacity);
1028: // System.out.println("Post-painting at index " + row + " ["
1029: // +
1030: // value
1031: // + "] " + (rendererComponent.isOpaque() ? "opaque" :
1032: // "transparent")
1033: // + " with bg " + rendererComponent.getBackground());
1034: }
1035: } else {
1036: component.setBounds(cellRect);
1037: component.validate();
1038: }
1039:
1040: } else {
1041: TableCellRenderer renderer = this .table.getCellRenderer(
1042: row, column);
1043: final Component rendererComponent = this .table
1044: .prepareRenderer(renderer, row, column);
1045:
1046: SubstanceTextPainter textPainter = SubstanceLookAndFeel
1047: .getCurrentTextPainter();
1048: final boolean isWatermarkBleed = SubstanceCoreUtilities
1049: .toBleedWatermark(this .table);
1050: if (rendererComponent != null) {
1051: if (textPainter.needsBackgroundImage()) {
1052: final Rectangle offsetHighlightRect = new Rectangle(
1053: 0, 0, cellRect.width, cellRect.height);
1054: textPainter.init(table, offsetHighlightRect, true);
1055: textPainter.setBackgroundFill(table, null, false,
1056: 0, 0);
1057: textPainter
1058: .attachCallback(new SubstanceTextPainter.BackgroundPaintingCallback() {
1059: public void paintBackground(Graphics g) {
1060: Graphics2D g2d = (Graphics2D) g
1061: .create();
1062: if (!isWatermarkBleed) {
1063: // fill with the renderer
1064: // background color
1065: g2d.setColor(rendererComponent
1066: .getBackground());
1067: g2d
1068: .fillRect(
1069: 0,
1070: 0,
1071: offsetHighlightRect.width,
1072: offsetHighlightRect.height);
1073: } else {
1074: SubstanceFillBackgroundDelegate.GLOBAL_INSTANCE
1075: .fillAndWatermark(
1076: g2d,
1077: table,
1078: rendererComponent
1079: .getBackground(),
1080: offsetHighlightRect);
1081: }
1082: g2d.dispose();
1083: }
1084: });
1085: } else {
1086: if (!isWatermarkBleed) {
1087: // fill with the renderer background color
1088: g2d.setColor(rendererComponent.getBackground());
1089: g2d.fillRect(cellRect.x, cellRect.y,
1090: cellRect.width, cellRect.height);
1091: } else {
1092: SubstanceFillBackgroundDelegate.GLOBAL_INSTANCE
1093: .fillAndWatermark(g2d, this .table,
1094: rendererComponent
1095: .getBackground(),
1096: cellRect);
1097: }
1098: }
1099: }
1100: if (textPainter.needsBackgroundImage()) {
1101: final float finalTotalAlpha = totalAlpha;
1102: final float finalFadeCoef = fadeCoef;
1103:
1104: // The text GC will be clipped to the renderer bound. To have
1105: // the background painted in the correct location, enforce this
1106: // by painting the background in 0, 0 position.
1107: final Rectangle zeroCellRect = new Rectangle(0, 0,
1108: cellRect.width, cellRect.height);
1109: textPainter.setBackgroundFill(table, rendererComponent
1110: .getBackground(), true, cellRect.x, cellRect.y);
1111: textPainter
1112: .attachCallback(new SubstanceTextPainter.BackgroundPaintingCallback() {
1113: public void paintBackground(Graphics g) {
1114: Graphics2D g2d = (Graphics2D) g
1115: .create();
1116:
1117: if (finalTotalAlpha > 0.0f) {
1118: g2d
1119: .setComposite(TransitionLayout
1120: .getAlphaComposite(
1121: table,
1122: finalTotalAlpha,
1123: g));
1124: SubstanceHighlightUtils
1125: .paintHighlight(
1126: g2d,
1127: rendererComponent,
1128: zeroCellRect,
1129: highlightBorderAlpha,
1130: highlightOpenSides,
1131: currTheme
1132: .getColorScheme(),
1133: prevTheme
1134: .getColorScheme(),
1135: finalFadeCoef);
1136: g2d
1137: .setComposite(TransitionLayout
1138: .getAlphaComposite(
1139: table, g));
1140: }
1141: }
1142: });
1143: } else {
1144: if (totalAlpha > 0.0f) {
1145: g2d.setComposite(TransitionLayout
1146: .getAlphaComposite(this .table, totalAlpha,
1147: g));
1148: float extra = SubstanceSizeUtils
1149: .getBorderStrokeWidth(SubstanceSizeUtils
1150: .getComponentFontSize(this .table
1151: .getTableHeader()));
1152: float extraWidth = highlightOpenSides
1153: .contains(SubstanceConstants.Side.LEFT) ? 0.0f
1154: : extra;
1155: float extraHeight = highlightOpenSides
1156: .contains(SubstanceConstants.Side.TOP) ? 0.0f
1157: : extra;
1158: // if ((column > 0) && isFocusedCell(row, column - 1))
1159: // extraWidth = 0.0f;
1160: // if ((row > 0) && isFocusedCell(row - 1, column))
1161: // extraHeight = 0.0f;
1162: // System.out
1163: // .println(row + ":" + column + ":"
1164: // + rendererComponent.getBackground() + ":"
1165: // + totalAlpha + ":"
1166: // + currTheme.getDisplayName() + ":"
1167: // + prevTheme.getDisplayName() + ":"
1168: // + fadeCoef);
1169: SubstanceHighlightUtils.paintHighlight(g2d,
1170: rendererComponent, new Rectangle(cellRect.x
1171: - (int) extraWidth, cellRect.y
1172: - (int) extraHeight, cellRect.width
1173: + (int) extraWidth, cellRect.height
1174: + (int) extraHeight),
1175: highlightBorderAlpha, highlightOpenSides,
1176: currTheme.getColorScheme(), prevTheme
1177: .getColorScheme(), fadeCoef);
1178: g2d.setComposite(TransitionLayout
1179: .getAlphaComposite(this .table, g));
1180: }
1181: }
1182:
1183: this .table.putClientProperty(
1184: SubstanceCoreUtilities.DO_NOT_FILL_BACKGROUND,
1185: Boolean.TRUE);
1186: if (rendererComponent instanceof JComponent) {
1187: // Play with opaqueness to make our own gradient background
1188: // on selected elements to show.
1189: JComponent jRenderer = (JComponent) rendererComponent;
1190: synchronized (jRenderer) {
1191: // Compute the selection status to prevent flicker - JTable
1192: // registers a listener on selection changes and repaints
1193: // the relevant cell before our listener (in TableUI) gets
1194: // the chance to start the fade sequence. The result is that
1195: // the first frame uses full opacity, and the next frame
1196: // starts the fade sequence. So, we use the UI delegate to
1197: // compute the selection status.
1198: boolean isSelected = this .hasSelectionAnimations() ? this .selectedIndices
1199: .containsKey(cellId)
1200: : this .table.isCellSelected(row, column);
1201: boolean newOpaque = !(isSelected || isRollover || (state != null));
1202: if (SubstanceCoreUtilities
1203: .toBleedWatermark(this .table))
1204: newOpaque = false;
1205:
1206: Map<Component, Boolean> opacity = new HashMap<Component, Boolean>();
1207: // System.out.println("Pre-painting at index " + row + " ["
1208: // +
1209: // value
1210: // + "] " + (rendererComponent.isOpaque() ? "opaque" :
1211: // "transparent")
1212: // + " with bg " + rendererComponent.getBackground());
1213: if (!newOpaque)
1214: SubstanceCoreUtilities.makeNonOpaque(jRenderer,
1215: opacity);
1216: // System.out.println("Painting "
1217: // + rendererComponent.getClass().getSimpleName()
1218: // + " at " + row + ":" + column + " "
1219: // + (newOpaque ? "opaque" : "transparent")
1220: // + " with bg " + rendererComponent.getBackground());
1221: this .rendererPane.paintComponent(g2d,
1222: rendererComponent, this .table, cellRect.x,
1223: cellRect.y, cellRect.width,
1224: cellRect.height, true);
1225: // System.out.println("Painting at index " + row + " [" +
1226: // value
1227: // + "] " + (newOpaque ? "opaque" : "transparent")
1228: // + " with bg " + rendererComponent.getBackground());
1229: if (!newOpaque)
1230: SubstanceCoreUtilities.restoreOpaque(jRenderer,
1231: opacity);
1232: // System.out.println("Post-painting at index " + row + " ["
1233: // +
1234: // value
1235: // + "] " + (rendererComponent.isOpaque() ? "opaque" :
1236: // "transparent")
1237: // + " with bg " + rendererComponent.getBackground());
1238: }
1239: } else {
1240: this .rendererPane.paintComponent(g2d,
1241: rendererComponent, this .table, cellRect.x,
1242: cellRect.y, cellRect.width, cellRect.height,
1243: true);
1244: }
1245: this .table
1246: .putClientProperty(
1247: SubstanceCoreUtilities.DO_NOT_FILL_BACKGROUND,
1248: null);
1249: }
1250: // System.out
1251: // .println("------------------------------------------------------------------------------");
1252: g2d.dispose();
1253: }
1254:
1255: /**
1256: * Repaints a single cell during the fade animation cycle.
1257: *
1258: * @author Kirill Grouchnikov
1259: */
1260: protected class CellRepaintCallback extends FadeTrackerAdapter {
1261: /**
1262: * Associated table.
1263: */
1264: protected JTable table;
1265:
1266: /**
1267: * Associated (animated) row index.
1268: */
1269: protected int rowIndex;
1270:
1271: /**
1272: * Associated (animated) column index.
1273: */
1274: protected int columnIndex;
1275:
1276: /**
1277: * Creates a new animation repaint callback.
1278: *
1279: * @param table
1280: * Associated table.
1281: * @param rowIndex
1282: * Associated (animated) row index.
1283: * @param columnIndex
1284: * Associated (animated) column index.
1285: */
1286: public CellRepaintCallback(JTable table, int rowIndex,
1287: int columnIndex) {
1288: super ();
1289: this .table = table;
1290: this .rowIndex = rowIndex;
1291: this .columnIndex = columnIndex;
1292: }
1293:
1294: /*
1295: * (non-Javadoc)
1296: *
1297: * @see org.jvnet.lafwidget.utils.FadeTracker$FadeTrackerCallback#fadeEnded(org.jvnet.lafwidget.utils.FadeTracker.FadeKind)
1298: */
1299: @Override
1300: public void fadeEnded(FadeKind fadeKind) {
1301: if ((SubstanceTableUI.this .table == this .table)
1302: && (this .rowIndex < this .table.getRowCount())
1303: && (this .columnIndex < this .table.getColumnCount())) {
1304: TableCellId cellIndex = new TableCellId(this .rowIndex,
1305: this .columnIndex);
1306: ComponentState currState = SubstanceTableUI.this
1307: .getCellState(cellIndex);
1308: // boolean isLarge = (table.getRowCount() *
1309: // table.getColumnCount() > 1000);
1310: if (currState == ComponentState.DEFAULT) {
1311: // || (isLarge && currState.isSelected()))
1312: SubstanceTableUI.this .prevStateMap
1313: .remove(cellIndex);
1314: SubstanceTableUI.this .nextStateMap
1315: .remove(cellIndex);
1316: } else {
1317: SubstanceTableUI.this .prevStateMap.put(cellIndex,
1318: currState);
1319: SubstanceTableUI.this .nextStateMap.put(cellIndex,
1320: currState);
1321: }
1322: // System.out.println(rowIndex + ":" + columnIndex + "->"
1323: // + prevStateMap.get(cellIndex).name());
1324: }
1325: // System.out.println("Cell - Fade ended on " + rowIndex + ":"
1326: // + columnIndex);
1327: this .repaintCell();
1328: }
1329:
1330: /*
1331: * (non-Javadoc)
1332: *
1333: * @see org.jvnet.lafwidget.utils.FadeTracker$FadeTrackerCallback#fadePerformed(org.jvnet.lafwidget.utils.FadeTracker.FadeKind,
1334: * float)
1335: */
1336: @Override
1337: public void fadePerformed(FadeKind fadeKind, float fade10) {
1338: // System.out.println("Cell - " + fadeKind + " on " + rowIndex + ":"
1339: // + columnIndex + ":" + fade10);
1340: if ((SubstanceTableUI.this .table == this .table)
1341: && (this .rowIndex < this .table.getRowCount())
1342: && (this .columnIndex < this .table.getColumnCount())) {
1343: TableCellId cellIndex = new TableCellId(this .rowIndex,
1344: this .columnIndex);
1345: SubstanceTableUI.this .nextStateMap.put(cellIndex,
1346: SubstanceTableUI.this .getCellState(cellIndex));
1347: }
1348: this .repaintCell();
1349: }
1350:
1351: /*
1352: * (non-Javadoc)
1353: *
1354: * @see org.jvnet.lafwidget.animation.FadeTrackerAdapter#fadeReversed(org.jvnet.lafwidget.animation.FadeKind,
1355: * boolean, float)
1356: */
1357: @Override
1358: public void fadeReversed(FadeKind fadeKind, boolean isFadingIn,
1359: float fadeCycle10) {
1360: if ((SubstanceTableUI.this .table == this .table)
1361: && (this .rowIndex < this .table.getRowCount())
1362: && (this .columnIndex < this .table.getColumnCount())) {
1363: TableCellId cellIndex = new TableCellId(this .rowIndex,
1364: this .columnIndex);
1365: ComponentState nextState = SubstanceTableUI.this .nextStateMap
1366: .get(cellIndex);
1367: if (nextState == null) {
1368: SubstanceTableUI.this .prevStateMap
1369: .remove(cellIndex);
1370: } else {
1371: SubstanceTableUI.this .prevStateMap.put(cellIndex,
1372: nextState);
1373: }
1374: // System.out.println(tabIndex + "->"
1375: // + prevStateMap.get(tabIndex).name());
1376: }
1377: this .repaintCell();
1378: }
1379:
1380: /**
1381: * Repaints the associated cell.
1382: */
1383: private void repaintCell() {
1384: SwingUtilities.invokeLater(new Runnable() {
1385: public void run() {
1386: if (SubstanceTableUI.this .table == null) {
1387: // may happen if the LAF was switched in the meantime
1388: return;
1389: }
1390: int rowCount = CellRepaintCallback.this .table
1391: .getRowCount();
1392: int colCount = CellRepaintCallback.this .table
1393: .getColumnCount();
1394: if ((rowCount > 0)
1395: && (CellRepaintCallback.this .rowIndex < rowCount)
1396: && (colCount > 0)
1397: && (CellRepaintCallback.this .columnIndex < colCount)) {
1398: // need to retrieve the cell rectangle since the cells
1399: // can be moved while animating
1400: Rectangle rect = CellRepaintCallback.this .table
1401: .getCellRect(
1402: CellRepaintCallback.this .rowIndex,
1403: CellRepaintCallback.this .columnIndex,
1404: true);
1405:
1406: if (!table.getShowHorizontalLines()
1407: && !table.getShowVerticalLines()) {
1408: float extra = SubstanceSizeUtils
1409: .getBorderStrokeWidth(SubstanceSizeUtils
1410: .getComponentFontSize(table
1411: .getTableHeader()));
1412: rect.x -= (int) extra;
1413: rect.width += 2 * (int) extra;
1414: rect.y -= (int) extra;
1415: rect.height += 2 * (int) extra;
1416: }
1417: // System.out.println("Cell Repainting " + rowIndex +
1418: // ":"
1419: // + columnIndex + ":" + rect);
1420: CellRepaintCallback.this .table.repaint(rect);
1421: }
1422: }
1423: });
1424: }
1425: }
1426:
1427: /**
1428: * Repaints a single row during the fade animation cycle.
1429: *
1430: * @author Kirill Grouchnikov
1431: */
1432: protected class RowRepaintCallback extends FadeTrackerAdapter {
1433: /**
1434: * Associated table.
1435: */
1436: protected JTable table;
1437:
1438: /**
1439: * Associated (animated) row index.
1440: */
1441: protected int rowIndex;
1442:
1443: /**
1444: * Creates a new animation repaint callback.
1445: *
1446: * @param table
1447: * Associated table.
1448: * @param rowIndex
1449: * Associated (animated) row index.
1450: */
1451: public RowRepaintCallback(JTable table, int rowIndex) {
1452: super ();
1453: this .table = table;
1454: this .rowIndex = rowIndex;
1455: }
1456:
1457: /*
1458: * (non-Javadoc)
1459: *
1460: * @see org.jvnet.lafwidget.utils.FadeTracker$FadeTrackerCallback#fadeEnded(org.jvnet.lafwidget.utils.FadeTracker.FadeKind)
1461: */
1462: @Override
1463: public void fadeEnded(FadeKind fadeKind) {
1464: if ((SubstanceTableUI.this .table == this .table)
1465: && (this .rowIndex < this .table.getRowCount())) {
1466: for (int columnIndex = 0; columnIndex < this .table
1467: .getColumnCount(); columnIndex++) {
1468: TableCellId cellIndex = new TableCellId(
1469: this .rowIndex, columnIndex);
1470: ComponentState currState = SubstanceTableUI.this
1471: .getCellState(cellIndex);
1472: // boolean isLarge = (table.getRowCount()
1473: // * table.getColumnCount() > 1000);
1474: if (currState == ComponentState.DEFAULT) {
1475: // || (isLarge && currState.isSelected()))
1476: SubstanceTableUI.this .prevStateMap
1477: .remove(cellIndex);
1478: SubstanceTableUI.this .nextStateMap
1479: .remove(cellIndex);
1480: } else {
1481: SubstanceTableUI.this .prevStateMap.put(
1482: cellIndex, currState);
1483: SubstanceTableUI.this .nextStateMap.put(
1484: cellIndex, currState);
1485: }
1486: }
1487: // System.out.println(tabIndex + "->"
1488: // + prevStateMap.get(tabIndex).name());
1489: }
1490: // System.out.println("Fade ended on " + rowIndex);
1491: this .repaintRow();
1492: }
1493:
1494: /*
1495: * (non-Javadoc)
1496: *
1497: * @see org.jvnet.lafwidget.utils.FadeTracker$FadeTrackerCallback#fadePerformed(org.jvnet.lafwidget.utils.FadeTracker.FadeKind,
1498: * float)
1499: */
1500: @Override
1501: public void fadePerformed(FadeKind fadeKind, float fade10) {
1502: // System.out.println("Fade on " + rowIndex + ":" + fade10);
1503: if ((SubstanceTableUI.this .table == this .table)
1504: && (this .rowIndex < this .table.getRowCount())) {
1505: for (int columnIndex = 0; columnIndex < this .table
1506: .getColumnCount(); columnIndex++) {
1507: TableCellId cellIndex = new TableCellId(
1508: this .rowIndex, columnIndex);
1509: SubstanceTableUI.this .nextStateMap.put(cellIndex,
1510: SubstanceTableUI.this
1511: .getCellState(cellIndex));
1512: }
1513: }
1514: this .repaintRow();
1515: }
1516:
1517: /*
1518: * (non-Javadoc)
1519: *
1520: * @see org.jvnet.lafwidget.animation.FadeTrackerAdapter#fadeReversed(org.jvnet.lafwidget.animation.FadeKind,
1521: * boolean, float)
1522: */
1523: @Override
1524: public void fadeReversed(FadeKind fadeKind, boolean isFadingIn,
1525: float fadeCycle10) {
1526: if ((SubstanceTableUI.this .table == this .table)
1527: && (this .rowIndex < this .table.getRowCount())) {
1528: for (int columnIndex = 0; columnIndex < this .table
1529: .getColumnCount(); columnIndex++) {
1530: TableCellId cellIndex = new TableCellId(
1531: this .rowIndex, columnIndex);
1532: ComponentState nextState = SubstanceTableUI.this .nextStateMap
1533: .get(cellIndex);
1534: if (nextState == null) {
1535: SubstanceTableUI.this .prevStateMap
1536: .remove(cellIndex);
1537: } else {
1538: SubstanceTableUI.this .prevStateMap.put(
1539: cellIndex, nextState);
1540: }
1541: // System.out.println(tabIndex + "->"
1542: // + prevStateMap.get(tabIndex).name());
1543: }
1544: }
1545: this .repaintRow();
1546: }
1547:
1548: /**
1549: * Repaints the associated row.
1550: */
1551: private void repaintRow() {
1552: SwingUtilities.invokeLater(new Runnable() {
1553: public void run() {
1554: if (SubstanceTableUI.this .table == null) {
1555: // may happen if the LAF was switched in the meantime
1556: return;
1557: }
1558: int rowCount = RowRepaintCallback.this .table
1559: .getRowCount();
1560: if ((rowCount > 0)
1561: && (RowRepaintCallback.this .rowIndex < rowCount)) {
1562: // need to retrieve the cell rectangle since the cells
1563: // can be moved while animating
1564: Rectangle rect = RowRepaintCallback.this .table
1565: .getCellRect(
1566: RowRepaintCallback.this .rowIndex,
1567: 0, true);
1568: for (int i = 1; i < RowRepaintCallback.this .table
1569: .getColumnCount(); i++) {
1570: rect = rect
1571: .union(RowRepaintCallback.this .table
1572: .getCellRect(
1573: RowRepaintCallback.this .rowIndex,
1574: i, true));
1575: }
1576: if (!table.getShowHorizontalLines()
1577: && !table.getShowVerticalLines()) {
1578: float extra = SubstanceSizeUtils
1579: .getBorderStrokeWidth(SubstanceSizeUtils
1580: .getComponentFontSize(table
1581: .getTableHeader()));
1582: rect.y -= (int) extra;
1583: rect.height += 2 * (int) extra;
1584: }
1585: // System.out.println("Repainting row " + rowIndex
1586: // + " at " + rect);
1587: RowRepaintCallback.this .table.repaint(rect);
1588: }
1589: }
1590: });
1591: }
1592: }
1593:
1594: /**
1595: * Repaints a single column during the fade animation cycle.
1596: *
1597: * @author Kirill Grouchnikov
1598: */
1599: protected class ColumnRepaintCallback extends FadeTrackerAdapter {
1600: /**
1601: * Associated table.
1602: */
1603: protected JTable table;
1604:
1605: /**
1606: * Associated (animated) column index.
1607: */
1608: protected int columnIndex;
1609:
1610: /**
1611: * Creates a new animation repaint callback.
1612: *
1613: * @param table
1614: * Associated table.
1615: * @param columnIndex
1616: * Associated (animated) column index.
1617: */
1618: public ColumnRepaintCallback(JTable table, int columnIndex) {
1619: super ();
1620: this .table = table;
1621: this .columnIndex = columnIndex;
1622: }
1623:
1624: /*
1625: * (non-Javadoc)
1626: *
1627: * @see org.jvnet.lafwidget.utils.FadeTracker$FadeTrackerCallback#fadeEnded(org.jvnet.lafwidget.utils.FadeTracker.FadeKind)
1628: */
1629: @Override
1630: public void fadeEnded(FadeKind fadeKind) {
1631: if ((SubstanceTableUI.this .table == this .table)
1632: && (this .columnIndex < this .table.getColumnCount())) {
1633: for (int rowIndex = 0; rowIndex < this .table
1634: .getRowCount(); rowIndex++) {
1635: TableCellId cellIndex = new TableCellId(rowIndex,
1636: this .columnIndex);
1637: ComponentState currState = SubstanceTableUI.this
1638: .getCellState(cellIndex);
1639: // boolean isLarge = (table.getRowCount()
1640: // * table.getColumnCount() > 1000);
1641: if (currState == ComponentState.DEFAULT) {
1642: // || (isLarge && currState.isSelected()))
1643: SubstanceTableUI.this .prevStateMap
1644: .remove(cellIndex);
1645: SubstanceTableUI.this .nextStateMap
1646: .remove(cellIndex);
1647: } else {
1648: SubstanceTableUI.this .prevStateMap.put(
1649: cellIndex, currState);
1650: SubstanceTableUI.this .nextStateMap.put(
1651: cellIndex, currState);
1652: }
1653: }
1654: // System.out.println(tabIndex + "->"
1655: // + prevStateMap.get(tabIndex).name());
1656: }
1657: this .repaintColumn();
1658: }
1659:
1660: /*
1661: * (non-Javadoc)
1662: *
1663: * @see org.jvnet.lafwidget.animation.FadeTrackerAdapter#fadeReversed(org.jvnet.lafwidget.animation.FadeKind,
1664: * boolean, float)
1665: */
1666: @Override
1667: public void fadeReversed(FadeKind fadeKind, boolean isFadingIn,
1668: float fadeCycle10) {
1669: if ((SubstanceTableUI.this .table == this .table)
1670: && (this .columnIndex < this .table.getColumnCount())) {
1671: for (int rowIndex = 0; rowIndex < this .table
1672: .getRowCount(); rowIndex++) {
1673: TableCellId cellIndex = new TableCellId(rowIndex,
1674: this .columnIndex);
1675: ComponentState nextState = SubstanceTableUI.this .nextStateMap
1676: .get(cellIndex);
1677: if (nextState == null) {
1678: SubstanceTableUI.this .prevStateMap
1679: .remove(cellIndex);
1680: } else {
1681: SubstanceTableUI.this .prevStateMap.put(
1682: cellIndex, nextState);
1683: }
1684: // System.out.println(tabIndex + "->"
1685: // + prevStateMap.get(tabIndex).name());
1686: }
1687: }
1688: this .repaintColumn();
1689: }
1690:
1691: /*
1692: * (non-Javadoc)
1693: *
1694: * @see org.jvnet.lafwidget.utils.FadeTracker$FadeTrackerCallback#fadePerformed(org.jvnet.lafwidget.utils.FadeTracker.FadeKind,
1695: * float)
1696: */
1697: @Override
1698: public void fadePerformed(FadeKind fadeKind, float fade10) {
1699: if ((SubstanceTableUI.this .table == this .table)
1700: && (this .columnIndex < this .table.getColumnCount())) {
1701: for (int rowIndex = 0; rowIndex < this .table
1702: .getRowCount(); rowIndex++) {
1703: TableCellId cellIndex = new TableCellId(rowIndex,
1704: this .columnIndex);
1705: SubstanceTableUI.this .nextStateMap.put(cellIndex,
1706: SubstanceTableUI.this
1707: .getCellState(cellIndex));
1708: }
1709: }
1710: this .repaintColumn();
1711: }
1712:
1713: /**
1714: * Repaints the associated row.
1715: */
1716: private void repaintColumn() {
1717: SwingUtilities.invokeLater(new Runnable() {
1718: public void run() {
1719: if (SubstanceTableUI.this .table == null) {
1720: // may happen if the LAF was switched in the meantime
1721: return;
1722: }
1723: int columnCount = ColumnRepaintCallback.this .table
1724: .getColumnCount();
1725: if ((columnCount > 0)
1726: && (ColumnRepaintCallback.this .columnIndex < columnCount)) {
1727: // need to retrieve the cell rectangle since the cells
1728: // can be moved while animating
1729: Rectangle rect = ColumnRepaintCallback.this .table
1730: .getCellRect(
1731: 0,
1732: ColumnRepaintCallback.this .columnIndex,
1733: true);
1734: for (int i = 1; i < ColumnRepaintCallback.this .table
1735: .getRowCount(); i++) {
1736: rect = rect
1737: .union(ColumnRepaintCallback.this .table
1738: .getCellRect(
1739: i,
1740: ColumnRepaintCallback.this .columnIndex,
1741: true));
1742: }
1743: if (!table.getShowHorizontalLines()
1744: && !table.getShowVerticalLines()) {
1745: float extra = SubstanceSizeUtils
1746: .getBorderStrokeWidth(SubstanceSizeUtils
1747: .getComponentFontSize(table
1748: .getTableHeader()));
1749: rect.x -= (int) extra;
1750: rect.width += 2 * (int) extra;
1751: }
1752: ColumnRepaintCallback.this .table.repaint(rect);
1753: }
1754: }
1755: });
1756: }
1757: }
1758:
1759: /**
1760: * ID of a single table cell.
1761: *
1762: * @author Kirill Grouchnikov
1763: */
1764: @SuppressWarnings("unchecked")
1765: protected static class TableCellId implements Comparable {
1766: /**
1767: * Cell row.
1768: */
1769: protected int row;
1770:
1771: /**
1772: * Cell column.
1773: */
1774: protected int column;
1775:
1776: /**
1777: * Indicates whether the comparison ({@link #equals(Object)}) should
1778: * return <code>false</code> when it is passed either
1779: * {@link TableColumnId} or {@link TableRowId}.
1780: */
1781: protected boolean isExactComparison;
1782:
1783: /**
1784: * Creates a new cell ID.
1785: *
1786: * @param row
1787: * Cell row.
1788: * @param column
1789: * Cell column.
1790: */
1791: public TableCellId(int row, int column) {
1792: this .row = row;
1793: this .column = column;
1794: }
1795:
1796: /**
1797: * Sets the comparison flag.
1798: *
1799: * @param isExactComparison
1800: * If <code>true</code>, the ({@link #equals(Object)})
1801: * will return <code>false</code> when it is passed either
1802: * {@link TableColumnId} or {@link TableRowId}.
1803: */
1804: public void setExactComparison(boolean isExactComparison) {
1805: this .isExactComparison = isExactComparison;
1806: }
1807:
1808: /*
1809: * (non-Javadoc)
1810: *
1811: * @see java.lang.Comparable#compareTo(java.lang.Object)
1812: */
1813: public int compareTo(Object o) {
1814: if (o instanceof TableCellId) {
1815: TableCellId otherId = (TableCellId) o;
1816: if ((this .row == otherId.row)
1817: && (this .column == otherId.column))
1818: return 0;
1819: return 1;
1820: }
1821: if (!this .isExactComparison) {
1822: if (o instanceof TableRowId) {
1823: TableRowId otherId = (TableRowId) o;
1824: if (this .row == otherId.row)
1825: return 0;
1826: return 1;
1827: }
1828: if (o instanceof TableColumnId) {
1829: TableColumnId otherId = (TableColumnId) o;
1830: if (this .column == otherId.column)
1831: return 0;
1832: return 1;
1833: }
1834: }
1835: return -1;
1836: }
1837:
1838: /*
1839: * (non-Javadoc)
1840: *
1841: * @see java.lang.Object#equals(java.lang.Object)
1842: */
1843: @Override
1844: public boolean equals(Object obj) {
1845: return this .compareTo(obj) == 0;
1846: }
1847:
1848: /*
1849: * (non-Javadoc)
1850: *
1851: * @see java.lang.Object#hashCode()
1852: */
1853: @Override
1854: public int hashCode() {
1855: return (this .row ^ (this .row >>> 32))
1856: & (this .column ^ (this .column >>> 32));
1857: }
1858:
1859: /*
1860: * (non-Javadoc)
1861: *
1862: * @see java.lang.Object#toString()
1863: */
1864: @Override
1865: public String toString() {
1866: return this .row + ":" + this .column;
1867: }
1868: }
1869:
1870: /**
1871: * ID of a single table column.
1872: *
1873: * @author Kirill Grouchnikov
1874: */
1875: @SuppressWarnings("unchecked")
1876: protected static class TableColumnId implements Comparable {
1877: /**
1878: * Column.
1879: */
1880: protected int column;
1881:
1882: /**
1883: * Creates a new column ID.
1884: *
1885: * @param column
1886: * Column.
1887: */
1888: public TableColumnId(int column) {
1889: this .column = column;
1890: }
1891:
1892: /*
1893: * (non-Javadoc)
1894: *
1895: * @see java.lang.Comparable#compareTo(java.lang.Object)
1896: */
1897: public int compareTo(Object o) {
1898: if (o instanceof TableCellId) {
1899: TableCellId otherId = (TableCellId) o;
1900: if (this .column == otherId.column)
1901: return 0;
1902: return 1;
1903: }
1904: if (o instanceof TableColumnId) {
1905: TableColumnId otherId = (TableColumnId) o;
1906: if (this .column == otherId.column)
1907: return 0;
1908: return 1;
1909: }
1910: return -1;
1911: }
1912:
1913: /*
1914: * (non-Javadoc)
1915: *
1916: * @see java.lang.Object#equals(java.lang.Object)
1917: */
1918: @Override
1919: public boolean equals(Object obj) {
1920: return this .compareTo(obj) == 0;
1921: }
1922:
1923: /*
1924: * (non-Javadoc)
1925: *
1926: * @see java.lang.Object#hashCode()
1927: */
1928: @Override
1929: public int hashCode() {
1930: return (this .column ^ (this .column >>> 32));
1931: }
1932: }
1933:
1934: /**
1935: * ID of a single table row.
1936: *
1937: * @author Kirill Grouchnikov
1938: */
1939: @SuppressWarnings("unchecked")
1940: protected static class TableRowId implements Comparable {
1941: /**
1942: * Row.
1943: */
1944: protected int row;
1945:
1946: /**
1947: * Creates a new row ID.
1948: *
1949: * @param row
1950: * Row.
1951: */
1952: public TableRowId(int row) {
1953: this .row = row;
1954: }
1955:
1956: /*
1957: * (non-Javadoc)
1958: *
1959: * @see java.lang.Comparable#compareTo(java.lang.Object)
1960: */
1961: public int compareTo(Object o) {
1962: if (o instanceof TableCellId) {
1963: TableCellId otherId = (TableCellId) o;
1964: if (this .row == otherId.row)
1965: return 0;
1966: return 1;
1967: }
1968: if (o instanceof TableRowId) {
1969: TableRowId otherId = (TableRowId) o;
1970: if (this .row == otherId.row)
1971: return 0;
1972: return 1;
1973: }
1974: return -1;
1975: }
1976:
1977: /*
1978: * (non-Javadoc)
1979: *
1980: * @see java.lang.Object#equals(java.lang.Object)
1981: */
1982: @Override
1983: public boolean equals(Object obj) {
1984: return this .compareTo(obj) == 0;
1985: }
1986:
1987: /*
1988: * (non-Javadoc)
1989: *
1990: * @see java.lang.Object#hashCode()
1991: */
1992: @Override
1993: public int hashCode() {
1994: return (this .row ^ (this .row >>> 32));
1995: }
1996: }
1997:
1998: /**
1999: * State listener for tracking the selection changes.
2000: *
2001: * @author Kirill Grouchnikov
2002: */
2003: protected class TableStateListener implements
2004: ListSelectionListener, TableModelListener {
2005: /*
2006: * (non-Javadoc)
2007: *
2008: * @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent)
2009: */
2010: @SuppressWarnings("unchecked")
2011: public void valueChanged(final ListSelectionEvent e) {
2012: SwingUtilities.invokeLater(new Runnable() {
2013: public void run() {
2014: syncSelection();
2015: }
2016: });
2017: }
2018:
2019: /*
2020: * (non-Javadoc)
2021: *
2022: * @see javax.swing.event.TableModelListener#tableChanged(javax.swing.event.TableModelEvent)
2023: */
2024: public void tableChanged(final TableModelEvent e) {
2025: // fix for defect 291 - tracking changes to the table.
2026: SwingUtilities.invokeLater(new Runnable() {
2027: public void run() {
2028: // fix for defect 328 - do not clear the
2029: // internal selection and focus tracking
2030: // when the event is table update.
2031: if (e.getType() != TableModelEvent.UPDATE) {
2032: selectedIndices.clear();
2033: prevStateMap.clear();
2034: nextStateMap.clear();
2035: focusedColumn = -1;
2036: focusedRow = -1;
2037: }
2038: syncSelection();
2039: table.repaint();
2040: }
2041: });
2042: }
2043: }
2044:
2045: /**
2046: * Listener for fade animations on table rollovers.
2047: *
2048: * @author Kirill Grouchnikov
2049: */
2050: private class RolloverFadeListener implements MouseListener,
2051: MouseMotionListener {
2052: public void mouseClicked(MouseEvent e) {
2053: }
2054:
2055: public void mouseEntered(MouseEvent e) {
2056: }
2057:
2058: public void mousePressed(MouseEvent e) {
2059: }
2060:
2061: public void mouseReleased(MouseEvent e) {
2062: }
2063:
2064: public void mouseExited(MouseEvent e) {
2065: // if (SubstanceCoreUtilities.toBleedWatermark(list))
2066: // return;
2067:
2068: if (!SubstanceTableUI.this .table.isEnabled())
2069: return;
2070: synchronized (SubstanceTableUI.this .table) {
2071: this .fadeOut();
2072: this .fadeOutTableHeader();
2073: // System.out.println("Nulling RO index");
2074: SubstanceTableUI.this .rolledOverId = null;
2075: SubstanceTableUI.this .rolledOverColumn = -1;
2076: }
2077: }
2078:
2079: public void mouseMoved(MouseEvent e) {
2080: // if (SubstanceCoreUtilities.toBleedWatermark(list))
2081: // return;
2082:
2083: if (!SubstanceTableUI.this .table.isEnabled())
2084: return;
2085: this .handleMove(e);
2086: this .handleMoveForHeader(e);
2087: }
2088:
2089: public void mouseDragged(MouseEvent e) {
2090: // if (SubstanceCoreUtilities.toBleedWatermark(list))
2091: // return;
2092:
2093: if (!SubstanceTableUI.this .table.isEnabled())
2094: return;
2095: this .handleMove(e);
2096: this .handleMoveForHeader(e);
2097: }
2098:
2099: /**
2100: * Handles various mouse move events and initiates the fade animation if
2101: * necessary.
2102: *
2103: * @param e
2104: * Mouse event.
2105: */
2106: private void handleMove(MouseEvent e) {
2107: synchronized (SubstanceTableUI.this .table) {
2108: int row = SubstanceTableUI.this .table.rowAtPoint(e
2109: .getPoint());
2110: int column = SubstanceTableUI.this .table
2111: .columnAtPoint(e.getPoint());
2112: if ((row < 0)
2113: || (row >= SubstanceTableUI.this .table
2114: .getRowCount())
2115: || (column < 0)
2116: || (column >= SubstanceTableUI.this .table
2117: .getColumnCount())) {
2118: this .fadeOut();
2119: // System.out.println("Nulling RO index");
2120: // table.putClientProperty(ROLLED_OVER_INDEX, null);
2121: SubstanceTableUI.this .rolledOverId = null;
2122: } else {
2123: // check if this is the same index
2124: Comparable<?> newId = SubstanceTableUI.this .getId(
2125: row, column);
2126: // Comparable currId = (Comparable) table
2127: // .getClientProperty(ROLLED_OVER_INDEX);
2128: if ((SubstanceTableUI.this .rolledOverId != null)
2129: && newId
2130: .equals(SubstanceTableUI.this .rolledOverId))
2131: return;
2132:
2133: this .fadeOut();
2134: FadeTrackerCallback callback = SubstanceTableUI.this
2135: .getCallback(row, column);
2136: if (SubstanceTableUI.this .hasRolloverAnimations()) {
2137: FadeTracker.getInstance().trackFadeIn(
2138: FadeKind.ROLLOVER,
2139: SubstanceTableUI.this .table, newId,
2140: false, callback);
2141: } else {
2142: callback.fadeEnded(FadeKind.ROLLOVER);
2143: }
2144: // System.out.println("Setting RO index to " + roIndex);
2145: if (FadeConfigurationManager.getInstance()
2146: .fadeAllowed(FadeKind.ROLLOVER,
2147: SubstanceTableUI.this .table)) {
2148: SubstanceTableUI.this .rolledOverId = newId;
2149: // table.putClientProperty(ROLLED_OVER_INDEX, newId);
2150: }
2151: }
2152: }
2153: }
2154:
2155: /**
2156: * Handles various mouse move events and initiates the fade animation if
2157: * necessary.
2158: *
2159: * @param e
2160: * Mouse event.
2161: */
2162: private void handleMoveForHeader(MouseEvent e) {
2163: if (!SubstanceTableUI.this .table
2164: .getColumnSelectionAllowed())
2165: return;
2166: JTableHeader header = SubstanceTableUI.this .table
2167: .getTableHeader();
2168: if ((header == null) || (!header.isVisible()))
2169: return;
2170:
2171: SubstanceTableHeaderUI ui = (SubstanceTableHeaderUI) header
2172: .getUI();
2173:
2174: synchronized (SubstanceTableUI.this .table) {
2175: int row = SubstanceTableUI.this .table.rowAtPoint(e
2176: .getPoint());
2177: int column = SubstanceTableUI.this .table
2178: .columnAtPoint(e.getPoint());
2179: if ((row < 0)
2180: || (row >= SubstanceTableUI.this .table
2181: .getRowCount())
2182: || (column < 0)
2183: || (column >= SubstanceTableUI.this .table
2184: .getColumnCount())) {
2185: this .fadeOutTableHeader();
2186: // System.out.println("Nulling RO column index");
2187: SubstanceTableUI.this .rolledOverColumn = -1;
2188: } else {
2189: // check if this is the same column
2190: if (SubstanceTableUI.this .rolledOverColumn == column)
2191: return;
2192:
2193: this .fadeOutTableHeader();
2194: FadeTracker.getInstance().trackFadeIn(
2195: FadeKind.ROLLOVER, header, column, false,
2196: ui.getCallback(column));
2197: // System.out.println("Setting RO column index to " +
2198: // column);
2199: if (FadeConfigurationManager.getInstance()
2200: .fadeAllowed(FadeKind.ROLLOVER,
2201: SubstanceTableUI.this .table)) {
2202: SubstanceTableUI.this .rolledOverColumn = column;
2203: }
2204: }
2205: }
2206: }
2207:
2208: /**
2209: * Initiates the fade out effect.
2210: */
2211: private void fadeOut() {
2212: if (SubstanceTableUI.this .rolledOverId != null) {
2213: FadeTrackerCallback callback = SubstanceTableUI.this
2214: .getCallback(SubstanceTableUI.this .rolledOverId);
2215: if (SubstanceTableUI.this .hasRolloverAnimations()) {
2216: FadeTracker.getInstance().trackFadeOut(
2217: FadeKind.ROLLOVER,
2218: SubstanceTableUI.this .table,
2219: SubstanceTableUI.this .rolledOverId, false,
2220: callback);
2221: } else {
2222: callback.fadeEnded(FadeKind.ROLLOVER);
2223: }
2224: }
2225: }
2226:
2227: /**
2228: * Initiates the fade out effect.
2229: */
2230: private void fadeOutTableHeader() {
2231: if (SubstanceTableUI.this .rolledOverColumn >= 0) {
2232: JTableHeader header = SubstanceTableUI.this .table
2233: .getTableHeader();
2234: if ((header == null) || (!header.isVisible()))
2235: return;
2236: SubstanceTableHeaderUI ui = (SubstanceTableHeaderUI) header
2237: .getUI();
2238: FadeTracker
2239: .getInstance()
2240: .trackFadeOut(
2241: FadeKind.ROLLOVER,
2242: header,
2243: SubstanceTableUI.this .rolledOverColumn,
2244: false,
2245: ui
2246: .getCallback(SubstanceTableUI.this .rolledOverColumn));
2247: }
2248: }
2249:
2250: }
2251:
2252: /**
2253: * Returns fade callback for a cell at the specified row and column.
2254: *
2255: * @param row
2256: * Row index.
2257: * @param column
2258: * Column index.
2259: * @return Fade callback for the specified cell.
2260: */
2261: private FadeTrackerCallback getCallback(int row, int column) {
2262: boolean hasRowSelection = this .table.getRowSelectionAllowed();
2263: boolean hasColumnSelection = this .table
2264: .getColumnSelectionAllowed();
2265:
2266: if (hasRowSelection && !hasColumnSelection)
2267: return new RowRepaintCallback(this .table, row);
2268: if (!hasRowSelection && hasColumnSelection)
2269: return new ColumnRepaintCallback(this .table, column);
2270: return new CellRepaintCallback(this .table, row, column);
2271: }
2272:
2273: /**
2274: * Returns fade callback for a cell, row or column specified by the
2275: * parameter, which should be one of {@link TableRowId},
2276: * {@link TableColumnId} or {@link TableCellId}.
2277: *
2278: * @param comparable
2279: * One of {@link TableRowId}, {@link TableColumnId} or
2280: * {@link TableCellId}.
2281: * @return Fade callback.
2282: */
2283: private FadeTrackerCallback getCallback(Comparable<?> comparable) {
2284: if (comparable instanceof TableRowId)
2285: return new RowRepaintCallback(this .table,
2286: ((TableRowId) comparable).row);
2287: if (comparable instanceof TableColumnId)
2288: return new ColumnRepaintCallback(this .table,
2289: ((TableColumnId) comparable).column);
2290: return new CellRepaintCallback(this .table,
2291: ((TableCellId) comparable).row,
2292: ((TableCellId) comparable).column);
2293: }
2294:
2295: /**
2296: * Returns a comparable ID for the specified location. The result will be
2297: * one of {@link TableRowId}, {@link TableColumnId} or {@link TableCellId},
2298: * based on the row and column selection modes of the table.
2299: *
2300: * @param row
2301: * Row index.
2302: * @param column
2303: * Column index.
2304: * @return Comparable ID for the specified location.
2305: */
2306: public Comparable<?> getId(int row, int column) {
2307: boolean hasRowSelection = this .table.getRowSelectionAllowed();
2308: boolean hasColumnSelection = this .table
2309: .getColumnSelectionAllowed();
2310:
2311: if (hasRowSelection && !hasColumnSelection)
2312: return new TableRowId(row);
2313: if (!hasRowSelection && hasColumnSelection)
2314: return new TableColumnId(column);
2315: return new TableCellId(row, column);
2316: }
2317:
2318: /**
2319: * Synchronizes the current selection state.
2320: *
2321: * @param e
2322: * Selection event.
2323: */
2324: // @SuppressWarnings("unchecked")
2325: protected void syncSelection(/* ListSelectionEvent e */) {
2326: if (this .table == null) {
2327: // fix for defect 270 - if the UI delegate is updated
2328: // by another selection listener, ignore this
2329: return;
2330: }
2331:
2332: int rows = this .table.getRowCount();
2333: int cols = this .table.getColumnCount();
2334:
2335: // fix for defect 209 - selection very slow on large tables with
2336: // column selection set to true and row selection set to false.
2337: // Solution - no selection animations on tables with more than 1000
2338: // cells.
2339: if (!this .hasSelectionAnimations()) {
2340: // this.selectedIndices.clear();
2341: // for (int i = 0; i < rows; i++) {
2342: // for (int j = 0; j < cols; j++) {
2343: // if (this.table.isCellSelected(i, j)) {
2344: // TableCellId cellId = new TableCellId(i, j);
2345: // this.selectedIndices.put(cellId, this.table.getModel()
2346: // .getValueAt(i, j));
2347: // }
2348: // }
2349: // }
2350: this .prevStateMap.clear();
2351: table.repaint();
2352: return;
2353: }
2354: // Map<TableCellId, Object> currSelected = (Map<TableCellId, Object>)
2355: // SubstanceTableUI.this.table
2356: // .getClientProperty(SubstanceTableUI.SELECTED_INDICES);
2357: // System.err.println("SyncSelection was " + currSelected.size());
2358: // if (e != null) {
2359: // System.err.println(e.getFirstIndex() + ":" + e.getLastIndex() + ":"
2360: // + e.getValueIsAdjusting());
2361: // }
2362:
2363: int rowLeadIndex = this .table.getSelectionModel()
2364: .getLeadSelectionIndex();
2365: int colLeadIndex = this .table.getColumnModel()
2366: .getSelectionModel().getLeadSelectionIndex();
2367: boolean isFocusOwner = this .table.isFocusOwner();
2368:
2369: Set<Long> initiatedFadeSequences = new HashSet<Long>();
2370: boolean fadeCanceled = false;
2371:
2372: FadeTracker fadeTrackerInstance = FadeTracker.getInstance();
2373: for (int i = 0; i < rows; i++) {
2374: for (int j = 0; j < cols; j++) {
2375: if (this .table.isCellSelected(i, j)) {
2376: TableCellId cellId = new TableCellId(i, j);
2377: // check if was selected before
2378: if (!this .selectedIndices.containsKey(cellId)) {
2379: // start fading in
2380: // System.err.println("Fade in on " + i + ":" + j);
2381: if (!fadeCanceled) {
2382: long fadeId = fadeTrackerInstance
2383: .trackFadeIn(FadeKind.SELECTION,
2384: this .table, cellId, false,
2385: new CellRepaintCallback(
2386: this .table, i, j));
2387: initiatedFadeSequences.add(fadeId);
2388: if (initiatedFadeSequences.size() > 15) {
2389: SubstanceFadeUtilities
2390: .cancelFades(initiatedFadeSequences);
2391: initiatedFadeSequences.clear();
2392: fadeCanceled = true;
2393: }
2394: } else {
2395: new CellRepaintCallback(this .table, i, j)
2396: .fadeEnded(FadeKind.SELECTION);
2397: }
2398:
2399: this .selectedIndices.put(cellId, this .table
2400: .getValueAt(i, j));
2401: // prevStateMap.put(cellId, ComponentState.SELECTED);
2402: }
2403: } else {
2404: TableCellId cellId = new TableCellId(i, j);
2405: // check if was selected before and still points
2406: // to the same element
2407: if (this .selectedIndices.containsKey(cellId)) {
2408: // corner case when the model returns null
2409: Object oldValue = this .selectedIndices
2410: .get(cellId);
2411: Object currValue = this .table.getValueAt(i, j);
2412: boolean isSame = false;
2413: if (oldValue == null) {
2414: isSame = (currValue == null);
2415: } else {
2416: // if (oldValue instanceof Comparable) {
2417: // try {
2418: // isSame = (((Comparable) oldValue)
2419: // .compareTo(currValue) == 0);
2420: // } catch (Throwable t) {
2421: // isSame = oldValue.toString().equals(
2422: // currValue.toString());
2423: // }
2424: // } else {
2425: isSame = oldValue.equals(currValue);
2426: // }
2427: }
2428: // if (!isSame) {
2429: // System.err.println(i + ":" + j + ":" + oldValue
2430: // + ":" + currValue);
2431: // }
2432: if (isSame) {
2433: // start fading out
2434: // System.err.println("Fade out on " + i + ":" + j);
2435: if (!fadeCanceled) {
2436: long fadeId = fadeTrackerInstance
2437: .trackFadeOut(
2438: FadeKind.SELECTION,
2439: this .table,
2440: cellId,
2441: false,
2442: new CellRepaintCallback(
2443: this .table, i,
2444: j));
2445: initiatedFadeSequences.add(fadeId);
2446: // System.err.println("Has "
2447: // + initiatedFadeSequences.size()
2448: // + " sqs [" + fadeId + "]");
2449: if (initiatedFadeSequences.size() > 15) {
2450: // System.err.println("Cancelling fades");
2451: SubstanceFadeUtilities
2452: .cancelFades(initiatedFadeSequences);
2453: initiatedFadeSequences.clear();
2454: fadeCanceled = true;
2455: }
2456: } else {
2457: new CellRepaintCallback(this .table, i,
2458: j)
2459: .fadeEnded(FadeKind.SELECTION);
2460: }
2461: }
2462: this .selectedIndices.remove(cellId);
2463: }
2464: // ComponentState state = getCellState(cellId);
2465: // if (state == ComponentState.DEFAULT) {
2466: // prevStateMap.remove(cellId);
2467: // } else {
2468: // prevStateMap.put(cellId, getCellState(cellId));
2469: // System.out.println(cellId.row + ":" + cellId.column
2470: // + "->" + state.name());
2471: // }
2472: }
2473:
2474: // handle focus animations
2475: boolean cellHasFocus = isFocusOwner
2476: && (i == rowLeadIndex) && (j == colLeadIndex);
2477: if (cellHasFocus) {
2478: // check is it's a different cell
2479: if ((this .focusedRow != i)
2480: || (this .focusedColumn != j)) {
2481: if ((this .focusedRow >= 0)
2482: && (this .focusedColumn >= 0)) {
2483: // fade out the previous focus holder
2484: TableCellId prevFocusedId = new TableCellId(
2485: this .focusedRow, this .focusedColumn);
2486: // set indication to make exact comparison (since
2487: // focus can be only on one cell).
2488: prevFocusedId.setExactComparison(true);
2489: FadeTrackerCallback callback = this
2490: .getCallback(prevFocusedId);
2491: // System.out.println("Fading out " +
2492: // prevFocusedId);
2493: FadeTracker.getInstance().trackFadeOut(
2494: FadeKind.FOCUS, this .table,
2495: prevFocusedId, false, callback);
2496: }
2497:
2498: FadeTrackerCallback callback = this
2499: .getCallback(i, j);
2500: // fade in the current cell (new focus holder)
2501: // System.out.println("Fading in " + cellId);
2502: TableCellId currId = new TableCellId(i, j);
2503: // set indication to make exact comparison (since
2504: // focus can be only on one cell).
2505: currId.setExactComparison(true);
2506: FadeTracker.getInstance().trackFadeIn(
2507: FadeKind.FOCUS, this .table, currId,
2508: false, callback);
2509: // System.out.println("Setting focus index to " + i +
2510: // ":"
2511: // + j);
2512: if (FadeConfigurationManager
2513: .getInstance()
2514: .fadeAllowed(FadeKind.FOCUS, this .table)) {
2515: // and store it for future checks
2516: this .focusedRow = i;
2517: this .focusedColumn = j;
2518: }
2519: }
2520: } else {
2521: // check if previously it held focus
2522: if ((this .focusedRow == i)
2523: && (this .focusedColumn == j)) {
2524: // fade it out
2525: TableCellId prevFocusedId = new TableCellId(
2526: this .focusedRow, this .focusedColumn);
2527: // set indication to make exact comparison (since
2528: // focus can be only on one cell).
2529: prevFocusedId.setExactComparison(true);
2530: FadeTrackerCallback callback = SubstanceTableUI.this
2531: .getCallback(prevFocusedId);
2532: // System.out.println("Fading out " + prevFocusedId);
2533: FadeTracker.getInstance().trackFadeOut(
2534: FadeKind.FOCUS,
2535: SubstanceTableUI.this .table,
2536: prevFocusedId, false, callback);
2537: this .focusedRow = -1;
2538: this .focusedColumn = -1;
2539: }
2540: }
2541: }
2542: }
2543: // System.err.println("Has " + currSelected.size() + " selected cells");
2544: // for (TableCellId cellId : currSelected.keySet()) {
2545: // System.err.println("\t" + cellId.row + ":" + cellId.column);
2546: // }
2547: }
2548:
2549: /**
2550: * Returns the previous state for the specified cell.
2551: *
2552: * @param cellIndex
2553: * Cell index.
2554: * @return The previous state for the specified cell.
2555: */
2556: public ComponentState getPrevCellState(TableCellId cellIndex) {
2557: if (this .prevStateMap.containsKey(cellIndex))
2558: return this .prevStateMap.get(cellIndex);
2559: return ComponentState.DEFAULT;
2560: }
2561:
2562: /**
2563: * Returns the current state for the specified cell.
2564: *
2565: * @param cellIndex
2566: * Cell index.
2567: * @return The current state for the specified cell.
2568: */
2569: public ComponentState getCellState(TableCellId cellIndex) {
2570: ButtonModel synthModel = new DefaultButtonModel();
2571: int row = cellIndex.row;
2572: int column = cellIndex.column;
2573: synthModel.setEnabled(this .table.isEnabled());
2574: // Integer currRoIndex = (Integer) list
2575: // .getClientProperty(ROLLED_OVER_INDEX);
2576: Comparable<?> cellId = this .getId(row, column);
2577: // synthModel
2578: // .setRollover(((this.rolledOverId != null) && this.rolledOverId
2579: // .equals(cellIndex)));
2580: synthModel.setRollover(cellId.equals(this .rolledOverId));
2581: if (this .hasSelectionAnimations()
2582: && FadeConfigurationManager.getInstance().fadeAllowed(
2583: FadeKind.SELECTION, table))
2584: synthModel.setSelected(this .selectedIndices
2585: .containsKey(cellId));
2586: else {
2587: // System.out.println(row + ":" + column + "-->"
2588: // + this.table.isCellSelected(row, column));
2589: synthModel.setSelected(this .table.isCellSelected(row,
2590: column));
2591: }
2592: // System.out.println(row + ":" + column + "-->"
2593: // + ComponentState.getState(synthModel, null));
2594: return ComponentState.getState(synthModel, null);
2595: }
2596:
2597: /**
2598: * Checks whether the table has animations.
2599: *
2600: * @return <code>true</code> if the table has animations,
2601: * <code>false</code> otherwise.
2602: */
2603: protected boolean hasAnimations() {
2604: // fix for defects 164 and 209 - selection
2605: // and deletion are very slow on large tables.
2606: int rowCount = this .table.getRowCount();
2607: int colCount = this .table.getColumnCount();
2608: if (rowCount * colCount >= 500)
2609: return false;
2610: if (this .table.getColumnSelectionAllowed()
2611: && !this .table.getRowSelectionAllowed()) {
2612: if (!this .table.getShowHorizontalLines()
2613: && !this .table.getShowVerticalLines())
2614: return rowCount <= 8;
2615: return rowCount <= 25;
2616: }
2617: if (!this .table.getColumnSelectionAllowed()
2618: && this .table.getRowSelectionAllowed()) {
2619: if (!this .table.getShowHorizontalLines()
2620: && !this .table.getShowVerticalLines())
2621: return colCount <= 8;
2622: return colCount <= 25;
2623: }
2624: return true;
2625: }
2626:
2627: /**
2628: * Checks whether the table has selection animations.
2629: *
2630: * @return <code>true</code> if the table has selection animations,
2631: * <code>false</code> otherwise.
2632: */
2633: public boolean hasSelectionAnimations() {
2634: return this .hasAnimations()
2635: && !LafWidgetUtilities.hasNoFades(this .table,
2636: FadeKind.SELECTION);
2637: }
2638:
2639: /**
2640: * Checks whether the table has rollover animations.
2641: *
2642: * @return <code>true</code> if the table has rollover animations,
2643: * <code>false</code> otherwise.
2644: */
2645: public boolean hasRolloverAnimations() {
2646: return this .hasAnimations()
2647: && !LafWidgetUtilities.hasNoFades(this .table,
2648: FadeKind.ROLLOVER);
2649: }
2650:
2651: /**
2652: * Returns the index of the rollover column.
2653: *
2654: * @return The index of the rollover column.
2655: */
2656: public int getRolloverColumnIndex() {
2657: return this .rolledOverColumn;
2658: }
2659:
2660: /**
2661: * Returns indication whether the specified cell has focus.
2662: *
2663: * @param row
2664: * Cell row index.
2665: * @param column
2666: * Cell column index.
2667: * @return <code>true</code> If the focus is on the specified cell,
2668: * <code>false</code> otherwise.
2669: */
2670: public boolean isFocusedCell(int row, int column) {
2671: return (this .focusedRow == row)
2672: && (this .focusedColumn == column);
2673: }
2674:
2675: /*
2676: * (non-Javadoc)
2677: *
2678: * @see javax.swing.plaf.ComponentUI#update(java.awt.Graphics,
2679: * javax.swing.JComponent)
2680: */
2681: @Override
2682: public void update(Graphics g, JComponent c) {
2683: SubstanceFillBackgroundDelegate.GLOBAL_INSTANCE.updateIfOpaque(
2684: g, c);
2685:
2686: Graphics2D g2d = (Graphics2D) g.create();
2687: // install state-aware alpha channel (support for skins that use
2688: // translucency on disabled states).
2689: // SubstanceTheme theme = SubstanceCoreUtilities.getTheme(this.table,
2690: // true,
2691: // true);
2692: // float themeAlpha = theme.getThemeAlpha(this.table,
2693: // this.table.isEnabled() ? ComponentState.DEFAULT
2694: // : ComponentState.DISABLED_UNSELECTED);
2695: // g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
2696: // themeAlpha));
2697: this.paint(g2d, c);
2698: g2d.dispose();
2699: }
2700: }
|