Source Code Cross Referenced for SubstanceTableUI.java in  » Swing-Library » substance-look-feel » org » jvnet » substance » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Swing Library » substance look feel » org.jvnet.substance 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.