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.beans.PropertyChangeEvent;
0034: import java.beans.PropertyChangeListener;
0035: import java.util.*;
0036:
0037: import javax.swing.*;
0038: import javax.swing.event.ListSelectionEvent;
0039: import javax.swing.event.ListSelectionListener;
0040: import javax.swing.plaf.*;
0041: import javax.swing.plaf.basic.BasicTableHeaderUI;
0042: import javax.swing.table.*;
0043:
0044: import org.jvnet.lafwidget.animation.*;
0045: import org.jvnet.lafwidget.layout.TransitionLayout;
0046: import org.jvnet.substance.painter.highlight.SubstanceHighlightUtils;
0047: import org.jvnet.substance.painter.text.SubstanceTextPainter;
0048: import org.jvnet.substance.theme.SubstanceTheme;
0049: import org.jvnet.substance.utils.*;
0050:
0051: /**
0052: * UI for table headers in <b>Substance</b> look and feel.
0053: *
0054: * @author Kirill Grouchnikov
0055: */
0056: public class SubstanceTableHeaderUI extends BasicTableHeaderUI {
0057: /**
0058: * Repaints the header on column selection.
0059: */
0060: protected TableHeaderListener substanceHeaderListener;
0061:
0062: /**
0063: * The default renderer.
0064: */
0065: protected TableCellRenderer defaultHeaderRenderer;
0066:
0067: /**
0068: * Holds the list of currently selected indices.
0069: */
0070: protected Map<Integer, Object> selectedIndices;
0071:
0072: /**
0073: * Listener for fade animations on list selections.
0074: */
0075: protected ListSelectionListener substanceFadeSelectionListener;
0076:
0077: /**
0078: * Map of previous fade states (for state-aware theme transitions).
0079: */
0080: private Map<Integer, ComponentState> prevStateMap;
0081:
0082: /**
0083: * Map of next fade states (for state-aware theme transitions).
0084: */
0085: private Map<Integer, ComponentState> nextStateMap;
0086:
0087: protected PropertyChangeListener substancePropertyChangeListener;
0088:
0089: /**
0090: * Listener for table header.
0091: *
0092: * @author Kirill Grouchnikov
0093: */
0094: private static class TableHeaderListener implements
0095: ListSelectionListener {
0096: /**
0097: * The associated table header UI.
0098: */
0099: private SubstanceTableHeaderUI ui;
0100:
0101: /**
0102: * Simple constructor.
0103: *
0104: * @param ui
0105: * The associated table header UI
0106: */
0107: public TableHeaderListener(SubstanceTableHeaderUI ui) {
0108: this .ui = ui;
0109: }
0110:
0111: /*
0112: * (non-Javadoc)
0113: *
0114: * @see javax.swing.event.ListSelectionListener#valueChanged(javax.swing.event.ListSelectionEvent)
0115: */
0116: public void valueChanged(ListSelectionEvent e) {
0117: if (ui.header == null)
0118: return;
0119: if (ui.header.isValid())
0120: ui.header.repaint();
0121: }
0122: }
0123:
0124: /*
0125: * (non-Javadoc)
0126: *
0127: * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
0128: */
0129: public static ComponentUI createUI(JComponent h) {
0130: SubstanceTableHeaderUI result = new SubstanceTableHeaderUI();
0131: return result;
0132: }
0133:
0134: /**
0135: * Creates a new UI delegate.
0136: */
0137: public SubstanceTableHeaderUI() {
0138: prevStateMap = new HashMap<Integer, ComponentState>();
0139: nextStateMap = new HashMap<Integer, ComponentState>();
0140: selectedIndices = new HashMap<Integer, Object>();
0141: }
0142:
0143: /*
0144: * (non-Javadoc)
0145: *
0146: * @see javax.swing.plaf.basic.BasicTableHeaderUI#installListeners()
0147: */
0148: @Override
0149: protected void installListeners() {
0150: super .installListeners();
0151: TableColumnModel columnModel = header.getColumnModel();
0152: if (columnModel != null) {
0153: ListSelectionModel lsm = columnModel.getSelectionModel();
0154: if (lsm != null) {
0155: // fix for defect 109 - memory leak on theme switch
0156: substanceHeaderListener = new TableHeaderListener(this );
0157: lsm.addListSelectionListener(substanceHeaderListener);
0158: }
0159: }
0160:
0161: // Add listener for the selection animation
0162: this .substanceFadeSelectionListener = new ListSelectionListener() {
0163: protected void cancelFades(Set<Long> initiatedFadeSequences) {
0164: FadeTracker fadeTrackerInstance = FadeTracker
0165: .getInstance();
0166: for (long fadeId : initiatedFadeSequences) {
0167: fadeTrackerInstance.cancelFadeInstance(fadeId);
0168: }
0169: }
0170:
0171: @SuppressWarnings("unchecked")
0172: public void valueChanged(ListSelectionEvent e) {
0173: if (header == null)
0174: return;
0175:
0176: // optimization on large tables and large selections
0177: // and syncing the fade presence with the table
0178: // (issue 309)
0179: SubstanceTableUI tableUI = (SubstanceTableUI) header
0180: .getTable().getUI();
0181: // System.out.println("Sel anim: "
0182: // + tableUI.hasSelectionAnimations());
0183: if (!tableUI.hasSelectionAnimations())
0184: return;
0185:
0186: Set<Long> initiatedFadeSequences = new HashSet<Long>();
0187: boolean fadeCanceled = false;
0188:
0189: // if (SubstanceCoreUtilities.toBleedWatermark(list))
0190: // return;
0191:
0192: FadeTracker fadeTrackerInstance = FadeTracker
0193: .getInstance();
0194: TableColumnModel columnModel = header.getColumnModel();
0195: int size = columnModel.getColumnCount();
0196: ListSelectionModel lsm = columnModel
0197: .getSelectionModel();
0198: for (int i = e.getFirstIndex(); i <= e.getLastIndex(); i++) {
0199: if (i >= size)
0200: continue;
0201: if (lsm.isSelectedIndex(i)) {
0202: // check if was selected before
0203: if (!selectedIndices.containsKey(i)) {
0204: // start fading in
0205: // System.out.println("Fade in on index " + i);
0206:
0207: if (!fadeCanceled) {
0208: long fadeId = fadeTrackerInstance
0209: .trackFadeIn(
0210: FadeKind.SELECTION,
0211: header,
0212: i,
0213: false,
0214: new ColumnHeaderRepaintCallback(
0215: header, i));
0216: initiatedFadeSequences.add(fadeId);
0217: if (initiatedFadeSequences.size() > 25) {
0218: cancelFades(initiatedFadeSequences);
0219: initiatedFadeSequences.clear();
0220: fadeCanceled = true;
0221: }
0222: }
0223:
0224: selectedIndices.put(i, columnModel
0225: .getColumn(i));
0226: }
0227: } else {
0228: // check if was selected before and still points to the
0229: // same element
0230: if (selectedIndices.containsKey(i)) {
0231: if (selectedIndices.get(i) == columnModel
0232: .getColumn(i)) {
0233: // start fading out
0234: // System.out.println("Fade out on index " + i);
0235:
0236: if (!fadeCanceled) {
0237: long fadeId = fadeTrackerInstance
0238: .trackFadeOut(
0239: FadeKind.SELECTION,
0240: header,
0241: i,
0242: false,
0243: new ColumnHeaderRepaintCallback(
0244: header, i));
0245: initiatedFadeSequences.add(fadeId);
0246: if (initiatedFadeSequences.size() > 25) {
0247: cancelFades(initiatedFadeSequences);
0248: initiatedFadeSequences.clear();
0249: fadeCanceled = true;
0250: }
0251: }
0252: }
0253: selectedIndices.remove(i);
0254: }
0255: }
0256: }
0257: }
0258: };
0259:
0260: if (columnModel != null) {
0261: ListSelectionModel lsm = columnModel.getSelectionModel();
0262: if (lsm != null) {
0263: lsm
0264: .addListSelectionListener(substanceFadeSelectionListener);
0265: }
0266: }
0267:
0268: this .substancePropertyChangeListener = new PropertyChangeListener() {
0269: public void propertyChange(PropertyChangeEvent evt) {
0270: if ("table".equals(evt.getPropertyName())) {
0271: // track changes to the table and re-register the
0272: // column model listener to the new table.
0273: TableColumnModel oldModel = (evt.getOldValue() instanceof JTable) ? ((JTable) evt
0274: .getOldValue()).getColumnModel()
0275: : null;
0276: TableColumnModel newModel = (evt.getNewValue() instanceof JTable) ? ((JTable) evt
0277: .getNewValue()).getColumnModel()
0278: : null;
0279: processColumnModelChangeEvent(oldModel, newModel);
0280: }
0281: }
0282: };
0283: this .header
0284: .addPropertyChangeListener(this .substancePropertyChangeListener);
0285: }
0286:
0287: /*
0288: * (non-Javadoc)
0289: *
0290: * @see javax.swing.plaf.basic.BasicTableHeaderUI#installDefaults()
0291: */
0292: @Override
0293: protected void installDefaults() {
0294: super .installDefaults();
0295:
0296: defaultHeaderRenderer = header.getDefaultRenderer();
0297: if (defaultHeaderRenderer instanceof UIResource) {
0298: header
0299: .setDefaultRenderer(new SubstanceDefaultTableHeaderCellRenderer());
0300: }
0301:
0302: for (int i = 0; i < header.getColumnModel().getColumnCount(); i++) {
0303: if (header.getColumnModel().getSelectionModel()
0304: .isSelectedIndex(i)) {
0305: selectedIndices.put(i, header.getColumnModel()
0306: .getColumn(i));
0307: prevStateMap.put(i, ComponentState.SELECTED);
0308: }
0309: }
0310: }
0311:
0312: /*
0313: * (non-Javadoc)
0314: *
0315: * @see javax.swing.plaf.basic.BasicTableHeaderUI#uninstallListeners()
0316: */
0317: @Override
0318: protected void uninstallListeners() {
0319: // fix for defect 109 - memory leak on theme switch
0320: TableColumnModel columnModel = header.getColumnModel();
0321: if (columnModel != null) {
0322: ListSelectionModel lsm = columnModel.getSelectionModel();
0323: if (lsm != null) {
0324: lsm
0325: .removeListSelectionListener(substanceHeaderListener);
0326: substanceHeaderListener = null;
0327: }
0328: }
0329:
0330: this .header
0331: .removePropertyChangeListener(this .substancePropertyChangeListener);
0332: this .substancePropertyChangeListener = null;
0333:
0334: super .uninstallListeners();
0335: }
0336:
0337: /*
0338: * (non-Javadoc)
0339: *
0340: * @see javax.swing.plaf.basic.BasicTableHeaderUI#uninstallDefaults()
0341: */
0342: @Override
0343: protected void uninstallDefaults() {
0344: super .uninstallDefaults();
0345:
0346: selectedIndices.clear();
0347:
0348: if (header.getDefaultRenderer() instanceof SubstanceDefaultTableHeaderCellRenderer) {
0349: header.setDefaultRenderer(defaultHeaderRenderer);
0350: if (defaultHeaderRenderer instanceof Component)
0351: SwingUtilities
0352: .updateComponentTreeUI((Component) defaultHeaderRenderer);
0353: }
0354: }
0355:
0356: /*
0357: * (non-Javadoc)
0358: *
0359: * @see javax.swing.plaf.ComponentUI#paint(java.awt.Graphics,
0360: * javax.swing.JComponent)
0361: */
0362: @Override
0363: public void paint(Graphics g, JComponent c) {
0364: if (header.getColumnModel().getColumnCount() <= 0) {
0365: return;
0366: }
0367: boolean ltr = header.getComponentOrientation().isLeftToRight();
0368:
0369: Rectangle clip = g.getClipBounds();
0370: Point left = clip.getLocation();
0371: Point right = new Point(clip.x + clip.width - 1, clip.y);
0372:
0373: TableColumnModel cm = header.getColumnModel();
0374: int[] selectedColumns = cm.getSelectedColumns();
0375: Set<Integer> selected = new HashSet<Integer>();
0376: for (int sel : selectedColumns)
0377: selected.add(sel);
0378:
0379: int cMin = header.columnAtPoint(ltr ? left : right);
0380: int cMax = header.columnAtPoint(ltr ? right : left);
0381: // This should never happen.
0382: if (cMin == -1) {
0383: cMin = 0;
0384: }
0385: // If the table does not have enough columns to fill the view we'll get
0386: // -1.
0387: // Replace this with the index of the last column.
0388: if (cMax == -1) {
0389: cMax = cm.getColumnCount() - 1;
0390: }
0391:
0392: TableColumn draggedColumn = header.getDraggedColumn();
0393: int columnWidth;
0394: Rectangle cellRect = header.getHeaderRect(ltr ? cMin : cMax);
0395: TableColumn aColumn;
0396: if (ltr) {
0397: for (int column = cMin; column <= cMax; column++) {
0398: aColumn = cm.getColumn(column);
0399: columnWidth = aColumn.getWidth();
0400: cellRect.width = columnWidth;
0401: if (aColumn != draggedColumn) {
0402: this .paintCell(g, cellRect, column, selected
0403: .contains(column));
0404: }
0405: cellRect.x += columnWidth;
0406: }
0407: } else {
0408: for (int column = cMax; column >= cMin; column--) {
0409: aColumn = cm.getColumn(column);
0410: columnWidth = aColumn.getWidth();
0411: cellRect.width = columnWidth;
0412: if (aColumn != draggedColumn) {
0413: this .paintCell(g, cellRect, column, selected
0414: .contains(column));
0415: }
0416: cellRect.x += columnWidth;
0417: }
0418: }
0419:
0420: this .paintGrid(g, c);
0421:
0422: // Paint the dragged column if we are dragging.
0423: if (draggedColumn != null) {
0424: int draggedColumnIndex = viewIndexForColumn(draggedColumn);
0425: Rectangle draggedCellRect = header
0426: .getHeaderRect(draggedColumnIndex);
0427:
0428: // Draw a gray well in place of the moving column.
0429: g.setColor(header.getParent().getBackground());
0430: g.fillRect(draggedCellRect.x, draggedCellRect.y,
0431: draggedCellRect.width, draggedCellRect.height);
0432:
0433: draggedCellRect.x += header.getDraggedDistance();
0434:
0435: // Fill the background.
0436: g.setColor(header.getBackground());
0437: g.fillRect(draggedCellRect.x, draggedCellRect.y,
0438: draggedCellRect.width, draggedCellRect.height);
0439:
0440: this .paintCell(g, draggedCellRect, draggedColumnIndex,
0441: selected.contains(draggedColumnIndex));
0442: }
0443:
0444: // Remove all components in the rendererPane.
0445: rendererPane.removeAll();
0446: }
0447:
0448: /**
0449: * Retrieves renderer for the specified column header.
0450: *
0451: * @param columnIndex
0452: * Column index.
0453: * @return Renderer for the specified column header.
0454: */
0455: private Component getHeaderRenderer(int columnIndex) {
0456: TableColumn aColumn = header.getColumnModel().getColumn(
0457: columnIndex);
0458: TableCellRenderer renderer = aColumn.getHeaderRenderer();
0459: if (renderer == null) {
0460: renderer = header.getDefaultRenderer();
0461: }
0462: return renderer.getTableCellRendererComponent(
0463: header.getTable(), aColumn.getHeaderValue(), false,
0464: false, -1, columnIndex);
0465: }
0466:
0467: public void paintGrid(Graphics g, JComponent c) {
0468: boolean ltr = header.getComponentOrientation().isLeftToRight();
0469:
0470: Graphics2D g2d = (Graphics2D) g.create();
0471:
0472: Rectangle clip = g.getClipBounds();
0473: Point left = clip.getLocation();
0474: Point right = new Point(clip.x + clip.width - 1, clip.y);
0475:
0476: TableColumnModel cm = header.getColumnModel();
0477:
0478: int cMin = header.columnAtPoint(ltr ? left : right);
0479: int cMax = header.columnAtPoint(ltr ? right : left);
0480: // This should never happen.
0481: if (cMin == -1) {
0482: cMin = 0;
0483: }
0484:
0485: Rectangle cellRect0 = header.getHeaderRect(cMin);
0486: int top = cellRect0.y;
0487: int bottom = cellRect0.y + cellRect0.height;
0488:
0489: Color gridColor = getGridColor(this .header);
0490:
0491: float strokeWidth = SubstanceSizeUtils
0492: .getBorderStrokeWidth(SubstanceSizeUtils
0493: .getComponentFontSize(this .header));
0494: g2d.setStroke(new BasicStroke(strokeWidth,
0495: BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
0496: g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0497: RenderingHints.VALUE_ANTIALIAS_ON);
0498: g2d.setColor(gridColor);
0499: g2d.setComposite(TransitionLayout.getAlphaComposite(
0500: this .header, 0.7f, g));
0501:
0502: g2d.drawLine((int) left.getX(),
0503: (int) (bottom - strokeWidth / 2), (int) right.getX(),
0504: (int) (bottom - strokeWidth / 2));
0505: // g2d.drawLine((int) left.getX(), top + (int) (strokeWidth / 2),
0506: // (int) right.getX(), top + (int) (strokeWidth / 2));
0507:
0508: // If the table does not have enough columns to fill the view we'll
0509: // get
0510: // -1.
0511: // Replace this with the index of the last column.
0512: if (cMax == -1) {
0513: cMax = cm.getColumnCount() - 1;
0514: }
0515:
0516: TableColumn draggedColumn = header.getDraggedColumn();
0517: int columnWidth;
0518: Rectangle cellRect = header.getHeaderRect(ltr ? cMin : cMax);
0519: TableColumn aColumn;
0520: if (ltr) {
0521: for (int column = cMin; column <= cMax; column++) {
0522: aColumn = cm.getColumn(column);
0523: columnWidth = aColumn.getWidth();
0524: cellRect.width = columnWidth;
0525: if (aColumn != draggedColumn) {
0526: g2d.drawLine(cellRect.x - 1, cellRect.y,
0527: cellRect.x - 1, bottom);
0528: // if (column == cMax) {
0529: // g2d.drawLine(cellRect.x + cellRect.width - 1,
0530: // cellRect.y, cellRect.x + cellRect.width - 1,
0531: // bottom);
0532: // }
0533: }
0534: cellRect.x += columnWidth;
0535: }
0536: } else {
0537: for (int column = cMax; column >= cMin; column--) {
0538: aColumn = cm.getColumn(column);
0539: columnWidth = aColumn.getWidth();
0540: cellRect.width = columnWidth;
0541: if (aColumn != draggedColumn) {
0542: g2d.drawLine(cellRect.x - 1, cellRect.y,
0543: cellRect.x - 1, bottom);
0544: // if (column == cMin) {
0545: // g2d.drawLine(cellRect.x + cellRect.width - 1,
0546: // cellRect.y, cellRect.x + cellRect.width - 1,
0547: // bottom);
0548: // }
0549: }
0550: cellRect.x += columnWidth;
0551: }
0552: }
0553:
0554: g2d.dispose();
0555: }
0556:
0557: protected static Color getGridColor(JTableHeader header) {
0558: ComponentState currState = (header.isEnabled() && header
0559: .getTable().isEnabled()) ? ComponentState.DEFAULT
0560: : ComponentState.DISABLED_UNSELECTED;
0561: SubstanceTheme currTheme = SubstanceThemeUtilities
0562: .getHighlightTheme(header, currState);
0563: Color gridColor = currTheme.getBorderTheme().getColorScheme()
0564: .getDarkColor();
0565:
0566: // // support for enhancement 256 - colorization of controls.
0567: // boolean hasColorization = SubstanceCoreUtilities
0568: // .hasColorization(header);
0569: // if (hasColorization) {
0570: // Color backgr = header.getBackground();
0571: // if (!(backgr instanceof UIResource)) {
0572: // double colorizationFactor = SubstanceCoreUtilities
0573: // .getColorizationFactor(header);
0574: // if (!header.isEnabled() || !header.getTable().isEnabled())
0575: // colorizationFactor /= 2.0;
0576: // Color toShiftTo = SubstanceColorUtilities.deriveByBrightness(
0577: // backgr, gridColor);
0578: // gridColor = SubstanceColorUtilities.getInterpolatedColor(
0579: // toShiftTo, gridColor, colorizationFactor);
0580: // }
0581: // }
0582: return gridColor;
0583: }
0584:
0585: /**
0586: * Paints cell.
0587: *
0588: * @param g
0589: * Graphic context.
0590: * @param cellRect
0591: * Cell rectangle.
0592: * @param columnIndex
0593: * Column index.
0594: * @param isSelected
0595: * Selection indication.
0596: */
0597: private void paintCell(Graphics g, Rectangle cellRect,
0598: int columnIndex, boolean isSelected) {
0599: Graphics2D g2d = (Graphics2D) g.create();
0600: g2d.setComposite(TransitionLayout.getAlphaComposite(header, g));
0601:
0602: // paint default background
0603: final Component component = getHeaderRenderer(columnIndex);
0604: SubstanceTextPainter textPainter = SubstanceLookAndFeel
0605: .getCurrentTextPainter();
0606: final ComponentState prevState = getPrevColumnState(columnIndex);
0607: final ComponentState currState = getColumnState(columnIndex);
0608:
0609: final ComponentState backgroundState = (this .header.isEnabled() && this .header
0610: .getTable().isEnabled()) ? ComponentState.DEFAULT
0611: : ComponentState.DISABLED_UNSELECTED;
0612: final SubstanceTheme theme = SubstanceThemeUtilities.getTheme(
0613: component, backgroundState);
0614: if (textPainter.needsBackgroundImage()) {
0615: final Rectangle offsetCellRect = new Rectangle(0, 0,
0616: cellRect.width, cellRect.height);
0617: textPainter.init(header, offsetCellRect, true);
0618: textPainter
0619: .attachCallback(new SubstanceTextPainter.BackgroundPaintingCallback() {
0620: public void paintBackground(Graphics g) {
0621: Graphics2D g2d = (Graphics2D) g.create();
0622: SubstanceHighlightUtils.paintHighlight(g2d,
0623: component, offsetCellRect, 0.0f,
0624: null, theme.getColorScheme(), theme
0625: .getColorScheme(), 0.0f);
0626: // SubstanceHighlightUtils.paintHighlight(g2d,
0627: // component, backgroundState,
0628: // backgroundState, offsetCellRect, 0.0f);
0629: g2d.dispose();
0630: }
0631: });
0632: } else {
0633: SubstanceHighlightUtils.paintHighlight(g2d, component,
0634: cellRect, 0.0f, null, theme.getColorScheme(), theme
0635: .getColorScheme(), 0.0f);
0636: // highlightDelegate.paintClassicHighlight(g, component, cellRect,
0637: // SubstanceCoreUtilities.getTheme(header, backgroundState,
0638: // true, true, true), 0.0f);
0639: }
0640:
0641: // float alphaForPrevBackground = 0.0f;
0642:
0643: // Compute the alpha values for the animation.
0644: float startAlpha = SubstanceThemeUtilities.getHighlightAlpha(
0645: header, prevState);
0646: float endAlpha = SubstanceThemeUtilities.getHighlightAlpha(
0647: header, currState);
0648: // float alphaForCurrBackground = endAlpha;
0649:
0650: FadeState state = SubstanceFadeUtilities.getFadeState(header,
0651: columnIndex, FadeKind.SELECTION, FadeKind.ROLLOVER);
0652: float totalAlpha = endAlpha;
0653: float fadeCoef = 0.0f;
0654: if (state != null) {
0655: fadeCoef = state.getFadePosition();
0656:
0657: // compute the total alpha of the overlays.
0658: if (state.isFadingIn()) {
0659: totalAlpha = startAlpha + (endAlpha - startAlpha)
0660: * fadeCoef / 10.0f;
0661: } else {
0662: totalAlpha = startAlpha + (endAlpha - startAlpha)
0663: * (10.0f - fadeCoef) / 10.0f;
0664: }
0665:
0666: if (state.isFadingIn())
0667: fadeCoef = 10.0f - fadeCoef;
0668:
0669: // compute the alpha for each one of the animation overlays
0670: // alphaForPrevBackground = totalAlpha * fadeCoef / 10.0f;
0671: // alphaForCurrBackground = totalAlpha * (10.0f - fadeCoef) / 10.0f;
0672: }
0673:
0674: final SubstanceTheme prevTheme = SubstanceThemeUtilities
0675: .getHighlightTheme(header, prevState);
0676: final SubstanceTheme currTheme = SubstanceThemeUtilities
0677: .getHighlightTheme(header, currState);
0678:
0679: // System.out.println(row + ":" + prevTheme.getDisplayName() + "["
0680: // + alphaForPrevBackground + "]:" + currTheme.getDisplayName()
0681: // + "[" + alphaForCurrBackground + "]");
0682:
0683: if (textPainter.needsBackgroundImage()) {
0684: // final float finalPrevAlpha = alphaForPrevBackground;
0685: // final float finalCurrAlpha = alphaForCurrBackground;
0686: final float finalTotalAlpha = totalAlpha;
0687: final float finalFadeCoef = fadeCoef;
0688:
0689: final Rectangle offsetCellRect = new Rectangle(0, 0,
0690: cellRect.width, cellRect.height);
0691: textPainter
0692: .attachCallback(new SubstanceTextPainter.BackgroundPaintingCallback() {
0693: public void paintBackground(Graphics g) {
0694: Graphics2D g2d = (Graphics2D) g.create();
0695: if (finalTotalAlpha > 0.0f) {
0696: g2d.setComposite(TransitionLayout
0697: .getAlphaComposite(header,
0698: finalTotalAlpha, g));
0699: SubstanceHighlightUtils.paintHighlight(
0700: g2d, component, offsetCellRect,
0701: 0.0f, null, currTheme
0702: .getColorScheme(),
0703: prevTheme.getColorScheme(),
0704: finalFadeCoef);
0705: g2d.setComposite(TransitionLayout
0706: .getAlphaComposite(header, g));
0707: }
0708: // if (finalPrevAlpha > 0.0f) {
0709: // g2d.setComposite(TransitionLayout
0710: // .getAlphaComposite(header,
0711: // finalPrevAlpha, g));
0712: // SubstanceHighlightUtils.paintHighlight(g2d,
0713: // component, prevState, prevState,
0714: // offsetCellRect, 0.0f);
0715: // // SubstanceTableHeaderUI.highlightDelegate
0716: // // .paintClassicHighlight(g2d, component,
0717: // // offsetCellRect, prevTheme, 0.0f);
0718: // g2d.setComposite(TransitionLayout
0719: // .getAlphaComposite(header, g));
0720: // }
0721: // if (finalCurrAlpha > 0.0f) {
0722: // g2d.setComposite(TransitionLayout
0723: // .getAlphaComposite(header,
0724: // finalCurrAlpha, g));
0725: // SubstanceHighlightUtils.paintHighlight(g2d,
0726: // component, currState, currState,
0727: // offsetCellRect, 0.0f);
0728: // // SubstanceTableHeaderUI.highlightDelegate
0729: // // .paintClassicHighlight(g2d, component,
0730: // // offsetCellRect, currTheme, 0.0f);
0731: // g2d.setComposite(TransitionLayout
0732: // .getAlphaComposite(header, g));
0733: // }
0734: g2d.dispose();
0735: }
0736: });
0737: } else {
0738: if (totalAlpha > 0.0f) {
0739: g2d.setComposite(TransitionLayout.getAlphaComposite(
0740: this .header, totalAlpha, g));
0741: SubstanceHighlightUtils.paintHighlight(g2d, component,
0742: cellRect, 0.0f, null, currTheme
0743: .getColorScheme(), prevTheme
0744: .getColorScheme(), fadeCoef);
0745: g2d.setComposite(TransitionLayout.getAlphaComposite(
0746: this .header, g));
0747: }
0748:
0749: // if (alphaForPrevBackground > 0.0f) {
0750: // g2d.setComposite(TransitionLayout.getAlphaComposite(header,
0751: // alphaForPrevBackground, g));
0752: // SubstanceHighlightUtils.paintHighlight(g2d, component,
0753: // prevState, prevState, cellRect, 0.0f);
0754: // //
0755: // SubstanceTableHeaderUI.highlightDelegate.paintClassicHighlight(
0756: // // g2d, component, cellRect, prevTheme, 0.0f);
0757: // g2d.setComposite(TransitionLayout.getAlphaComposite(header, g));
0758: // }
0759: // if (alphaForCurrBackground > 0.0f) {
0760: // g2d.setComposite(TransitionLayout.getAlphaComposite(header,
0761: // alphaForCurrBackground, g));
0762: // SubstanceHighlightUtils.paintHighlight(g2d, component,
0763: // currState, currState, cellRect, 0.0f);
0764: // //
0765: // SubstanceTableHeaderUI.highlightDelegate.paintClassicHighlight(
0766: // // g2d, component, cellRect, currTheme, 0.0f);
0767: // g2d.setComposite(TransitionLayout.getAlphaComposite(header, g));
0768: // }
0769: }
0770:
0771: this .header.putClientProperty(
0772: SubstanceCoreUtilities.DO_NOT_FILL_BACKGROUND,
0773: Boolean.TRUE);
0774: rendererPane.paintComponent(g2d, component, header, cellRect.x,
0775: cellRect.y, cellRect.width, cellRect.height, true);
0776:
0777: this .header.putClientProperty(
0778: SubstanceCoreUtilities.DO_NOT_FILL_BACKGROUND, null);
0779: g2d.dispose();
0780: //
0781: // Component component = getHeaderRenderer(columnIndex);
0782: // SubstanceTheme theme = isSelected ? SubstanceCoreUtilities
0783: // .getActiveTheme(header, true) : SubstanceCoreUtilities
0784: // .getDefaultTheme(header, true);
0785: // SubstanceTableHeaderUI.backgroundDelegate.update(g, component,
0786: // cellRect, theme/* .getHighlightBackgroundTheme() */, true);
0787: // rendererPane.paintComponent(g, component, header, cellRect.x,
0788: // cellRect.y, cellRect.width, cellRect.height, true);
0789: }
0790:
0791: /**
0792: * Retrieves view index for the specified column.
0793: *
0794: * @param aColumn
0795: * Table column.
0796: * @return View index for the specified column.
0797: */
0798: private int viewIndexForColumn(TableColumn aColumn) {
0799: TableColumnModel cm = header.getColumnModel();
0800: for (int column = 0; column < cm.getColumnCount(); column++) {
0801: if (cm.getColumn(column) == aColumn) {
0802: return column;
0803: }
0804: }
0805: return -1;
0806: }
0807:
0808: /*
0809: * (non-Javadoc)
0810: *
0811: * @see javax.swing.plaf.ComponentUI#update(java.awt.Graphics,
0812: * javax.swing.JComponent)
0813: */
0814: @Override
0815: public void update(Graphics g, JComponent c) {
0816: // fix for issue 175 - table header under resize mode off
0817: // was painted in theme-agnostic (gray) color.
0818: final ComponentState backgroundState = (this .header.isEnabled() && this .header
0819: .getTable().isEnabled()) ? ComponentState.DEFAULT
0820: : ComponentState.DISABLED_UNSELECTED;
0821: // SubstanceTheme theme = SubstanceCoreUtilities.getTheme(c,
0822: // backgroundState, true, true, true);
0823: SubstanceTheme theme = SubstanceThemeUtilities.getTheme(c,
0824: backgroundState);
0825:
0826: SubstanceHighlightUtils.paintHighlight(g, c, c.getBounds(),
0827: 0.0f, null, theme.getColorScheme(), theme
0828: .getColorScheme(), 0.0f);
0829:
0830: // SubstanceHighlightUtils.paintHighlight(g, c, backgroundState,
0831: // backgroundState, c.getBounds(), 0.0f);
0832: // paintClassicHighlight(g, c, c.getBounds(), theme, 0.0f);
0833: paint(g, c);
0834: }
0835:
0836: /*
0837: * (non-Javadoc)
0838: *
0839: * @see javax.swing.plaf.basic.BasicTableHeaderUI#uninstallUI(javax.swing.JComponent)
0840: */
0841: @Override
0842: public void uninstallUI(JComponent c) {
0843: for (int i = 0; i < header.getColumnModel().getColumnCount(); i++) {
0844: TableColumn aColumn = header.getColumnModel().getColumn(i);
0845: TableCellRenderer renderer = aColumn.getHeaderRenderer();
0846: if (renderer == null) {
0847: renderer = header.getDefaultRenderer();
0848: }
0849: Component rendComp = renderer
0850: .getTableCellRendererComponent(header.getTable(),
0851: aColumn.getHeaderValue(), false, false, -1,
0852: i);
0853: SwingUtilities.updateComponentTreeUI(rendComp);
0854: }
0855: super .uninstallUI(c);
0856: }
0857:
0858: /**
0859: * Returns the previous state for the specified column.
0860: *
0861: * @param columnIndex
0862: * Column index.
0863: * @return The previous state for the specified column.
0864: */
0865: public ComponentState getPrevColumnState(int columnIndex) {
0866: if (this .header.isEnabled()
0867: && this .header.getTable().isEnabled()
0868: && SubstanceDefaultTableHeaderCellRenderer
0869: .isColumnSorted(this .header.getTable(),
0870: columnIndex))
0871: return ComponentState.SELECTED;
0872:
0873: if (prevStateMap.containsKey(columnIndex))
0874: return prevStateMap.get(columnIndex);
0875: return ComponentState.DEFAULT;
0876: }
0877:
0878: /**
0879: * Returns the current state for the specified cell.
0880: *
0881: * @param columnIndex
0882: * Column index.
0883: * @return The current state for the specified column.
0884: */
0885: public ComponentState getColumnState(int columnIndex) {
0886: ButtonModel synthModel = new DefaultButtonModel();
0887: boolean toEnable = header.isEnabled();
0888:
0889: // get the rollover column index from the table UI delegate
0890: JTable table = this .header.getTable();
0891: if (table != null) {
0892: toEnable = toEnable && table.isEnabled();
0893: TableUI ui = table.getUI();
0894: if (ui instanceof SubstanceTableUI) {
0895: SubstanceTableUI tableUI = (SubstanceTableUI) table
0896: .getUI();
0897: int rolledOverIndex = tableUI.getRolloverColumnIndex();
0898: synthModel.setRollover((rolledOverIndex >= 0)
0899: && (rolledOverIndex == columnIndex));
0900: }
0901: }
0902:
0903: if (toEnable
0904: && SubstanceDefaultTableHeaderCellRenderer
0905: .isColumnSorted(this .header.getTable(),
0906: columnIndex))
0907: return ComponentState.SELECTED;
0908:
0909: synthModel.setEnabled(toEnable);
0910:
0911: // set selection on the model.
0912: TableColumnModel columnModel = header.getColumnModel();
0913: synthModel.setSelected(columnModel.getColumnSelectionAllowed()
0914: && columnModel.getSelectionModel().isSelectedIndex(
0915: columnIndex));
0916: return ComponentState.getState(synthModel, null);
0917: }
0918:
0919: /**
0920: * Returns the animation callback for the specified column.
0921: *
0922: * @param columnIndex
0923: * Column index.
0924: * @return Animation callback for the specified column.
0925: */
0926: public FadeTrackerCallback getCallback(int columnIndex) {
0927: return new ColumnHeaderRepaintCallback(this .header, columnIndex);
0928: }
0929:
0930: public JComponent getScrollPaneCornerFiller() {
0931: return new ScrollPaneCornerFiller(this .header);
0932: }
0933:
0934: protected static class ScrollPaneCornerFiller extends JComponent
0935: implements UIResource {
0936: protected JTableHeader header;
0937:
0938: public ScrollPaneCornerFiller(JTableHeader header) {
0939: this .header = header;
0940: // if (this.header == null) {
0941: // System.err
0942: // .println("************** Header is null ******************");
0943: // }
0944: }
0945:
0946: @Override
0947: protected void paintComponent(Graphics g) {
0948: Graphics2D g2d = (Graphics2D) g.create();
0949: // System.err.println("Painting " + this.hashCode() + " from "
0950: // + ((header == null) ? "null" : header.hashCode()));
0951:
0952: boolean ltr = header.getComponentOrientation()
0953: .isLeftToRight();
0954: final ComponentState backgroundState = (header.isEnabled() && header
0955: .getTable().isEnabled()) ? ComponentState.DEFAULT
0956: : ComponentState.DISABLED_UNSELECTED;
0957: SubstanceTheme theme = SubstanceThemeUtilities.getTheme(
0958: header, backgroundState);
0959: SubstanceHighlightUtils.paintHighlight(g2d, this .header,
0960: new Rectangle(0, 0, this .getWidth(), this
0961: .getHeight()), 0.0f, null, theme
0962: .getColorScheme(), theme.getColorScheme(),
0963: 0.0f);
0964:
0965: // SubstanceHighlightUtils.paintHighlight(g2d, header,
0966: // backgroundState, backgroundState, new Rectangle(0, 0, this
0967: // .getWidth(), this.getHeight()), 0.0f);
0968: // paintClassicHighlight(g2d,
0969: // header, new Rectangle(0, 0, this.getWidth(), this
0970: // .getHeight()), theme, 0.0f);
0971:
0972: g2d.setColor(getGridColor(this .header));
0973: float strokeWidth = SubstanceSizeUtils
0974: .getBorderStrokeWidth(SubstanceSizeUtils
0975: .getComponentFontSize(header));
0976: g2d.setStroke(new BasicStroke(strokeWidth,
0977: BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
0978: g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
0979: RenderingHints.VALUE_ANTIALIAS_ON);
0980: g2d.setComposite(TransitionLayout.getAlphaComposite(
0981: this .header, 0.7f, g));
0982:
0983: int x = ltr ? (int) strokeWidth / 2 : getWidth() - 1
0984: - (int) strokeWidth / 2;
0985: g2d.drawLine(x, 0, x, getHeight());
0986:
0987: g2d.dispose();
0988: }
0989: }
0990:
0991: public void processColumnModelChangeEvent(
0992: TableColumnModel oldModel, TableColumnModel newModel) {
0993: if (oldModel != null) {
0994: oldModel.getSelectionModel().removeListSelectionListener(
0995: substanceFadeSelectionListener);
0996: }
0997: if (newModel != null) {
0998: newModel.getSelectionModel().addListSelectionListener(
0999: substanceFadeSelectionListener);
1000: }
1001: selectedIndices.clear();
1002: prevStateMap.clear();
1003: nextStateMap.clear();
1004: }
1005:
1006: /**
1007: * Repaints a single column header during the fade animation cycle.
1008: *
1009: * @author Kirill Grouchnikov
1010: */
1011: protected class ColumnHeaderRepaintCallback extends
1012: FadeTrackerAdapter {
1013: /**
1014: * Associated table header.
1015: */
1016: protected JTableHeader header;
1017:
1018: /**
1019: * Associated (animated) column index.
1020: */
1021: protected int columnIndex;
1022:
1023: /**
1024: * Creates a new animation repaint callback.
1025: *
1026: * @param header
1027: * Associated table header.
1028: * @param columnIndex
1029: * Associated (animated) column index.
1030: */
1031: public ColumnHeaderRepaintCallback(JTableHeader header,
1032: int columnIndex) {
1033: this .header = header;
1034: this .columnIndex = columnIndex;
1035: }
1036:
1037: /*
1038: * (non-Javadoc)
1039: *
1040: * @see org.jvnet.lafwidget.utils.FadeTracker$FadeTrackerCallback#fadeEnded(org.jvnet.lafwidget.utils.FadeTracker.FadeKind)
1041: */
1042: @Override
1043: public void fadeEnded(FadeKind fadeKind) {
1044: if ((SubstanceTableHeaderUI.this .header == header)
1045: && (columnIndex < header.getColumnModel()
1046: .getColumnCount())) {
1047: ComponentState currState = getColumnState(columnIndex);
1048: if (currState == ComponentState.DEFAULT) {
1049: prevStateMap.remove(columnIndex);
1050: nextStateMap.remove(columnIndex);
1051: } else {
1052: prevStateMap.put(columnIndex, currState);
1053: nextStateMap.put(columnIndex, currState);
1054: }
1055: // System.out.println(tabIndex + "->"
1056: // + prevStateMap.get(tabIndex).name());
1057: }
1058: repaintColumnHeader();
1059: }
1060:
1061: /*
1062: * (non-Javadoc)
1063: *
1064: * @see org.jvnet.lafwidget.animation.FadeTrackerAdapter#fadeReversed(org.jvnet.lafwidget.animation.FadeKind,
1065: * boolean, float)
1066: */
1067: @Override
1068: public void fadeReversed(FadeKind fadeKind, boolean isFadingIn,
1069: float fadeCycle10) {
1070: if ((SubstanceTableHeaderUI.this .header == header)
1071: && (columnIndex < header.getColumnModel()
1072: .getColumnCount())) {
1073: ComponentState nextState = nextStateMap
1074: .get(columnIndex);
1075: if (nextState == null) {
1076: prevStateMap.remove(columnIndex);
1077: } else {
1078: prevStateMap.put(columnIndex, nextState);
1079: }
1080: // System.out.println(tabIndex + "->"
1081: // + prevStateMap.get(tabIndex).name());
1082: }
1083: repaintColumnHeader();
1084: }
1085:
1086: /*
1087: * (non-Javadoc)
1088: *
1089: * @see org.jvnet.lafwidget.utils.FadeTracker$FadeTrackerCallback#fadePerformed(org.jvnet.lafwidget.utils.FadeTracker.FadeKind,
1090: * float)
1091: */
1092: @Override
1093: public void fadePerformed(FadeKind fadeKind, float fade10) {
1094: if ((SubstanceTableHeaderUI.this .header == header)
1095: && (columnIndex < header.getColumnModel()
1096: .getColumnCount())) {
1097: nextStateMap.put(columnIndex,
1098: getColumnState(columnIndex));
1099: }
1100: repaintColumnHeader();
1101: }
1102:
1103: /**
1104: * Repaints the associated cell.
1105: */
1106: private void repaintColumnHeader() {
1107: SwingUtilities.invokeLater(new Runnable() {
1108: public void run() {
1109: if (header == null) {
1110: // may happen if the LAF was switched in the meantime
1111: return;
1112: }
1113: try {
1114: // maybeUpdateLayoutState();
1115: int cellCount = header.getColumnModel()
1116: .getColumnCount();
1117: if ((cellCount > 0)
1118: && (columnIndex < cellCount)) {
1119: // need to retrieve the cell rectangle since the
1120: // cells can be moved while animating
1121: Rectangle rect = header
1122: .getHeaderRect(columnIndex);
1123: Rectangle damaged = new Rectangle(
1124: rect.x - 5, rect.y,
1125: rect.width + 10, rect.height);
1126: header.repaint(damaged);
1127: }
1128: } catch (RuntimeException re) {
1129: return;
1130: }
1131: }
1132: });
1133: }
1134: }
1135: }
|