~ubuntu-branches/debian/stretch/insubstantial/stretch

« back to all changes in this revision

Viewing changes to substance/src/main/java/org/pushingpixels/substance/internal/ui/SubstanceTableUI.java

  • Committer: Package Import Robot
  • Author(s): Felix Natter
  • Date: 2016-01-18 20:58:45 UTC
  • Revision ID: package-import@ubuntu.com-20160118205845-crbmrkda61qsi5qa
Tags: upstream-7.3+dfsg2
ImportĀ upstreamĀ versionĀ 7.3+dfsg2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2005-2010 Substance Kirill Grouchnikov. All Rights Reserved.
 
3
 *
 
4
 * Redistribution and use in source and binary forms, with or without
 
5
 * modification, are permitted provided that the following conditions are met:
 
6
 *
 
7
 *  o Redistributions of source code must retain the above copyright notice,
 
8
 *    this list of conditions and the following disclaimer.
 
9
 *
 
10
 *  o Redistributions in binary form must reproduce the above copyright notice,
 
11
 *    this list of conditions and the following disclaimer in the documentation
 
12
 *    and/or other materials provided with the distribution.
 
13
 *
 
14
 *  o Neither the name of Substance Kirill Grouchnikov nor the names of
 
15
 *    its contributors may be used to endorse or promote products derived
 
16
 *    from this software without specific prior written permission.
 
17
 *
 
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
19
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 
20
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 
21
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 
22
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 
23
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
24
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 
25
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 
26
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 
27
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 
28
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
29
 */
 
30
package org.pushingpixels.substance.internal.ui;
 
31
 
 
32
import java.awt.*;
 
33
import java.awt.event.*;
 
34
import java.beans.PropertyChangeEvent;
 
35
import java.beans.PropertyChangeListener;
 
36
import java.util.*;
 
37
import java.util.List;
 
38
 
 
39
import javax.swing.*;
 
40
import javax.swing.RowSorter.SortKey;
 
41
import javax.swing.border.Border;
 
42
import javax.swing.event.*;
 
43
import javax.swing.plaf.*;
 
44
import javax.swing.plaf.basic.BasicTableUI;
 
45
import javax.swing.table.*;
 
46
 
 
47
import org.pushingpixels.lafwidget.LafWidgetUtilities;
 
48
import org.pushingpixels.lafwidget.animation.AnimationConfigurationManager;
 
49
import org.pushingpixels.lafwidget.animation.AnimationFacet;
 
50
import org.pushingpixels.substance.api.*;
 
51
import org.pushingpixels.substance.api.SubstanceConstants.Side;
 
52
import org.pushingpixels.substance.api.renderers.SubstanceDefaultTableCellRenderer;
 
53
import org.pushingpixels.substance.api.renderers.SubstanceDefaultTableHeaderCellRenderer;
 
54
import org.pushingpixels.substance.internal.animation.StateTransitionMultiTracker;
 
55
import org.pushingpixels.substance.internal.animation.StateTransitionTracker;
 
56
import org.pushingpixels.substance.internal.animation.StateTransitionTracker.RepaintCallback;
 
57
import org.pushingpixels.substance.internal.painter.BackgroundPaintingUtils;
 
58
import org.pushingpixels.substance.internal.painter.HighlightPainterUtils;
 
59
import org.pushingpixels.substance.internal.utils.*;
 
60
import org.pushingpixels.trident.Timeline.TimelineState;
 
61
import org.pushingpixels.trident.callback.TimelineCallback;
 
62
import org.pushingpixels.trident.callback.UIThreadTimelineCallbackAdapter;
 
63
 
 
64
/**
 
65
 * UI for tables in <b>Substance</b> look and feel. Unfortunately, the entire
 
66
 * painting stack has been copied from {@link BasicTableUI} since the methods
 
67
 * are private. The animation effects are implemented in the
 
68
 * {@link #paintCell(Graphics, Rectangle, int, int)}.
 
69
 * 
 
70
 * @author Kirill Grouchnikov
 
71
 */
 
72
public class SubstanceTableUI extends BasicTableUI implements
 
73
                UpdateOptimizationAware {
 
74
        /**
 
75
         * Holds the list of currently selected row-column indexes.
 
76
         */
 
77
        protected Map<TableCellId, Object> selectedIndices;
 
78
 
 
79
        /**
 
80
         * Holds the currently rolled-over row-column index, or <code>null</code> if
 
81
         * none such.
 
82
         */
 
83
        protected Set<TableCellId> rolledOverIndices;
 
84
 
 
85
        protected TableCellId focusedCellId;
 
86
 
 
87
        /**
 
88
         * Holds the currently rolled-over column index, or <code>-1</code> if none
 
89
         * such. This is used for the table header animations.
 
90
         */
 
91
        protected int rolledOverColumn;
 
92
 
 
93
        /**
 
94
         * Map of default renderers.
 
95
         */
 
96
        protected Map<Class<?>, TableCellRenderer> defaultRenderers;
 
97
 
 
98
        /**
 
99
         * Map of default editors.
 
100
         */
 
101
        protected Map<Class<?>, TableCellEditor> defaultEditors;
 
102
 
 
103
        /**
 
104
         * Listener that listens to changes on table properties.
 
105
         */
 
106
        protected PropertyChangeListener substancePropertyChangeListener;
 
107
 
 
108
        /**
 
109
         * Listener for transition animations on list selections.
 
110
         */
 
111
        protected TableStateListener substanceTableStateListener;
 
112
 
 
113
        /**
 
114
         * Listener for transition animations on table rollovers.
 
115
         */
 
116
        protected RolloverFadeListener substanceFadeRolloverListener;
 
117
 
 
118
        protected FocusListener substanceFocusListener;
 
119
 
 
120
        private StateTransitionMultiTracker<TableCellId> stateTransitionMultiTracker;
 
121
 
 
122
    protected Boolean drawLeadingVerticalLine;
 
123
    protected Boolean drawTrailingVerticalLine;
 
124
 
 
125
        /**
 
126
         * Cell renderer insets. Is computed in {@link #installDefaults()} and
 
127
         * reused in
 
128
         * {@link SubstanceDefaultTableCellRenderer#getTableCellRendererComponent(JTable, Object, boolean, boolean, int, int)}
 
129
         * and
 
130
         * {@link SubstanceDefaultTableHeaderCellRenderer#getTableCellRendererComponent(JTable, Object, boolean, boolean, int, int)}
 
131
         * for performance optimizations.
 
132
         */
 
133
        private Insets cellRendererInsets;
 
134
 
 
135
        /*
 
136
         * (non-Javadoc)
 
137
         * 
 
138
         * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
 
139
         */
 
140
        public static ComponentUI createUI(JComponent comp) {
 
141
                SubstanceCoreUtilities.testComponentCreationThreadingViolation(comp);
 
142
                return new SubstanceTableUI();
 
143
        }
 
144
 
 
145
        /**
 
146
         * Creates a UI delegate for table.
 
147
         */
 
148
        public SubstanceTableUI() {
 
149
                super();
 
150
                this.selectedIndices = new HashMap<TableCellId, Object>();
 
151
                this.rolledOverIndices = new HashSet<TableCellId>();
 
152
                this.stateTransitionMultiTracker = new StateTransitionMultiTracker<TableCellId>();
 
153
                this.rolledOverColumn = -1;
 
154
 
 
155
                this.cellId = new TableCellId(-1, -1);
 
156
        }
 
157
 
 
158
        static class BooleanEditor extends DefaultCellEditor {
 
159
                private static class SubstanceEditorCheckBox extends JCheckBox {
 
160
                        @Override
 
161
                        public void setOpaque(boolean isOpaque) {
 
162
                                if (!isOpaque) {
 
163
                                        super.setOpaque(isOpaque);
 
164
                                }
 
165
                        }
 
166
 
 
167
                        @Override
 
168
                        public boolean isOpaque() {
 
169
                                return false;
 
170
                        }
 
171
 
 
172
                        @Override
 
173
                        public void setBorder(Border border) {
 
174
                        }
 
175
                }
 
176
 
 
177
                public BooleanEditor() {
 
178
                        super(new SubstanceEditorCheckBox());
 
179
                        JCheckBox checkBox = (JCheckBox) getComponent();
 
180
                        checkBox.setOpaque(false);
 
181
                        checkBox.setHorizontalAlignment(JCheckBox.CENTER);
 
182
                }
 
183
        }
 
184
 
 
185
        /*
 
186
         * (non-Javadoc)
 
187
         * 
 
188
         * @see javax.swing.plaf.basic.BasicTableUI#installDefaults()
 
189
         */
 
190
        @Override
 
191
        protected void installDefaults() {
 
192
                super.installDefaults();
 
193
                if (SubstanceCoreUtilities.toDrawWatermark(this.table))
 
194
                        this.table.setOpaque(false);
 
195
 
 
196
                // fix for defect 117 - need to restore default table cell
 
197
                // renderers when Substance is unset
 
198
                this.defaultRenderers = new HashMap<Class<?>, TableCellRenderer>();
 
199
 
 
200
                Class<?>[] defClasses = new Class[] { Object.class, Icon.class,
 
201
                                ImageIcon.class, Number.class, Float.class, Double.class,
 
202
                                Date.class, Boolean.class };
 
203
                for (Class<?> clazz : defClasses) {
 
204
                        this.defaultRenderers.put(clazz,
 
205
                                        this.table.getDefaultRenderer(clazz));
 
206
                }
 
207
 
 
208
                // Override default renderers - note fix for issue 194
 
209
                // that doesn't override user-specific renderers (those that don't come
 
210
                // from JTable class).
 
211
                this.installRendererIfNecessary(Object.class,
 
212
                                new SubstanceDefaultTableCellRenderer());
 
213
                this.installRendererIfNecessary(Icon.class,
 
214
                                new SubstanceDefaultTableCellRenderer.IconRenderer());
 
215
                this.installRendererIfNecessary(ImageIcon.class,
 
216
                                new SubstanceDefaultTableCellRenderer.IconRenderer());
 
217
                this.installRendererIfNecessary(Number.class,
 
218
                                new SubstanceDefaultTableCellRenderer.NumberRenderer());
 
219
                this.installRendererIfNecessary(Float.class,
 
220
                                new SubstanceDefaultTableCellRenderer.DoubleRenderer());
 
221
                this.installRendererIfNecessary(Double.class,
 
222
                                new SubstanceDefaultTableCellRenderer.DoubleRenderer());
 
223
                this.installRendererIfNecessary(Date.class,
 
224
                                new SubstanceDefaultTableCellRenderer.DateRenderer());
 
225
 
 
226
                // fix for bug 56 - making default renderer for Boolean a check box.
 
227
                this.installRendererIfNecessary(Boolean.class,
 
228
                                new SubstanceDefaultTableCellRenderer.BooleanRenderer());
 
229
 
 
230
                this.defaultEditors = new HashMap<Class<?>, TableCellEditor>();
 
231
 
 
232
                Class<?>[] defEditorClasses = new Class[] { Boolean.class };
 
233
                for (Class<?> clazz : defEditorClasses) {
 
234
                        this.defaultEditors.put(clazz, this.table.getDefaultEditor(clazz));
 
235
                }
 
236
                this.installEditorIfNecessary(Boolean.class, new BooleanEditor());
 
237
 
 
238
                int rows = this.table.getRowCount();
 
239
                int cols = this.table.getColumnCount();
 
240
                for (int i = 0; i < rows; i++) {
 
241
                        for (int j = 0; j < cols; j++) {
 
242
                                if (this.table.isCellSelected(i, j)) {
 
243
                                        TableCellId cellId = new TableCellId(i, j);
 
244
                                        this.selectedIndices.put(cellId,
 
245
                                                        this.table.getValueAt(i, j));
 
246
                                }
 
247
                        }
 
248
                }
 
249
 
 
250
                // This is a little tricky, and hopefully will not
 
251
                // interfere with existing applications. The row height in tables
 
252
                // is computed differently from trees and lists. While lists
 
253
                // trees respect the current renderers and their insets, the
 
254
                // JTable uses hard-code value of 16 pixels as the default
 
255
                // row height. This, obviously, doesn't sit well with the support
 
256
                // for custom fonts and high-DPI monitors.
 
257
                //
 
258
                // The current solution first checks whether all the renderers
 
259
                // come from Substance. If not, it does nothing. If they do, it
 
260
                // creates a dummy label, computes its preferred height and apply
 
261
                // insets. There's no need to go over each cell and compute its
 
262
                // preferred height - since at this moment the cell renderers
 
263
                // *are* Substance labels.
 
264
                boolean areAllRenderersFromSubstance = true;
 
265
                TableColumnModel columnModel = table.getColumnModel();
 
266
                for (int i = 0; i < columnModel.getColumnCount(); i++) {
 
267
                        TableColumn column = columnModel.getColumn(i);
 
268
                        TableCellRenderer renderer = column.getCellRenderer();
 
269
                        if (renderer == null) {
 
270
                                renderer = table.getDefaultRenderer(table.getColumnClass(i));
 
271
                        }
 
272
                        if ((renderer instanceof SubstanceDefaultTableCellRenderer)
 
273
                                        || (renderer instanceof SubstanceDefaultTableCellRenderer.BooleanRenderer))
 
274
                                continue;
 
275
                        areAllRenderersFromSubstance = false;
 
276
                        break;
 
277
                }
 
278
                if (areAllRenderersFromSubstance) {
 
279
                        Insets rendererInsets = SubstanceSizeUtils
 
280
                                        .getTableCellRendererInsets(SubstanceSizeUtils
 
281
                                                        .getComponentFontSize(table));
 
282
                        JLabel dummy = new JLabel("dummy");
 
283
                        dummy.setFont(table.getFont());
 
284
                        int rowHeight = dummy.getPreferredSize().height
 
285
                                        + rendererInsets.bottom + rendererInsets.top;
 
286
                        table.setRowHeight(rowHeight);
 
287
                }
 
288
 
 
289
                // instead of computing the cell renderer insets on
 
290
                // every cell rendering, compute it once and expose to the
 
291
                // SubstanceDefaultTableCellRenderer
 
292
                this.cellRendererInsets = SubstanceSizeUtils
 
293
                                .getTableCellRendererInsets(SubstanceSizeUtils
 
294
                                                .getComponentFontSize(table));
 
295
 
 
296
        drawLeadingVerticalLine = (Boolean) table.getClientProperty(SubstanceLookAndFeel.TABLE_LEADING_VERTICAL_LINE);
 
297
        drawTrailingVerticalLine = (Boolean) table.getClientProperty(SubstanceLookAndFeel.TABLE_TRAILING_VERTICAL_LINE);
 
298
        }
 
299
 
 
300
        /**
 
301
         * Installs Substance-specific renderers for column classes that don't have
 
302
         * application-specific renderers installed by the user code.
 
303
         * 
 
304
         * @param clazz
 
305
         *            Column class.
 
306
         * @param renderer
 
307
         *            Default renderer for the specified column class.
 
308
         */
 
309
        protected void installRendererIfNecessary(Class<?> clazz,
 
310
                        TableCellRenderer renderer) {
 
311
                TableCellRenderer currRenderer = this.table.getDefaultRenderer(clazz);
 
312
                if (currRenderer != null) {
 
313
                        boolean isCore = (currRenderer instanceof DefaultTableCellRenderer.UIResource)
 
314
                                        || (currRenderer.getClass().getName()
 
315
                                                        .startsWith("javax.swing.JTable"));
 
316
                        if (!isCore)
 
317
                                return;
 
318
                }
 
319
                this.table.setDefaultRenderer(clazz, renderer);
 
320
        }
 
321
 
 
322
        /**
 
323
         * Installs Substance-specific renderers for column classes that don't have
 
324
         * application-specific renderers installed by the user code.
 
325
         * 
 
326
         * @param clazz
 
327
         *            Column class.
 
328
         * @param editor
 
329
         *            Default renderer for the specified column class.
 
330
         */
 
331
        protected void installEditorIfNecessary(Class<?> clazz,
 
332
                        TableCellEditor editor) {
 
333
                TableCellEditor currEditor = this.table.getDefaultEditor(clazz);
 
334
                if (currEditor != null) {
 
335
                        boolean isCore = currEditor.getClass().getName()
 
336
                                        .startsWith("javax.swing.JTable");
 
337
                        if (!isCore)
 
338
                                return;
 
339
                }
 
340
                this.table.setDefaultEditor(clazz, editor);
 
341
        }
 
342
 
 
343
        /*
 
344
         * (non-Javadoc)
 
345
         * 
 
346
         * @see javax.swing.plaf.basic.BasicTableUI#uninstallDefaults()
 
347
         */
 
348
        @Override
 
349
        protected void uninstallDefaults() {
 
350
                // fix for defect 117 - need to restore default table cell
 
351
                // renderers when Substance is unset
 
352
                for (Map.Entry<Class<?>, TableCellRenderer> entry : this.defaultRenderers
 
353
                                .entrySet()) {
 
354
                        // fix for issue 194 - restore only those renderers that were
 
355
                        // overriden by Substance.
 
356
                        this.uninstallRendererIfNecessary(entry.getKey(), entry.getValue());
 
357
                }
 
358
 
 
359
                for (Map.Entry<Class<?>, TableCellEditor> entry : this.defaultEditors
 
360
                                .entrySet()) {
 
361
                        this.uninstallEditorIfNecessary(entry.getKey(), entry.getValue());
 
362
                }
 
363
 
 
364
                this.selectedIndices.clear();
 
365
                // this.table.putClientProperty(SubstanceTableUI.SELECTED_INDICES,
 
366
                // null);
 
367
 
 
368
                super.uninstallDefaults();
 
369
        }
 
370
 
 
371
        /**
 
372
         * Uninstalls default Substance renderers that were installed in
 
373
         * {@link #installRendererIfNecessary(Class, TableCellRenderer)}.
 
374
         * 
 
375
         * @param clazz
 
376
         *            Column class.
 
377
         * @param renderer
 
378
         *            Renderer to restore.
 
379
         */
 
380
        protected void uninstallRendererIfNecessary(Class<?> clazz,
 
381
                        TableCellRenderer renderer) {
 
382
                TableCellRenderer currRenderer = this.table.getDefaultRenderer(clazz);
 
383
                if (currRenderer != null) {
 
384
                        boolean isSubstanceRenderer = isSubstanceDefaultRenderer(currRenderer);
 
385
                        if (!isSubstanceRenderer)
 
386
                                return;
 
387
                }
 
388
                if (renderer instanceof Component)
 
389
                        SwingUtilities.updateComponentTreeUI((Component) renderer);
 
390
                this.table.setDefaultRenderer(clazz, renderer);
 
391
        }
 
392
 
 
393
        /**
 
394
         * Uninstalls default Substance editors that were installed in
 
395
         * {@link #installEditorIfNecessary(Class, TableCellEditor)}.
 
396
         * 
 
397
         * @param clazz
 
398
         *            Column class.
 
399
         * @param editor
 
400
         *            Editor to restore.
 
401
         */
 
402
        protected void uninstallEditorIfNecessary(Class<?> clazz,
 
403
                        TableCellEditor editor) {
 
404
                TableCellEditor currEditor = this.table.getDefaultEditor(clazz);
 
405
                if (currEditor != null) {
 
406
                        boolean isSubstanceEditor = isSubstanceDefaultEditor(currEditor);
 
407
                        if (!isSubstanceEditor)
 
408
                                return;
 
409
                }
 
410
                if (editor instanceof Component)
 
411
                        SwingUtilities.updateComponentTreeUI((Component) editor);
 
412
                this.table.setDefaultEditor(clazz, editor);
 
413
        }
 
414
 
 
415
        /*
 
416
         * (non-Javadoc)
 
417
         * 
 
418
         * @see javax.swing.plaf.basic.BasicTableUI#installListeners()
 
419
         */
 
420
        @Override
 
421
        protected void installListeners() {
 
422
                super.installListeners();
 
423
                this.substancePropertyChangeListener = new PropertyChangeListener() {
 
424
                        @Override
 
425
            public void propertyChange(PropertyChangeEvent evt) {
 
426
                                if (SubstanceLookAndFeel.WATERMARK_VISIBLE.equals(evt
 
427
                                                .getPropertyName())) {
 
428
                                        SubstanceTableUI.this.table
 
429
                                                        .setOpaque(!SubstanceCoreUtilities
 
430
                                                                        .toDrawWatermark(SubstanceTableUI.this.table));
 
431
                                }
 
432
 
 
433
                                if ("columnSelectionAllowed".equals(evt.getPropertyName())
 
434
                                                || "rowSelectionAllowed".equals(evt.getPropertyName())) {
 
435
                                        SubstanceTableUI.this.syncSelection(true);
 
436
                                }
 
437
 
 
438
                                if ("model".equals(evt.getPropertyName())) {
 
439
                                        TableModel old = (TableModel) evt.getOldValue();
 
440
                                        if (old != null) {
 
441
                                                old.removeTableModelListener(substanceTableStateListener);
 
442
                                        }
 
443
                                        // fix for defect 291 - track changes to the table.
 
444
                                        table.getModel().addTableModelListener(
 
445
                                                        substanceTableStateListener);
 
446
                                        selectedIndices.clear();
 
447
                                        stateTransitionMultiTracker.clear();
 
448
                                        SubstanceTableUI.this.syncSelection(true);
 
449
                                }
 
450
 
 
451
                                if ("columnModel".equals(evt.getPropertyName())) {
 
452
                                        TableColumnModel old = (TableColumnModel) evt.getOldValue();
 
453
                                        if (old != null) {
 
454
                                                old.getSelectionModel().removeListSelectionListener(
 
455
                                                                substanceTableStateListener);
 
456
                                        }
 
457
                                        table.getColumnModel()
 
458
                                                        .getSelectionModel()
 
459
                                                        .addListSelectionListener(
 
460
                                                                        substanceTableStateListener);
 
461
                                        selectedIndices.clear();
 
462
                                        stateTransitionMultiTracker.clear();
 
463
                                        SubstanceTableUI.this.syncSelection(true);
 
464
 
 
465
                                        JTableHeader tableHeader = table.getTableHeader();
 
466
                                        // fix for issue 408 - table header may be null.
 
467
                                        if (tableHeader != null) {
 
468
                                                // fix for issue 309 - syncing animations on tables
 
469
                                                // and table headers.
 
470
                                                SubstanceTableHeaderUI headerUI = (SubstanceTableHeaderUI) tableHeader
 
471
                                                                .getUI();
 
472
                                                headerUI.processColumnModelChangeEvent(
 
473
                                                                (TableColumnModel) evt.getOldValue(),
 
474
                                                                (TableColumnModel) evt.getNewValue());
 
475
                                        }
 
476
                                }
 
477
 
 
478
                                // fix for defect 243 - not tracking changes to selection
 
479
                                // model results in incorrect selection painting on JXTreeTable
 
480
                                // component from SwingX.
 
481
                                if ("selectionModel".equals(evt.getPropertyName())) {
 
482
                                        ListSelectionModel old = (ListSelectionModel) evt
 
483
                                                        .getOldValue();
 
484
                                        if (old != null) {
 
485
                                                old.removeListSelectionListener(substanceTableStateListener);
 
486
                                        }
 
487
                                        table.getSelectionModel().addListSelectionListener(
 
488
                                                        substanceTableStateListener);
 
489
                                        selectedIndices.clear();
 
490
                                        stateTransitionMultiTracker.clear();
 
491
                                        SubstanceTableUI.this.syncSelection(true);
 
492
                                }
 
493
 
 
494
                                // fix for issue 479 - tracking sort / filter changes and
 
495
                                // canceling selection animations
 
496
                                if ("rowSorter".equals(evt.getPropertyName())) {
 
497
                                        RowSorter old = (RowSorter) evt.getOldValue();
 
498
                                        if (old != null) {
 
499
                                                old.removeRowSorterListener(substanceTableStateListener);
 
500
                                        }
 
501
                                        RowSorter newSorter = (RowSorter) evt.getNewValue();
 
502
                                        if (newSorter != null) {
 
503
                                                newSorter
 
504
                                                                .addRowSorterListener(substanceTableStateListener);
 
505
                                        }
 
506
                                        selectedIndices.clear();
 
507
                                        stateTransitionMultiTracker.clear();
 
508
                                        SubstanceTableUI.this.syncSelection(true);
 
509
                                }
 
510
 
 
511
                                if ("font".equals(evt.getPropertyName())) {
 
512
                                        SwingUtilities.invokeLater(new Runnable() {
 
513
                                                @Override
 
514
                        public void run() {
 
515
                                                        // fix for bug 341
 
516
                                                        if (table == null)
 
517
                                                                return;
 
518
                                                        table.updateUI();
 
519
                                                }
 
520
                                        });
 
521
                                }
 
522
 
 
523
                                if ("background".equals(evt.getPropertyName())) {
 
524
                                        // propagate application-specific background color to the
 
525
                                        // header.
 
526
                                        Color newBackgr = (Color) evt.getNewValue();
 
527
                                        JTableHeader header = table.getTableHeader();
 
528
                                        if (header != null) {
 
529
                                                Color headerBackground = header.getBackground();
 
530
                                                if (SubstanceCoreUtilities
 
531
                                                                .canReplaceChildBackgroundColor(headerBackground)) {
 
532
                                                        if (!(newBackgr instanceof UIResource)) {
 
533
                                                                if (newBackgr == null) {
 
534
                                                                        header.setBackground(null);
 
535
                                                                } else {
 
536
                                                                        // Issue 450 - wrap the color in
 
537
                                                                        // SubstanceColorResource to
 
538
                                                                        // mark that it can be replaced.
 
539
                                                                        header.setBackground(new SubstanceColorResource(
 
540
                                                                                        newBackgr));
 
541
                                                                }
 
542
                                                        } else {
 
543
                                                                header.setBackground(newBackgr);
 
544
                                                        }
 
545
                                                }
 
546
                                        }
 
547
                                }
 
548
 
 
549
                                // fix for issue 361 - track enabled status of the table
 
550
                                // and propagate to the table header
 
551
                                if ("enabled".equals(evt.getPropertyName())) {
 
552
                                        JTableHeader header = table.getTableHeader();
 
553
                                        if (header != null) {
 
554
                                                header.setEnabled(table.isEnabled());
 
555
                                        }
 
556
                                }
 
557
 
 
558
                                if ("dropLocation".equals(evt.getPropertyName())) {
 
559
                                        JTable.DropLocation oldValue = (JTable.DropLocation) evt
 
560
                                                        .getOldValue();
 
561
                                        if (oldValue != null) {
 
562
                                                Rectangle oldRect = getCellRectangleForRepaint(
 
563
                                                                oldValue.getRow(), oldValue.getColumn());
 
564
                                                table.repaint(oldRect);
 
565
                                        }
 
566
                                        JTable.DropLocation newValue = table.getDropLocation();
 
567
                                        if (newValue != null) {
 
568
                                                Rectangle newRect = getCellRectangleForRepaint(table
 
569
                                                                .getDropLocation().getRow(), table
 
570
                                                                .getDropLocation().getColumn());
 
571
                                                table.repaint(newRect);
 
572
                                        }
 
573
                                }
 
574
 
 
575
                                if ("tableCellEditor".equals(evt.getPropertyName())) {
 
576
                                        // fix for issue 481 - rollovers on cell editing
 
577
                                        TableCellEditor newEditor = (TableCellEditor) evt
 
578
                                                        .getNewValue();
 
579
                                        TableCellEditor oldEditor = (TableCellEditor) evt
 
580
                                                        .getOldValue();
 
581
                                        if (oldEditor != null) {
 
582
                        if (table.getEditorComponent() != null) {
 
583
                            table.getEditorComponent().removeMouseListener(
 
584
                                                                substanceFadeRolloverListener);
 
585
                        }
 
586
                    }
 
587
                                        if (newEditor != null) {
 
588
                        if (table.getEditorComponent() != null) {
 
589
                            table.getEditorComponent().addMouseListener(
 
590
                                                                    substanceFadeRolloverListener);
 
591
                        }
 
592
                    }
 
593
                                }
 
594
                
 
595
                if (SubstanceLookAndFeel.TABLE_LEADING_VERTICAL_LINE.equals(evt.getPropertyName())) {
 
596
                    drawLeadingVerticalLine = (Boolean) evt.getNewValue();
 
597
                }
 
598
                if (SubstanceLookAndFeel.TABLE_TRAILING_VERTICAL_LINE.equals(evt.getPropertyName())) {
 
599
                    drawTrailingVerticalLine = (Boolean) evt.getNewValue();
 
600
                }
 
601
                        }
 
602
                };
 
603
                this.table
 
604
                                .addPropertyChangeListener(this.substancePropertyChangeListener);
 
605
 
 
606
                // Add listener for the selection animation
 
607
                this.substanceTableStateListener = new TableStateListener();
 
608
                this.table.getSelectionModel().addListSelectionListener(
 
609
                                this.substanceTableStateListener);
 
610
                TableColumnModel columnModel = this.table.getColumnModel();
 
611
                columnModel.getSelectionModel().addListSelectionListener(
 
612
                                this.substanceTableStateListener);
 
613
                this.table.getModel().addTableModelListener(
 
614
                                this.substanceTableStateListener);
 
615
                if (this.table.getRowSorter() != null) {
 
616
                        this.table.getRowSorter().addRowSorterListener(
 
617
                                        this.substanceTableStateListener);
 
618
                }
 
619
 
 
620
                // Add listener for the transition animation
 
621
                this.substanceFadeRolloverListener = new RolloverFadeListener();
 
622
                this.table.addMouseMotionListener(this.substanceFadeRolloverListener);
 
623
                this.table.addMouseListener(this.substanceFadeRolloverListener);
 
624
 
 
625
                // fix for issue 481 - tracking focus events on the table
 
626
                this.substanceFocusListener = new FocusListener() {
 
627
                        @Override
 
628
                        public void focusLost(FocusEvent e) {
 
629
                                if (focusedCellId == null)
 
630
                                        return;
 
631
 
 
632
                                ComponentState cellState = getCellState(focusedCellId);
 
633
                                StateTransitionTracker tracker = getTracker(focusedCellId,
 
634
                                                cellState.isFacetActive(ComponentStateFacet.ROLLOVER),
 
635
                                                cellState.isFacetActive(ComponentStateFacet.SELECTION));
 
636
                                tracker.setFocusState(false);
 
637
 
 
638
                                focusedCellId = null;
 
639
                        }
 
640
 
 
641
                        @Override
 
642
                        public void focusGained(FocusEvent e) {
 
643
                                int rowLead = table.getSelectionModel().getLeadSelectionIndex();
 
644
                                int colLead = table.getColumnModel().getSelectionModel()
 
645
                                                .getLeadSelectionIndex();
 
646
                                if ((rowLead >= 0) && (colLead >= 0)) {
 
647
                                        TableCellId toFocus = new TableCellId(rowLead, colLead);
 
648
                                        if (toFocus.equals(focusedCellId))
 
649
                                                return;
 
650
                                        ComponentState cellState = getCellState(toFocus);
 
651
                                        StateTransitionTracker tracker = getTracker(
 
652
                                                        toFocus,
 
653
                                                        cellState
 
654
                                                                        .isFacetActive(ComponentStateFacet.ROLLOVER),
 
655
                                                        cellState
 
656
                                                                        .isFacetActive(ComponentStateFacet.SELECTION));
 
657
                                        tracker.setFocusState(true);
 
658
 
 
659
                                        focusedCellId = toFocus;
 
660
                                }
 
661
                        }
 
662
                };
 
663
                this.table.addFocusListener(this.substanceFocusListener);
 
664
        }
 
665
 
 
666
        /*
 
667
         * (non-Javadoc)
 
668
         * 
 
669
         * @see javax.swing.plaf.basic.BasicTableUI#uninstallListeners()
 
670
         */
 
671
        @Override
 
672
        protected void uninstallListeners() {
 
673
                this.table
 
674
                                .removePropertyChangeListener(this.substancePropertyChangeListener);
 
675
                this.substancePropertyChangeListener = null;
 
676
 
 
677
                this.table.getSelectionModel().removeListSelectionListener(
 
678
                                this.substanceTableStateListener);
 
679
                this.table.getColumnModel().getSelectionModel()
 
680
                                .removeListSelectionListener(this.substanceTableStateListener);
 
681
                this.table.getModel().removeTableModelListener(
 
682
                                this.substanceTableStateListener);
 
683
                if (this.table.getRowSorter() != null) {
 
684
                        this.table.getRowSorter().removeRowSorterListener(
 
685
                                        this.substanceTableStateListener);
 
686
                }
 
687
                this.substanceTableStateListener = null;
 
688
 
 
689
                // Remove listener for the fade animation
 
690
                this.table
 
691
                                .removeMouseMotionListener(this.substanceFadeRolloverListener);
 
692
                this.table.removeMouseListener(this.substanceFadeRolloverListener);
 
693
                this.substanceFadeRolloverListener = null;
 
694
 
 
695
                this.table.removeFocusListener(this.substanceFocusListener);
 
696
                this.substanceFocusListener = null;
 
697
 
 
698
                super.uninstallListeners();
 
699
        }
 
700
 
 
701
        /**
 
702
         * Paint a representation of the <code>table</code> instance that was set in
 
703
         * installUI().
 
704
         */
 
705
        @Override
 
706
        public void paint(Graphics g, JComponent c) {
 
707
                Rectangle clip = g.getClipBounds();
 
708
 
 
709
                Rectangle bounds = this.table.getBounds();
 
710
                // account for the fact that the graphics has already been translated
 
711
                // into the table's bounds
 
712
                bounds.x = bounds.y = 0;
 
713
 
 
714
                if (this.table.getRowCount() <= 0 || this.table.getColumnCount() <= 0 ||
 
715
                // this check prevents us from painting the entire table
 
716
                // when the clip doesn't intersect our bounds at all
 
717
                                !bounds.intersects(clip)) {
 
718
 
 
719
                        return;
 
720
                }
 
721
 
 
722
                Point upperLeft = clip.getLocation();
 
723
                Point lowerRight = new Point(clip.x + clip.width - 1, clip.y
 
724
                                + clip.height - 1);
 
725
                int rMin = this.table.rowAtPoint(upperLeft);
 
726
                int rMax = this.table.rowAtPoint(lowerRight);
 
727
                // This should never happen (as long as our bounds intersect the clip,
 
728
                // which is why we bail above if that is the case).
 
729
                if (rMin == -1) {
 
730
                        rMin = 0;
 
731
                }
 
732
                // If the table does not have enough rows to fill the view we'll get -1.
 
733
                // (We could also get -1 if our bounds don't intersect the clip,
 
734
                // which is why we bail above if that is the case).
 
735
                // Replace this with the index of the last row.
 
736
                if (rMax == -1) {
 
737
                        rMax = this.table.getRowCount() - 1;
 
738
                }
 
739
 
 
740
                boolean ltr = this.table.getComponentOrientation().isLeftToRight();
 
741
                int cMin = this.table.columnAtPoint(ltr ? upperLeft : lowerRight);
 
742
                int cMax = this.table.columnAtPoint(ltr ? lowerRight : upperLeft);
 
743
                // This should never happen.
 
744
                if (cMin == -1) {
 
745
                        cMin = 0;
 
746
                }
 
747
                // If the table does not have enough columns to fill the view we'll get
 
748
                // -1.
 
749
                // Replace this with the index of the last column.
 
750
                if (cMax == -1) {
 
751
                        cMax = this.table.getColumnCount() - 1;
 
752
                }
 
753
 
 
754
                // Paint the cells.
 
755
                this.paintCells(g, rMin, rMax, cMin, cMax);
 
756
 
 
757
                // Paint the grid.
 
758
                this.paintGrid(g, rMin, rMax, cMin, cMax);
 
759
 
 
760
                // Paint the drop lines
 
761
                this.paintDropLines(g);
 
762
        }
 
763
 
 
764
        /**
 
765
         * Paints the grid lines within <I>aRect</I>, using the grid color set with
 
766
         * <I>setGridColor</I>. Paints vertical lines if
 
767
         * <code>getShowVerticalLines()</code> returns true and paints horizontal
 
768
         * lines if <code>getShowHorizontalLines()</code> returns true.
 
769
         */
 
770
        protected void paintGrid(Graphics g, int rMin, int rMax, int cMin, int cMax) {
 
771
                Graphics2D g2d = (Graphics2D) g.create();
 
772
                ComponentState currState = this.table.isEnabled() ? ComponentState.ENABLED
 
773
                                : ComponentState.DISABLED_UNSELECTED;
 
774
                float alpha = SubstanceColorSchemeUtilities.getAlpha(this.table,
 
775
                                currState);
 
776
                g2d.setComposite(LafWidgetUtilities.getAlphaComposite(this.table,
 
777
                                alpha, g));
 
778
 
 
779
                Color gridColor = this.table.getGridColor();
 
780
                if (gridColor instanceof UIResource) {
 
781
                        SubstanceColorScheme scheme = SubstanceColorSchemeUtilities
 
782
                                        .getColorScheme(this.table,
 
783
                                                        ColorSchemeAssociationKind.BORDER, this.table
 
784
                                                                        .isEnabled() ? ComponentState.ENABLED
 
785
                                                                        : ComponentState.DISABLED_UNSELECTED);
 
786
                        gridColor = scheme.getLineColor();
 
787
                }
 
788
                g2d.setColor(gridColor);
 
789
 
 
790
                Rectangle minCell = this.table.getCellRect(rMin, cMin, true);
 
791
                Rectangle maxCell = this.table.getCellRect(rMax, cMax, true);
 
792
                Rectangle damagedArea = minCell.union(maxCell);
 
793
 
 
794
                float strokeWidth = SubstanceSizeUtils
 
795
                                .getBorderStrokeWidth(SubstanceSizeUtils
 
796
                                                .getComponentFontSize(this.table));
 
797
                g2d.setStroke(new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND,
 
798
                                BasicStroke.JOIN_BEVEL));
 
799
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
 
800
                                RenderingHints.VALUE_ANTIALIAS_ON);
 
801
 
 
802
                if (this.table.getShowHorizontalLines()) {
 
803
                        int tableWidth = damagedArea.x + damagedArea.width;
 
804
                        int y = damagedArea.y;
 
805
                        for (int row = rMin; row <= rMax; row++) {
 
806
                                y += this.table.getRowHeight(row);
 
807
                                g2d.drawLine(damagedArea.x, y - 1, tableWidth - 1, y - 1);
 
808
                        }
 
809
                }
 
810
                if (this.table.getShowVerticalLines()) {
 
811
                        TableColumnModel cm = this.table.getColumnModel();
 
812
                        int tableHeight = damagedArea.y + damagedArea.height;
 
813
                        int x;
 
814
                        if (this.table.getComponentOrientation().isLeftToRight()) {
 
815
                                x = damagedArea.x;
 
816
                                for (int column = cMin; column <= cMax; column++) {
 
817
                                        int w = cm.getColumn(column).getWidth();
 
818
                                        if (hasLeadingVerticalGridLine(cm, column)) {
 
819
                                                g2d.drawLine(x, 0, x, tableHeight - 1);
 
820
                                        }
 
821
                                        x += w;
 
822
                                        if (hasTrailingVerticalGridLine(cm, column)) {
 
823
                                                g2d.drawLine(x - 1, 0, x - 1, tableHeight - 1);
 
824
                                        }
 
825
                                }
 
826
                        } else {
 
827
                                x = damagedArea.x + damagedArea.width;
 
828
                                // fix for defect 196 - proper grid painting on RTL tables
 
829
                                for (int column = cMin; column <= cMax; column++) {
 
830
                                        int w = cm.getColumn(column).getWidth();
 
831
                                        if (hasLeadingVerticalGridLine(cm, column)) {
 
832
                                                g2d.drawLine(x - 1, 0, x - 1, tableHeight - 1);
 
833
                                        }
 
834
                                        x -= w;
 
835
                                        if (hasTrailingVerticalGridLine(cm, column)) {
 
836
                                                g2d.drawLine(x, 0, x, tableHeight - 1);
 
837
                                        }
 
838
                                }
 
839
                        }
 
840
                }
 
841
                g2d.dispose();
 
842
        }
 
843
 
 
844
        private boolean hasTrailingVerticalGridLine(TableColumnModel cm, int column) {
 
845
                boolean toDrawLine = (column != (cm.getColumnCount() - 1));
 
846
                if (!toDrawLine) {
 
847
            if (drawTrailingVerticalLine != null) {
 
848
                toDrawLine = drawTrailingVerticalLine;
 
849
            } else {
 
850
                                Container parent = this.table.getParent();
 
851
                        toDrawLine = (parent != null)
 
852
                        && (parent.getWidth() >= this.table.getWidth());
 
853
            }
 
854
                }
 
855
                return toDrawLine;
 
856
        }
 
857
 
 
858
        private boolean hasLeadingVerticalGridLine(TableColumnModel cm, int column) {
 
859
                if (column != 0) {
 
860
                        return false;
 
861
        }
 
862
 
 
863
        if (drawLeadingVerticalLine != null) {
 
864
            return drawLeadingVerticalLine;
 
865
        }
 
866
 
 
867
                Container parent = this.table.getParent();
 
868
                if (parent instanceof JViewport) {
 
869
                        Container grand = parent.getParent();
 
870
                        if (grand instanceof JScrollPane) {
 
871
                                return (((JScrollPane) grand).getRowHeader() != null);
 
872
                        }
 
873
                }
 
874
                return false;
 
875
        }
 
876
 
 
877
        private int viewIndexForColumn(TableColumn aColumn) {
 
878
                TableColumnModel cm = this.table.getColumnModel();
 
879
                for (int column = 0; column < cm.getColumnCount(); column++) {
 
880
                        if (cm.getColumn(column) == aColumn) {
 
881
                                return column;
 
882
                        }
 
883
                }
 
884
                return -1;
 
885
        }
 
886
 
 
887
        protected void paintCells(Graphics g, int rMin, int rMax, int cMin, int cMax) {
 
888
                JTableHeader header = this.table.getTableHeader();
 
889
                TableColumn draggedColumn = (header == null) ? null : header
 
890
                                .getDraggedColumn();
 
891
 
 
892
                TableColumnModel cm = this.table.getColumnModel();
 
893
                int columnMargin = cm.getColumnMargin();
 
894
                int rowMargin = this.table.getRowMargin();
 
895
 
 
896
                Rectangle cellRect;
 
897
                Rectangle highlightCellRect;
 
898
                TableColumn aColumn;
 
899
                int columnWidth;
 
900
                if (this.table.getComponentOrientation().isLeftToRight()) {
 
901
                        for (int row = rMin; row <= rMax; row++) {
 
902
                                cellRect = this.table.getCellRect(row, cMin, false);
 
903
 
 
904
                                highlightCellRect = new Rectangle(cellRect);
 
905
                                highlightCellRect.y -= rowMargin / 2;
 
906
                                highlightCellRect.height += rowMargin;
 
907
 
 
908
                                for (int column = cMin; column <= cMax; column++) {
 
909
                                        aColumn = cm.getColumn(column);
 
910
                                        columnWidth = aColumn.getWidth();
 
911
 
 
912
                                        cellRect.width = columnWidth - columnMargin;
 
913
                                        highlightCellRect.x = cellRect.x - columnMargin / 2;
 
914
                                        highlightCellRect.width = columnWidth;
 
915
                                        //if (!hasTrailingVerticalGridLine(cm, column)) {
 
916
                                        //      cellRect.width++;
 
917
                                        //      highlightCellRect.width++;
 
918
                                        //}
 
919
 
 
920
                                        if (aColumn != draggedColumn) {
 
921
                                                this.paintCell(g, cellRect, highlightCellRect, row,
 
922
                                                                column);
 
923
                                        }
 
924
                                        cellRect.x += columnWidth;
 
925
                                }
 
926
                        }
 
927
                } else {
 
928
                        for (int row = rMin; row <= rMax; row++) {
 
929
                                cellRect = this.table.getCellRect(row, cMin, false);
 
930
                                highlightCellRect = new Rectangle(cellRect);
 
931
                                highlightCellRect.y -= rowMargin / 2;
 
932
                                highlightCellRect.height += rowMargin;
 
933
 
 
934
                                for (int column = cMin; column <= cMax; column++) {
 
935
                                        aColumn = cm.getColumn(column);
 
936
                                        columnWidth = aColumn.getWidth();
 
937
                                        cellRect.width = columnWidth - columnMargin;
 
938
 
 
939
                                        highlightCellRect.x = cellRect.x - columnMargin / 2;
 
940
                                        highlightCellRect.width = columnWidth;
 
941
                                        if (aColumn != draggedColumn) {
 
942
                                                this.paintCell(g, cellRect, highlightCellRect, row,
 
943
                                                                column);
 
944
                                        }
 
945
                                        cellRect.x -= columnWidth;
 
946
                                }
 
947
                        }
 
948
                }
 
949
 
 
950
                // Paint the dragged column if we are dragging.
 
951
                if (draggedColumn != null) {
 
952
                        Graphics2D g2d = (Graphics2D) g.create();
 
953
                        // enhancement 331 - translucent dragged column
 
954
                        g2d.setComposite(LafWidgetUtilities.getAlphaComposite(this.table,
 
955
                                        0.65f, g));
 
956
                        this.paintDraggedArea(g2d, rMin, rMax, draggedColumn,
 
957
                                        header.getDraggedDistance());
 
958
                        g2d.dispose();
 
959
                }
 
960
 
 
961
                // Remove any renderers that may be left in the rendererPane.
 
962
                this.rendererPane.removeAll();
 
963
        }
 
964
 
 
965
        protected void paintDraggedArea(Graphics g, int rMin, int rMax,
 
966
                        TableColumn draggedColumn, int distance) {
 
967
                int draggedColumnIndex = this.viewIndexForColumn(draggedColumn);
 
968
 
 
969
                Rectangle minCell = this.table.getCellRect(rMin, draggedColumnIndex,
 
970
                                true);
 
971
                Rectangle maxCell = this.table.getCellRect(rMax, draggedColumnIndex,
 
972
                                true);
 
973
 
 
974
                Rectangle vacatedColumnRect = minCell.union(maxCell);
 
975
 
 
976
                // Paint a gray well in place of the moving column.
 
977
                g.setColor(this.table.getParent().getBackground());
 
978
                g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
 
979
                                vacatedColumnRect.width, vacatedColumnRect.height);
 
980
 
 
981
                // Move to the where the cell has been dragged.
 
982
                vacatedColumnRect.x += distance;
 
983
 
 
984
                // Fill the background.
 
985
                g.setColor(this.table.getBackground());
 
986
                g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
 
987
                                vacatedColumnRect.width, vacatedColumnRect.height);
 
988
 
 
989
                // Paint the vertical grid lines if necessary.
 
990
                if (this.table.getShowVerticalLines()) {
 
991
                        g.setColor(this.table.getGridColor());
 
992
                        int x1 = vacatedColumnRect.x;
 
993
                        int y1 = vacatedColumnRect.y;
 
994
                        int x2 = x1 + vacatedColumnRect.width - 1;
 
995
                        int y2 = y1 + vacatedColumnRect.height - 1;
 
996
                        // Left
 
997
                        g.drawLine(x1 - 1, y1, x1 - 1, y2);
 
998
                        // Right
 
999
                        g.drawLine(x2, y1, x2, y2);
 
1000
                }
 
1001
 
 
1002
                for (int row = rMin; row <= rMax; row++) {
 
1003
                        // Render the cell value
 
1004
                        Rectangle r = this.table
 
1005
                                        .getCellRect(row, draggedColumnIndex, false);
 
1006
                        r.x += distance;
 
1007
                        this.paintCell(g, r, r, row, draggedColumnIndex);
 
1008
 
 
1009
                        // Paint the (lower) horizontal grid line if necessary.
 
1010
                        if (this.table.getShowHorizontalLines()) {
 
1011
                                g.setColor(this.table.getGridColor());
 
1012
                                Rectangle rcr = this.table.getCellRect(row, draggedColumnIndex,
 
1013
                                                true);
 
1014
                                rcr.x += distance;
 
1015
                                int x1 = rcr.x;
 
1016
                                int y1 = rcr.y;
 
1017
                                int x2 = x1 + rcr.width - 1;
 
1018
                                int y2 = y1 + rcr.height - 1;
 
1019
                                g.drawLine(x1, y2, x2, y2);
 
1020
                        }
 
1021
                }
 
1022
        }
 
1023
 
 
1024
        protected void paintCell(Graphics g, Rectangle cellRect,
 
1025
                        Rectangle highlightCellRect, int row, int column) {
 
1026
                // System.out.println("Painting " + row + ":" + column);
 
1027
                Component rendererComponent = null;
 
1028
                if (!this.table.isEditing() || this.table.getEditingRow() != row
 
1029
                                || this.table.getEditingColumn() != column) {
 
1030
                        TableCellRenderer renderer = this.table
 
1031
                                        .getCellRenderer(row, column);
 
1032
                        boolean isSubstanceRenderer = isSubstanceDefaultRenderer(renderer);
 
1033
                        rendererComponent = this.table.prepareRenderer(renderer, row,
 
1034
                                        column);
 
1035
                        boolean isSubstanceRendererComponent = isSubstanceDefaultRenderer(rendererComponent);
 
1036
                        if (isSubstanceRenderer && !isSubstanceRendererComponent) {
 
1037
                if (!Boolean.getBoolean("insubstantial.looseTableCellRenderers")) {
 
1038
                    throw new IllegalArgumentException(
 
1039
                            "Renderer extends the SubstanceDefaultTableCellRenderer but does not return one in its getTableCellRendererComponent() method");
 
1040
                }
 
1041
                        }
 
1042
 
 
1043
                        if (!isSubstanceRenderer) {
 
1044
                                rendererPane.paintComponent(g, rendererComponent, table,
 
1045
                                                cellRect.x, cellRect.y, cellRect.width,
 
1046
                                                cellRect.height, true);
 
1047
                                return;
 
1048
                        }
 
1049
                }
 
1050
 
 
1051
                Graphics2D g2d = (Graphics2D) g.create();
 
1052
                // fix for issue 183 - passing the original Graphics context
 
1053
                // to compute the alpha composite. If the table is in a JXPanel
 
1054
                // (component from SwingX) and it has custom alpha value set,
 
1055
                // then the original graphics context will have a SRC_OVER
 
1056
                // alpha composite applied to it.
 
1057
                g2d.setComposite(LafWidgetUtilities.getAlphaComposite(this.table, g));
 
1058
 
 
1059
                TableCellId cellId = new TableCellId(row, column);
 
1060
 
 
1061
                StateTransitionTracker.ModelStateInfo modelStateInfo = this
 
1062
                                .getModelStateInfo(cellId);
 
1063
                Map<ComponentState, StateTransitionTracker.StateContributionInfo> activeStates = ((modelStateInfo == null) ? null
 
1064
                                : modelStateInfo.getStateContributionMap());
 
1065
                // optimize for tables that don't initiate rollover
 
1066
                // or selection animations
 
1067
                if (!updateInfo.hasRolloverAnimations
 
1068
                                && !updateInfo.hasSelectionAnimations)
 
1069
                        activeStates = null;
 
1070
                ComponentState currState = ((modelStateInfo == null) ? this
 
1071
                                .getCellState(cellId) : modelStateInfo.getCurrModelState());
 
1072
 
 
1073
                boolean hasHighlights = (currState != ComponentState.ENABLED)
 
1074
                                || (activeStates != null);
 
1075
                if (activeStates != null) {
 
1076
                        for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> stateEntry : activeStates
 
1077
                                        .entrySet()) {
 
1078
                                hasHighlights = (this.updateInfo.getHighlightAlpha(stateEntry
 
1079
                                                .getKey()) * stateEntry.getValue().getContribution() > 0.0f);
 
1080
                                if (hasHighlights)
 
1081
                                        break;
 
1082
                        }
 
1083
                } else {
 
1084
                        hasHighlights = (this.updateInfo.getHighlightAlpha(currState) > 0.0f);
 
1085
                }
 
1086
 
 
1087
        Object highlightProperty = table.getClientProperty("substancelaf.highlightCells");
 
1088
        hasHighlights = (highlightProperty instanceof Boolean) ? (Boolean)highlightProperty && hasHighlights : hasHighlights;
 
1089
 
 
1090
                Set<SubstanceConstants.Side> highlightOpenSides = null;
 
1091
                float highlightBorderAlpha = 0.0f;
 
1092
 
 
1093
                if (hasHighlights) {
 
1094
                        // compute the highlight visuals, but only if there are
 
1095
                        // highlights on this cell (optimization)
 
1096
                        highlightOpenSides = EnumSet.noneOf(Side.class);
 
1097
                        // show highlight border only when the table grid is not shown
 
1098
                        highlightBorderAlpha = (table.getShowHorizontalLines() || table
 
1099
                                        .getShowVerticalLines()) ? 0.0f : 0.8f;
 
1100
                        if (!table.getColumnSelectionAllowed()
 
1101
                                        && table.getRowSelectionAllowed()) {
 
1102
                                // if row selection is on and column selection is off, we
 
1103
                                // will show the highlight for the entire row
 
1104
 
 
1105
                                // all cells have open left side
 
1106
                                highlightOpenSides.add(SubstanceConstants.Side.LEFT);
 
1107
                                // all cells have open right side
 
1108
                                highlightOpenSides.add(SubstanceConstants.Side.RIGHT);
 
1109
                        }
 
1110
                        if (table.getColumnSelectionAllowed()
 
1111
                                        && !table.getRowSelectionAllowed()) {
 
1112
                                // if row selection is off and column selection is on, we
 
1113
                                // will show the highlight for the entire column
 
1114
 
 
1115
                                // the top side is open for all rows except the
 
1116
                                // first, or when the table header is visible
 
1117
                                highlightOpenSides.add(SubstanceConstants.Side.TOP);
 
1118
                                // all cells but the last have open bottom side
 
1119
                                highlightOpenSides.add(SubstanceConstants.Side.BOTTOM);
 
1120
                        }
 
1121
                        if (row > 1) {
 
1122
                                ComponentState upperNeighbourState = this
 
1123
                                                .getCellState(new TableCellId(row - 1, column));
 
1124
                                if (currState == upperNeighbourState) {
 
1125
                                        // the cell above it is in the same state
 
1126
                                        highlightOpenSides.add(SubstanceConstants.Side.TOP);
 
1127
                                }
 
1128
                        }
 
1129
                        if (column > 1) {
 
1130
                                ComponentState leftNeighbourState = this
 
1131
                                                .getCellState(new TableCellId(row, column - 1));
 
1132
                                if (currState == leftNeighbourState) {
 
1133
                                        // the cell to the left is in the same state
 
1134
                                        highlightOpenSides.add(SubstanceConstants.Side.LEFT);
 
1135
                                }
 
1136
                        }
 
1137
                        if (row == 0) {
 
1138
                                highlightOpenSides.add(SubstanceConstants.Side.TOP);
 
1139
                        }
 
1140
                        if (row == (table.getRowCount() - 1)) {
 
1141
                                highlightOpenSides.add(SubstanceConstants.Side.BOTTOM);
 
1142
                        }
 
1143
                        if (column == 0) {
 
1144
                                highlightOpenSides.add(SubstanceConstants.Side.LEFT);
 
1145
                        }
 
1146
                        if (column == (table.getColumnCount() - 1)) {
 
1147
                                highlightOpenSides.add(SubstanceConstants.Side.RIGHT);
 
1148
                        }
 
1149
                }
 
1150
 
 
1151
                boolean isRollover = this.rolledOverIndices.contains(cellId);
 
1152
                if (this.table.isEditing() && this.table.getEditingRow() == row
 
1153
                                && this.table.getEditingColumn() == column) {
 
1154
                        Component component = this.table.getEditorComponent();
 
1155
                        component.applyComponentOrientation(this.table
 
1156
                                        .getComponentOrientation());
 
1157
 
 
1158
                        if (hasHighlights) {
 
1159
                                float extra = SubstanceSizeUtils
 
1160
                                                .getBorderStrokeWidth(SubstanceSizeUtils
 
1161
                                                                .getComponentFontSize(this.table
 
1162
                                                                                .getTableHeader()));
 
1163
                                float extraWidth = highlightOpenSides
 
1164
                                                .contains(SubstanceConstants.Side.LEFT) ? 0.0f : extra;
 
1165
                                float extraHeight = highlightOpenSides
 
1166
                                                .contains(SubstanceConstants.Side.TOP) ? 0.0f : extra;
 
1167
                                Rectangle highlightRect = new Rectangle(highlightCellRect.x
 
1168
                                                - (int) extraWidth, highlightCellRect.y
 
1169
                                                - (int) extraHeight, highlightCellRect.width
 
1170
                                                + (int) extraWidth, highlightCellRect.height
 
1171
                                                + (int) extraHeight);
 
1172
                                if (activeStates == null) {
 
1173
                                        float alpha = this.updateInfo.getHighlightAlpha(currState);
 
1174
                                        if (alpha > 0.0f) {
 
1175
                                                SubstanceColorScheme fillScheme = this.updateInfo
 
1176
                                                                .getHighlightColorScheme(currState);
 
1177
                                                SubstanceColorScheme borderScheme = this.updateInfo
 
1178
                                                                .getHighlightBorderColorScheme(currState);
 
1179
                                                g2d.setComposite(LafWidgetUtilities.getAlphaComposite(
 
1180
                                                                this.table, alpha, g));
 
1181
                                                HighlightPainterUtils.paintHighlight(g2d,
 
1182
                                                                this.rendererPane, component, highlightRect,
 
1183
                                                                highlightBorderAlpha, highlightOpenSides,
 
1184
                                                                fillScheme, borderScheme);
 
1185
                                                g2d.setComposite(LafWidgetUtilities.getAlphaComposite(
 
1186
                                                                this.table, g));
 
1187
                                        }
 
1188
                                } else {
 
1189
                                        for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> stateEntry : activeStates
 
1190
                                                        .entrySet()) {
 
1191
                                                ComponentState activeState = stateEntry.getKey();
 
1192
                                                float alpha = this.updateInfo
 
1193
                                                                .getHighlightAlpha(activeState)
 
1194
                                                                * stateEntry.getValue().getContribution();
 
1195
                                                if (alpha == 0.0f)
 
1196
                                                        continue;
 
1197
                                                SubstanceColorScheme fillScheme = this.updateInfo
 
1198
                                                                .getHighlightColorScheme(activeState);
 
1199
                                                SubstanceColorScheme borderScheme = this.updateInfo
 
1200
                                                                .getHighlightBorderColorScheme(activeState);
 
1201
                                                g2d.setComposite(LafWidgetUtilities.getAlphaComposite(
 
1202
                                                                this.table, alpha, g));
 
1203
                                                HighlightPainterUtils.paintHighlight(g2d,
 
1204
                                                                this.rendererPane, component, highlightRect,
 
1205
                                                                highlightBorderAlpha, highlightOpenSides,
 
1206
                                                                fillScheme, borderScheme);
 
1207
                                                g2d.setComposite(LafWidgetUtilities.getAlphaComposite(
 
1208
                                                                this.table, g));
 
1209
                                        }
 
1210
                                }
 
1211
                        }
 
1212
 
 
1213
                        component.setBounds(cellRect);
 
1214
                        component.validate();
 
1215
                } else {
 
1216
                        boolean isWatermarkBleed = this.updateInfo.toDrawWatermark;
 
1217
                        if (rendererComponent != null) {
 
1218
                                if (!isWatermarkBleed) {
 
1219
                                        Color background = rendererComponent.getBackground();
 
1220
                                        // optimization - only render background if it's different
 
1221
                                        // from the table background
 
1222
                                        if ((background != null)
 
1223
                                                        && (!table.getBackground().equals(background) || this.updateInfo.isInDecorationArea)) {
 
1224
                                                // fill with the renderer background color
 
1225
                                                g2d.setColor(background);
 
1226
                                                g2d.fillRect(highlightCellRect.x, highlightCellRect.y,
 
1227
                                                                highlightCellRect.width,
 
1228
                                                                highlightCellRect.height);
 
1229
                                        }
 
1230
                                } else {
 
1231
                                        BackgroundPaintingUtils.fillAndWatermark(g2d, this.table,
 
1232
                                                        rendererComponent.getBackground(),
 
1233
                                                        highlightCellRect);
 
1234
                                }
 
1235
                        }
 
1236
 
 
1237
                        if (hasHighlights) {
 
1238
                                JTable.DropLocation dropLocation = table.getDropLocation();
 
1239
                                if (dropLocation != null && !dropLocation.isInsertRow()
 
1240
                                                && !dropLocation.isInsertColumn()
 
1241
                                                && dropLocation.getRow() == row
 
1242
                                                && dropLocation.getColumn() == column) {
 
1243
                                        // mark drop location
 
1244
                                        SubstanceColorScheme scheme = SubstanceColorSchemeUtilities
 
1245
                                                        .getColorScheme(table,
 
1246
                                                                        ColorSchemeAssociationKind.TEXT_HIGHLIGHT,
 
1247
                                                                        currState);
 
1248
                                        SubstanceColorScheme borderScheme = SubstanceColorSchemeUtilities
 
1249
                                                        .getColorScheme(table,
 
1250
                                                                        ColorSchemeAssociationKind.BORDER,
 
1251
                                                                        currState);
 
1252
                                        float extra = SubstanceSizeUtils
 
1253
                                                        .getBorderStrokeWidth(SubstanceSizeUtils
 
1254
                                                                        .getComponentFontSize(this.table
 
1255
                                                                                        .getTableHeader()));
 
1256
                                        HighlightPainterUtils.paintHighlight(g2d,
 
1257
                                                        this.rendererPane, rendererComponent,
 
1258
                                                        new Rectangle(highlightCellRect.x - (int) extra,
 
1259
                                                                        highlightCellRect.y - (int) extra,
 
1260
                                                                        highlightCellRect.width + (int) extra,
 
1261
                                                                        highlightCellRect.height + (int) extra),
 
1262
                                                        0.8f, null, scheme, borderScheme);
 
1263
                                } else {
 
1264
                                        float extra = SubstanceSizeUtils
 
1265
                                                        .getBorderStrokeWidth(SubstanceSizeUtils
 
1266
                                                                        .getComponentFontSize(this.table
 
1267
                                                                                        .getTableHeader()));
 
1268
                                        float extraWidth = highlightOpenSides
 
1269
                                                        .contains(SubstanceConstants.Side.LEFT) ? 0.0f
 
1270
                                                        : extra;
 
1271
                                        float extraHeight = highlightOpenSides
 
1272
                                                        .contains(SubstanceConstants.Side.TOP) ? 0.0f
 
1273
                                                        : extra;
 
1274
                                        Rectangle highlightRect = new Rectangle(highlightCellRect.x
 
1275
                                                        - (int) extraWidth, highlightCellRect.y
 
1276
                                                        - (int) extraHeight, highlightCellRect.width
 
1277
                                                        + (int) extraWidth, highlightCellRect.height
 
1278
                                                        + (int) extraHeight);
 
1279
                                        if (activeStates == null) {
 
1280
                                                SubstanceColorScheme fillScheme = this.updateInfo
 
1281
                                                                .getHighlightColorScheme(currState);
 
1282
                                                SubstanceColorScheme borderScheme = this.updateInfo
 
1283
                                                                .getHighlightBorderColorScheme(currState);
 
1284
                                                float alpha = this.updateInfo
 
1285
                                                                .getHighlightAlpha(currState);
 
1286
                                                if (alpha > 0.0f) {
 
1287
                                                        g2d.setComposite(LafWidgetUtilities
 
1288
                                                                        .getAlphaComposite(this.table, alpha, g));
 
1289
                                                        HighlightPainterUtils.paintHighlight(g2d,
 
1290
                                                                        this.rendererPane, rendererComponent,
 
1291
                                                                        highlightRect, highlightBorderAlpha,
 
1292
                                                                        highlightOpenSides, fillScheme,
 
1293
                                                                        borderScheme);
 
1294
                                                        g2d.setComposite(LafWidgetUtilities
 
1295
                                                                        .getAlphaComposite(this.table, g));
 
1296
                                                }
 
1297
                                        } else {
 
1298
                                                for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> stateEntry : activeStates
 
1299
                                                                .entrySet()) {
 
1300
                                                        ComponentState activeState = stateEntry.getKey();
 
1301
                                                        SubstanceColorScheme fillScheme = this.updateInfo
 
1302
                                                                        .getHighlightColorScheme(activeState);
 
1303
                                                        SubstanceColorScheme borderScheme = this.updateInfo
 
1304
                                                                        .getHighlightBorderColorScheme(activeState);
 
1305
                                                        float alpha = this.updateInfo
 
1306
                                                                        .getHighlightAlpha(activeState)
 
1307
                                                                        * stateEntry.getValue().getContribution();
 
1308
                                                        if (alpha > 0.0f) {
 
1309
                                                                g2d.setComposite(LafWidgetUtilities
 
1310
                                                                                .getAlphaComposite(this.table, alpha, g));
 
1311
                                                                HighlightPainterUtils.paintHighlight(g2d,
 
1312
                                                                                this.rendererPane, rendererComponent,
 
1313
                                                                                highlightRect, highlightBorderAlpha,
 
1314
                                                                                highlightOpenSides, fillScheme,
 
1315
                                                                                borderScheme);
 
1316
                                                                g2d.setComposite(LafWidgetUtilities
 
1317
                                                                                .getAlphaComposite(this.table, g));
 
1318
                                                        }
 
1319
                                                }
 
1320
                                        }
 
1321
                                }
 
1322
                        }
 
1323
 
 
1324
                        rendererComponent.applyComponentOrientation(this.table
 
1325
                                        .getComponentOrientation());
 
1326
                        if (rendererComponent instanceof JComponent) {
 
1327
                                // Play with opacity to make our own gradient background
 
1328
                                // on selected elements to show.
 
1329
                                JComponent jRenderer = (JComponent) rendererComponent;
 
1330
                                // Compute the selection status to prevent flicker - JTable
 
1331
                                // registers a listener on selection changes and repaints
 
1332
                                // the relevant cell before our listener (in TableUI) gets
 
1333
                                // the chance to start the fade sequence. The result is that
 
1334
                                // the first frame uses full opacity, and the next frame
 
1335
                                // starts the fade sequence. So, we use the UI delegate to
 
1336
                                // compute the selection status.
 
1337
                                boolean isSelected = updateInfo.hasSelectionAnimations ? this.selectedIndices
 
1338
                                                .containsKey(cellId) : this.table.isCellSelected(row,
 
1339
                                                column);
 
1340
                                boolean newOpaque = !(isSelected || isRollover || hasHighlights);
 
1341
 
 
1342
                                if (this.updateInfo.toDrawWatermark)
 
1343
                                        newOpaque = false;
 
1344
 
 
1345
                                Map<Component, Boolean> opacity = new HashMap<Component, Boolean>();
 
1346
                                if (!newOpaque)
 
1347
                                        SubstanceCoreUtilities.makeNonOpaque(jRenderer, opacity);
 
1348
                                this.rendererPane.paintComponent(g2d, rendererComponent,
 
1349
                                                this.table, cellRect.x, cellRect.y, cellRect.width,
 
1350
                                                cellRect.height, true);
 
1351
                                if (!newOpaque)
 
1352
                                        SubstanceCoreUtilities.restoreOpaque(jRenderer, opacity);
 
1353
                        } else {
 
1354
                                this.rendererPane.paintComponent(g2d, rendererComponent,
 
1355
                                                this.table, cellRect.x, cellRect.y, cellRect.width,
 
1356
                                                cellRect.height, true);
 
1357
                        }
 
1358
                }
 
1359
                g2d.dispose();
 
1360
        }
 
1361
 
 
1362
        protected void paintDropLines(Graphics g) {
 
1363
                JTable.DropLocation loc = table.getDropLocation();
 
1364
                if (loc == null) {
 
1365
                        return;
 
1366
                }
 
1367
 
 
1368
                Color color = UIManager.getColor("Table.dropLineColor");
 
1369
                Color shortColor = UIManager.getColor("Table.dropLineShortColor");
 
1370
                if (color == null && shortColor == null) {
 
1371
                        return;
 
1372
                }
 
1373
 
 
1374
                Rectangle rect;
 
1375
 
 
1376
                rect = getHDropLineRect(loc);
 
1377
                if (rect != null) {
 
1378
                        int x = rect.x;
 
1379
                        int w = rect.width;
 
1380
                        if (color != null) {
 
1381
                                extendRect(rect, true);
 
1382
                                g.setColor(color);
 
1383
                                g.fillRect(rect.x, rect.y, rect.width, rect.height);
 
1384
                        }
 
1385
                        if (!loc.isInsertColumn() && shortColor != null) {
 
1386
                                g.setColor(shortColor);
 
1387
                                g.fillRect(x, rect.y, w, rect.height);
 
1388
                        }
 
1389
                }
 
1390
 
 
1391
                rect = getVDropLineRect(loc);
 
1392
                if (rect != null) {
 
1393
                        int y = rect.y;
 
1394
                        int h = rect.height;
 
1395
                        if (color != null) {
 
1396
                                extendRect(rect, false);
 
1397
                                g.setColor(color);
 
1398
                                g.fillRect(rect.x, rect.y, rect.width, rect.height);
 
1399
                        }
 
1400
                        if (!loc.isInsertRow() && shortColor != null) {
 
1401
                                g.setColor(shortColor);
 
1402
                                g.fillRect(rect.x, y, rect.width, h);
 
1403
                        }
 
1404
                }
 
1405
        }
 
1406
 
 
1407
        private Rectangle getHDropLineRect(JTable.DropLocation loc) {
 
1408
                if (!loc.isInsertRow()) {
 
1409
                        return null;
 
1410
                }
 
1411
 
 
1412
                int row = loc.getRow();
 
1413
                int col = loc.getColumn();
 
1414
                if (col >= table.getColumnCount()) {
 
1415
                        col--;
 
1416
                }
 
1417
 
 
1418
                Rectangle rect = table.getCellRect(row, col, true);
 
1419
 
 
1420
                if (row >= table.getRowCount()) {
 
1421
                        row--;
 
1422
                        Rectangle prevRect = table.getCellRect(row, col, true);
 
1423
                        rect.y = prevRect.y + prevRect.height;
 
1424
                }
 
1425
 
 
1426
                if (rect.y == 0) {
 
1427
                        rect.y = -1;
 
1428
                } else {
 
1429
                        rect.y -= 2;
 
1430
                }
 
1431
 
 
1432
                rect.height = 3;
 
1433
 
 
1434
                return rect;
 
1435
        }
 
1436
 
 
1437
        private Rectangle getVDropLineRect(JTable.DropLocation loc) {
 
1438
                if (!loc.isInsertColumn()) {
 
1439
                        return null;
 
1440
                }
 
1441
 
 
1442
                boolean ltr = table.getComponentOrientation().isLeftToRight();
 
1443
                int col = loc.getColumn();
 
1444
                Rectangle rect = table.getCellRect(loc.getRow(), col, true);
 
1445
 
 
1446
                if (col >= table.getColumnCount()) {
 
1447
                        col--;
 
1448
                        rect = table.getCellRect(loc.getRow(), col, true);
 
1449
                        if (ltr) {
 
1450
                                rect.x = rect.x + rect.width;
 
1451
                        }
 
1452
                } else if (!ltr) {
 
1453
                        rect.x = rect.x + rect.width;
 
1454
                }
 
1455
 
 
1456
                if (rect.x == 0) {
 
1457
                        rect.x = -1;
 
1458
                } else {
 
1459
                        rect.x -= 2;
 
1460
                }
 
1461
 
 
1462
                rect.width = 3;
 
1463
 
 
1464
                return rect;
 
1465
        }
 
1466
 
 
1467
        private Rectangle extendRect(Rectangle rect, boolean horizontal) {
 
1468
                if (rect == null) {
 
1469
                        return rect;
 
1470
                }
 
1471
 
 
1472
                if (horizontal) {
 
1473
                        rect.x = 0;
 
1474
                        rect.width = table.getWidth();
 
1475
                } else {
 
1476
                        rect.y = 0;
 
1477
 
 
1478
                        if (table.getRowCount() != 0) {
 
1479
                                Rectangle lastRect = table.getCellRect(table.getRowCount() - 1,
 
1480
                                                0, true);
 
1481
                                rect.height = lastRect.y + lastRect.height;
 
1482
                        } else {
 
1483
                                rect.height = table.getHeight();
 
1484
                        }
 
1485
                }
 
1486
 
 
1487
                return rect;
 
1488
        }
 
1489
 
 
1490
        /**
 
1491
         * Repaints a single cell during the fade animation cycle.
 
1492
         * 
 
1493
         * @author Kirill Grouchnikov
 
1494
         */
 
1495
        protected class CellRepaintCallback extends UIThreadTimelineCallbackAdapter {
 
1496
                /**
 
1497
                 * Associated table.
 
1498
                 */
 
1499
                protected JTable table;
 
1500
 
 
1501
                /**
 
1502
                 * Associated (animated) row index.
 
1503
                 */
 
1504
                protected int rowIndex;
 
1505
 
 
1506
                /**
 
1507
                 * Associated (animated) column index.
 
1508
                 */
 
1509
                protected int columnIndex;
 
1510
 
 
1511
                /**
 
1512
                 * Creates a new animation repaint callback.
 
1513
                 * 
 
1514
                 * @param table
 
1515
                 *            Associated table.
 
1516
                 * @param rowIndex
 
1517
                 *            Associated (animated) row index.
 
1518
                 * @param columnIndex
 
1519
                 *            Associated (animated) column index.
 
1520
                 */
 
1521
                public CellRepaintCallback(JTable table, int rowIndex, int columnIndex) {
 
1522
                        super();
 
1523
                        this.table = table;
 
1524
                        this.rowIndex = rowIndex;
 
1525
                        this.columnIndex = columnIndex;
 
1526
                }
 
1527
 
 
1528
                @Override
 
1529
                public void onTimelinePulse(float durationFraction,
 
1530
                                float timelinePosition) {
 
1531
                        this.repaintCell();
 
1532
                }
 
1533
 
 
1534
                @Override
 
1535
                public void onTimelineStateChanged(TimelineState oldState,
 
1536
                                TimelineState newState, float durationFraction,
 
1537
                                float timelinePosition) {
 
1538
                        this.repaintCell();
 
1539
                }
 
1540
 
 
1541
                /**
 
1542
                 * Repaints the associated cell.
 
1543
                 */
 
1544
                private void repaintCell() {
 
1545
                        SwingUtilities.invokeLater(new Runnable() {
 
1546
                                @Override
 
1547
                public void run() {
 
1548
                                        if (SubstanceTableUI.this.table == null) {
 
1549
                                                // may happen if the LAF was switched in the meantime
 
1550
                                                return;
 
1551
                                        }
 
1552
                                        int rowCount = table.getRowCount();
 
1553
                                        int colCount = table.getColumnCount();
 
1554
                                        if ((rowCount > 0) && (rowIndex < rowCount)
 
1555
                                                        && (colCount > 0) && (columnIndex < colCount)) {
 
1556
                                                // need to retrieve the cell rectangle since the cells
 
1557
                                                // can be moved while animating
 
1558
                                                Rectangle rect = getCellRectangleForRepaint(rowIndex,
 
1559
                                                                columnIndex);
 
1560
                                                // System.out.println("Cell Repainting " + rowIndex +
 
1561
                                                // ":"
 
1562
                                                // + columnIndex + ":" + rect);
 
1563
                                                CellRepaintCallback.this.table.repaint(rect);
 
1564
                                        }
 
1565
                                }
 
1566
                        });
 
1567
                }
 
1568
        }
 
1569
 
 
1570
        /**
 
1571
         * Repaints a single row during the fade animation cycle.
 
1572
         * 
 
1573
         * @author Kirill Grouchnikov
 
1574
         */
 
1575
        protected class RowRepaintCallback extends UIThreadTimelineCallbackAdapter {
 
1576
                /**
 
1577
                 * Associated table.
 
1578
                 */
 
1579
                protected JTable table;
 
1580
 
 
1581
                /**
 
1582
                 * Associated (animated) row index.
 
1583
                 */
 
1584
                protected int rowIndex;
 
1585
 
 
1586
                /**
 
1587
                 * Creates a new animation repaint callback.
 
1588
                 * 
 
1589
                 * @param table
 
1590
                 *            Associated table.
 
1591
                 * @param rowIndex
 
1592
                 *            Associated (animated) row index.
 
1593
                 */
 
1594
                public RowRepaintCallback(JTable table, int rowIndex) {
 
1595
                        super();
 
1596
                        this.table = table;
 
1597
                        this.rowIndex = rowIndex;
 
1598
                }
 
1599
 
 
1600
                @Override
 
1601
                public void onTimelinePulse(float durationFraction,
 
1602
                                float timelinePosition) {
 
1603
                        this.repaintRow();
 
1604
                }
 
1605
 
 
1606
                @Override
 
1607
                public void onTimelineStateChanged(TimelineState oldState,
 
1608
                                TimelineState newState, float durationFraction,
 
1609
                                float timelinePosition) {
 
1610
                        this.repaintRow();
 
1611
                }
 
1612
 
 
1613
                /**
 
1614
                 * Repaints the associated row.
 
1615
                 */
 
1616
                private void repaintRow() {
 
1617
                        SwingUtilities.invokeLater(new Runnable() {
 
1618
                                @Override
 
1619
                public void run() {
 
1620
                                        if (SubstanceTableUI.this.table == null) {
 
1621
                                                // may happen if the LAF was switched in the meantime
 
1622
                                                return;
 
1623
                                        }
 
1624
                                        int rowCount = RowRepaintCallback.this.table.getRowCount();
 
1625
                                        if ((rowCount > 0)
 
1626
                                                        && (RowRepaintCallback.this.rowIndex < rowCount)) {
 
1627
                                                // need to retrieve the cell rectangle since the cells
 
1628
                                                // can be moved while animating
 
1629
                                                Rectangle rect = RowRepaintCallback.this.table
 
1630
                                                                .getCellRect(RowRepaintCallback.this.rowIndex,
 
1631
                                                                                0, true);
 
1632
                                                for (int i = 1; i < RowRepaintCallback.this.table
 
1633
                                                                .getColumnCount(); i++) {
 
1634
                                                        rect = rect.union(RowRepaintCallback.this.table
 
1635
                                                                        .getCellRect(
 
1636
                                                                                        RowRepaintCallback.this.rowIndex,
 
1637
                                                                                        i, true));
 
1638
                                                }
 
1639
                                                if (!table.getShowHorizontalLines()
 
1640
                                                                && !table.getShowVerticalLines()) {
 
1641
                                                        float extra = SubstanceSizeUtils
 
1642
                                                                        .getBorderStrokeWidth(SubstanceSizeUtils
 
1643
                                                                                        .getComponentFontSize(table
 
1644
                                                                                                        .getTableHeader()));
 
1645
                                                        rect.y -= (int) extra;
 
1646
                                                        rect.height += 2 * (int) extra;
 
1647
                                                }
 
1648
                                                // System.out.println("Repainting row " + rowIndex
 
1649
                                                // + " at " + rect);
 
1650
                                                RowRepaintCallback.this.table.repaint(rect);
 
1651
                                        }
 
1652
                                }
 
1653
                        });
 
1654
                }
 
1655
        }
 
1656
 
 
1657
        /**
 
1658
         * Repaints a single column during the fade animation cycle.
 
1659
         * 
 
1660
         * @author Kirill Grouchnikov
 
1661
         */
 
1662
        protected class ColumnRepaintCallback extends
 
1663
                        UIThreadTimelineCallbackAdapter {
 
1664
                /**
 
1665
                 * Associated table.
 
1666
                 */
 
1667
                protected JTable table;
 
1668
 
 
1669
                /**
 
1670
                 * Associated (animated) column index.
 
1671
                 */
 
1672
                protected int columnIndex;
 
1673
 
 
1674
                /**
 
1675
                 * Creates a new animation repaint callback.
 
1676
                 * 
 
1677
                 * @param table
 
1678
                 *            Associated table.
 
1679
                 * @param columnIndex
 
1680
                 *            Associated (animated) column index.
 
1681
                 */
 
1682
                public ColumnRepaintCallback(JTable table, int columnIndex) {
 
1683
                        super();
 
1684
                        this.table = table;
 
1685
                        this.columnIndex = columnIndex;
 
1686
                }
 
1687
 
 
1688
                @Override
 
1689
                public void onTimelinePulse(float durationFraction,
 
1690
                                float timelinePosition) {
 
1691
                        this.repaintColumn();
 
1692
                }
 
1693
 
 
1694
                @Override
 
1695
                public void onTimelineStateChanged(TimelineState oldState,
 
1696
                                TimelineState newState, float durationFraction,
 
1697
                                float timelinePosition) {
 
1698
                        this.repaintColumn();
 
1699
                }
 
1700
 
 
1701
                /**
 
1702
                 * Repaints the associated row.
 
1703
                 */
 
1704
                private void repaintColumn() {
 
1705
                        SwingUtilities.invokeLater(new Runnable() {
 
1706
                                @Override
 
1707
                public void run() {
 
1708
                                        if (SubstanceTableUI.this.table == null) {
 
1709
                                                // may happen if the LAF was switched in the meantime
 
1710
                                                return;
 
1711
                                        }
 
1712
                                        int columnCount = ColumnRepaintCallback.this.table
 
1713
                                                        .getColumnCount();
 
1714
                                        if ((columnCount > 0)
 
1715
                                                        && (ColumnRepaintCallback.this.columnIndex < columnCount)) {
 
1716
                                                // need to retrieve the cell rectangle since the cells
 
1717
                                                // can be moved while animating
 
1718
                                                Rectangle rect = ColumnRepaintCallback.this.table
 
1719
                                                                .getCellRect(0,
 
1720
                                                                                ColumnRepaintCallback.this.columnIndex,
 
1721
                                                                                true);
 
1722
                                                for (int i = 1; i < ColumnRepaintCallback.this.table
 
1723
                                                                .getRowCount(); i++) {
 
1724
                                                        rect = rect.union(ColumnRepaintCallback.this.table
 
1725
                                                                        .getCellRect(
 
1726
                                                                                        i,
 
1727
                                                                                        ColumnRepaintCallback.this.columnIndex,
 
1728
                                                                                        true));
 
1729
                                                }
 
1730
                                                if (!table.getShowHorizontalLines()
 
1731
                                                                && !table.getShowVerticalLines()) {
 
1732
                                                        float extra = SubstanceSizeUtils
 
1733
                                                                        .getBorderStrokeWidth(SubstanceSizeUtils
 
1734
                                                                                        .getComponentFontSize(table
 
1735
                                                                                                        .getTableHeader()));
 
1736
                                                        rect.x -= (int) extra;
 
1737
                                                        rect.width += 2 * (int) extra;
 
1738
                                                }
 
1739
                                                ColumnRepaintCallback.this.table.repaint(rect);
 
1740
                                        }
 
1741
                                }
 
1742
                        });
 
1743
                }
 
1744
        }
 
1745
 
 
1746
        /**
 
1747
         * ID of a single table cell.
 
1748
         * 
 
1749
         * @author Kirill Grouchnikov
 
1750
         */
 
1751
        public static class TableCellId implements Comparable<TableCellId> {
 
1752
                /**
 
1753
                 * Cell row.
 
1754
                 */
 
1755
                protected int row;
 
1756
 
 
1757
                /**
 
1758
                 * Cell column.
 
1759
                 */
 
1760
                protected int column;
 
1761
 
 
1762
                /**
 
1763
                 * Creates a new cell ID.
 
1764
                 * 
 
1765
                 * @param row
 
1766
                 *            Cell row.
 
1767
                 * @param column
 
1768
                 *            Cell column.
 
1769
                 */
 
1770
                public TableCellId(int row, int column) {
 
1771
                        this.row = row;
 
1772
                        this.column = column;
 
1773
                }
 
1774
 
 
1775
                /*
 
1776
                 * (non-Javadoc)
 
1777
                 * 
 
1778
                 * @see java.lang.Comparable#compareTo(java.lang.Object)
 
1779
                 */
 
1780
                @Override
 
1781
                public int compareTo(TableCellId o) {
 
1782
                        if (row == o.row) {
 
1783
                                return (column < o.column) ? -1 : ((column == o.column) ? 0 : 1);
 
1784
                        } else {
 
1785
                                return (row < o.row) ? -1 : ((row == o.row) ? 0 : 1);
 
1786
                        }
 
1787
                }
 
1788
 
 
1789
                /*
 
1790
                 * (non-Javadoc)
 
1791
                 * 
 
1792
                 * @see java.lang.Object#equals(java.lang.Object)
 
1793
                 */
 
1794
                @Override
 
1795
                public boolean equals(Object obj) {
 
1796
                        if (obj instanceof TableCellId) {
 
1797
                                return this.compareTo((TableCellId) obj) == 0;
 
1798
                        }
 
1799
                        return false;
 
1800
                }
 
1801
 
 
1802
                /*
 
1803
                 * (non-Javadoc)
 
1804
                 *
 
1805
                 * @see java.lang.Object#hashCode()
 
1806
                 */
 
1807
                @Override
 
1808
                public int hashCode() {
 
1809
                        return (this.row ^ (this.row << 16))
 
1810
                                        & (this.column ^ (this.column << 16));
 
1811
                }
 
1812
 
 
1813
                /*
 
1814
                 * (non-Javadoc)
 
1815
                 *
 
1816
                 * @see java.lang.Object#toString()
 
1817
                 */
 
1818
                @Override
 
1819
                public String toString() {
 
1820
                        return "Row " + this.row + ", Column " + this.column;
 
1821
                }
 
1822
        }
 
1823
 
 
1824
        /**
 
1825
         * State listener for tracking the selection changes.
 
1826
         * 
 
1827
         * @author Kirill Grouchnikov
 
1828
         */
 
1829
        protected class TableStateListener implements ListSelectionListener,
 
1830
                        TableModelListener, RowSorterListener {
 
1831
                List<SortKey> oldSortKeys = null;
 
1832
 
 
1833
                private boolean isSameSorter(List<? extends SortKey> sortKeys1,
 
1834
                                List<? extends SortKey> sortKeys2) {
 
1835
                        int size1 = (sortKeys1 == null) ? 0 : sortKeys1.size();
 
1836
                        int size2 = (sortKeys2 == null) ? 0 : sortKeys2.size();
 
1837
                        if ((size1 == 0) && (size2 == 0)) {
 
1838
                                return true;
 
1839
                        }
 
1840
                        if ((sortKeys1 == null) && (sortKeys2 == null))
 
1841
                                return true;
 
1842
                        if ((sortKeys1 == null) || (sortKeys2 == null))
 
1843
                                return false;
 
1844
                        if (size1 != size2)
 
1845
                                return false;
 
1846
                        for (int i = 0; i < size1; i++) {
 
1847
                                SortKey sortKey1 = sortKeys1.get(i);
 
1848
                                SortKey sortKey2 = sortKeys2.get(i);
 
1849
                                if ((sortKey1.getColumn() != sortKey2.getColumn())
 
1850
                                                || (sortKey1.getSortOrder() != sortKey2.getSortOrder())) {
 
1851
                                        return false;
 
1852
                                }
 
1853
                        }
 
1854
                        return true;
 
1855
                }
 
1856
 
 
1857
                /*
 
1858
                 * (non-Javadoc)
 
1859
                 * 
 
1860
                 * @see
 
1861
                 * javax.swing.event.ListSelectionListener#valueChanged(javax.swing.
 
1862
                 * event.ListSelectionEvent)
 
1863
                 */
 
1864
                @Override
 
1865
        @SuppressWarnings("unchecked")
 
1866
                public void valueChanged(final ListSelectionEvent e) {
 
1867
                        // fix for issue 478 - no animations when sorter has changed
 
1868
                        List<? extends SortKey> sortKeys = (table.getRowSorter() == null) ? null
 
1869
                                        : table.getRowSorter().getSortKeys();
 
1870
                        boolean isDifferentSorter = !isSameSorter(sortKeys, oldSortKeys);
 
1871
                        if (e.getValueIsAdjusting() && isDifferentSorter)
 
1872
                                return;
 
1873
                        if (sortKeys == null) {
 
1874
                                oldSortKeys = null;
 
1875
                        } else {
 
1876
                                oldSortKeys = new ArrayList<SortKey>();
 
1877
                                for (SortKey sortKey : sortKeys) {
 
1878
                                        SortKey copy = new SortKey(sortKey.getColumn(),
 
1879
                                                        sortKey.getSortOrder());
 
1880
                                        oldSortKeys.add(copy);
 
1881
                                }
 
1882
                        }
 
1883
                        syncSelection(isDifferentSorter);
 
1884
                }
 
1885
 
 
1886
                /*
 
1887
                 * (non-Javadoc)
 
1888
                 * 
 
1889
                 * @see
 
1890
                 * javax.swing.event.TableModelListener#tableChanged(javax.swing.event
 
1891
                 * .TableModelEvent)
 
1892
                 */
 
1893
                @Override
 
1894
        public void tableChanged(final TableModelEvent e) {
 
1895
                        // fix for defect 291 - tracking changes to the table.
 
1896
                        SwingUtilities.invokeLater(new Runnable() {
 
1897
                                @Override
 
1898
                public void run() {
 
1899
                                        // fix for defect 350 - font might have been
 
1900
                                        // switched in the middle of update
 
1901
                                        if (table == null)
 
1902
                                                return;
 
1903
 
 
1904
                                        // fix for defect 328 - do not clear the
 
1905
                                        // internal selection and focus tracking
 
1906
                                        // when the event is table update.
 
1907
                                        if (e.getType() != TableModelEvent.UPDATE) {
 
1908
                                                selectedIndices.clear();
 
1909
                                                stateTransitionMultiTracker.clear();
 
1910
                                                focusedCellId = null;
 
1911
                                        }
 
1912
                                        syncSelection(true);
 
1913
                                        table.repaint();
 
1914
                                }
 
1915
                        });
 
1916
                }
 
1917
 
 
1918
                @Override
 
1919
                public void sorterChanged(RowSorterEvent e) {
 
1920
                        SwingUtilities.invokeLater(new Runnable() {
 
1921
                                @Override
 
1922
                                public void run() {
 
1923
                                        // fix for issue 479 - cancel selection animations
 
1924
                                        // that are happening due to changes in sorter
 
1925
                                        stateTransitionMultiTracker.clear();
 
1926
                                }
 
1927
                        });
 
1928
                }
 
1929
        }
 
1930
 
 
1931
        /**
 
1932
         * Listener for fade animations on table rollovers.
 
1933
         * 
 
1934
         * @author Kirill Grouchnikov
 
1935
         */
 
1936
        private class RolloverFadeListener implements MouseListener,
 
1937
                        MouseMotionListener {
 
1938
                @Override
 
1939
        public void mouseClicked(MouseEvent e) {
 
1940
                }
 
1941
 
 
1942
                @Override
 
1943
        public void mouseEntered(MouseEvent e) {
 
1944
                }
 
1945
 
 
1946
                @Override
 
1947
        public void mousePressed(MouseEvent e) {
 
1948
                }
 
1949
 
 
1950
                @Override
 
1951
        public void mouseReleased(MouseEvent e) {
 
1952
                }
 
1953
 
 
1954
                @Override
 
1955
        public void mouseExited(MouseEvent e) {
 
1956
                        // if (SubstanceCoreUtilities.toBleedWatermark(list))
 
1957
                        // return;
 
1958
 
 
1959
                        if (table == null)
 
1960
                                return;
 
1961
 
 
1962
                        if (!table.isEnabled())
 
1963
                                return;
 
1964
 
 
1965
                        // check the mouse location. If the cell editor has been shown
 
1966
                        // or hidden, we will get a mouseExited() event, but shouldn't
 
1967
                        // be changing the rollover indication if the mouse is still
 
1968
                        // over the table
 
1969
            PointerInfo pi = MouseInfo.getPointerInfo(); 
 
1970
                        Point mouseLoc = pi == null ?  null : pi.getLocation();
 
1971
            Window windowAncestor = SwingUtilities.getWindowAncestor(table);
 
1972
            if ((mouseLoc != null) && (windowAncestor != null)) {
 
1973
                SwingUtilities.convertPointFromScreen(mouseLoc, windowAncestor);
 
1974
                Component deepest = SwingUtilities.getDeepestComponentAt(
 
1975
                        windowAncestor, mouseLoc.x, mouseLoc.y);
 
1976
 
 
1977
                while (deepest != null) {
 
1978
                    if (deepest == table) {
 
1979
                        // still in table
 
1980
                        return;
 
1981
                    }
 
1982
                    deepest = deepest.getParent();
 
1983
                }
 
1984
            }
 
1985
 
 
1986
                        fadeOutAllRollovers();
 
1987
                        this.fadeOutTableHeader();
 
1988
                        rolledOverIndices.clear();
 
1989
                        rolledOverColumn = -1;
 
1990
                }
 
1991
 
 
1992
                @Override
 
1993
        public void mouseMoved(MouseEvent e) {
 
1994
                        if (!SubstanceTableUI.this.table.isEnabled())
 
1995
                                return;
 
1996
                        handleMouseMove(e.getPoint());
 
1997
                        this.handleMoveForHeader(e);
 
1998
                }
 
1999
 
 
2000
                @Override
 
2001
        public void mouseDragged(MouseEvent e) {
 
2002
                        if (!SubstanceTableUI.this.table.isEnabled())
 
2003
                                return;
 
2004
                        handleMouseMove(e.getPoint());
 
2005
                        this.handleMoveForHeader(e);
 
2006
                }
 
2007
 
 
2008
                /**
 
2009
                 * Handles various mouse move events and initiates the fade animation if
 
2010
                 * necessary.
 
2011
                 * 
 
2012
                 * @param e
 
2013
                 *            Mouse event.
 
2014
                 */
 
2015
                private void handleMoveForHeader(MouseEvent e) {
 
2016
                        if (!SubstanceTableUI.this.table.getColumnSelectionAllowed())
 
2017
                                return;
 
2018
                        JTableHeader header = SubstanceTableUI.this.table.getTableHeader();
 
2019
                        if ((header == null) || (!header.isVisible()))
 
2020
                                return;
 
2021
 
 
2022
                        TableHeaderUI ui = header.getUI();
 
2023
                        if (!(ui instanceof SubstanceTableHeaderUI))
 
2024
                                return;
 
2025
 
 
2026
                        SubstanceTableHeaderUI sthui = (SubstanceTableHeaderUI) ui;
 
2027
 
 
2028
                        // synchronized (SubstanceTableUI.this.table) {
 
2029
                        int row = SubstanceTableUI.this.table.rowAtPoint(e.getPoint());
 
2030
                        int column = SubstanceTableUI.this.table
 
2031
                                        .columnAtPoint(e.getPoint());
 
2032
                        if ((row < 0) || (row >= SubstanceTableUI.this.table.getRowCount())
 
2033
                                        || (column < 0)
 
2034
                                        || (column >= SubstanceTableUI.this.table.getColumnCount())) {
 
2035
                                this.fadeOutTableHeader();
 
2036
                                // System.out.println("Nulling RO column index");
 
2037
                                SubstanceTableUI.this.rolledOverColumn = -1;
 
2038
                        } else {
 
2039
                                // check if this is the same column
 
2040
                                if (SubstanceTableUI.this.rolledOverColumn == column)
 
2041
                                        return;
 
2042
 
 
2043
                                this.fadeOutTableHeader();
 
2044
 
 
2045
                                TableColumnModel columnModel = header.getColumnModel();
 
2046
                                StateTransitionTracker columnTransitionTracker = sthui
 
2047
                                                .getTracker(column, false,
 
2048
                                                                columnModel.getColumnSelectionAllowed()
 
2049
                                                                                && columnModel.getSelectionModel()
 
2050
                                                                                                .isSelectedIndex(column));
 
2051
                                columnTransitionTracker.getModel().setRollover(true);
 
2052
 
 
2053
                                SubstanceTableUI.this.rolledOverColumn = column;
 
2054
                        }
 
2055
                        // }
 
2056
                }
 
2057
 
 
2058
                /**
 
2059
                 * Initiates the fade out effect.
 
2060
                 */
 
2061
                private void fadeOutTableHeader() {
 
2062
                        if (SubstanceTableUI.this.rolledOverColumn >= 0) {
 
2063
                                JTableHeader header = SubstanceTableUI.this.table
 
2064
                                                .getTableHeader();
 
2065
                                if ((header == null) || (!header.isVisible()))
 
2066
                                        return;
 
2067
                                SubstanceTableHeaderUI ui = (SubstanceTableHeaderUI) header
 
2068
                                                .getUI();
 
2069
 
 
2070
                                TableColumnModel columnModel = header.getColumnModel();
 
2071
                                StateTransitionTracker columnTransitionTracker = ui.getTracker(
 
2072
                                                rolledOverColumn, true,
 
2073
                                                columnModel.getColumnSelectionAllowed()
 
2074
                                                                && columnModel.getSelectionModel()
 
2075
                                                                                .isSelectedIndex(rolledOverColumn));
 
2076
                                columnTransitionTracker.getModel().setRollover(false);
 
2077
                        }
 
2078
                }
 
2079
 
 
2080
                /**
 
2081
                 * Handles various mouse move events and initiates the fade animation if
 
2082
                 * necessary.
 
2083
                 * 
 
2084
                 * @param mousePoint
 
2085
                 *            Mouse point.
 
2086
                 */
 
2087
                private void handleMouseMove(Point mousePoint) {
 
2088
                        // synchronized (SubstanceTableUI.this.table) {
 
2089
                        int row = table.rowAtPoint(mousePoint);
 
2090
                        int column = table.columnAtPoint(mousePoint);
 
2091
                        if ((row < 0) || (row >= table.getRowCount()) || (column < 0)
 
2092
                                        || (column >= table.getColumnCount())) {
 
2093
                                this.fadeOutAllRollovers();
 
2094
                                // System.out.println("Nulling RO index in handleMove()");
 
2095
                                // table.putClientProperty(ROLLED_OVER_INDEX, null);
 
2096
                                rolledOverIndices.clear();
 
2097
                        } else {
 
2098
                                // check if this is the same index
 
2099
                                boolean hasRowSelection = table.getRowSelectionAllowed();
 
2100
                                boolean hasColumnSelection = table.getColumnSelectionAllowed();
 
2101
                                int startRolloverRow = row;
 
2102
                                int endRolloverRow = row;
 
2103
                                int startRolloverColumn = column;
 
2104
                                int endRolloverColumn = column;
 
2105
                                if (hasRowSelection && !hasColumnSelection) {
 
2106
                                        startRolloverColumn = 0;
 
2107
                                        endRolloverColumn = table.getColumnCount() - 1;
 
2108
                                }
 
2109
                                if (!hasRowSelection && hasColumnSelection) {
 
2110
                                        startRolloverRow = 0;
 
2111
                                        endRolloverRow = table.getRowCount() - 1;
 
2112
                                }
 
2113
                                Set<TableCellId> toRemove = new HashSet<TableCellId>();
 
2114
                                for (TableCellId currRolloverId : rolledOverIndices) {
 
2115
                                        if ((currRolloverId.row < startRolloverRow)
 
2116
                                                        || (currRolloverId.row > endRolloverRow)
 
2117
                                                        || (currRolloverId.column < startRolloverColumn)
 
2118
                                                        || (currRolloverId.column > endRolloverColumn)) {
 
2119
                                                fadeOutRollover(currRolloverId);
 
2120
                                                toRemove.add(currRolloverId);
 
2121
                                        }
 
2122
                                }
 
2123
                                for (TableCellId id : toRemove) {
 
2124
                                        rolledOverIndices.remove(id);
 
2125
                                }
 
2126
 
 
2127
                                int totalRolloverCount = (endRolloverRow - startRolloverRow + 1)
 
2128
                                                * (endRolloverColumn - startRolloverColumn + 1);
 
2129
                                if (totalRolloverCount > 20) {
 
2130
                                        for (int i = startRolloverRow; i <= endRolloverRow; i++) {
 
2131
                                                for (int j = startRolloverColumn; j <= endRolloverColumn; j++) {
 
2132
                                                        rolledOverIndices.add(new TableCellId(i, j));
 
2133
                                                }
 
2134
                                        }
 
2135
                                        table.repaint();
 
2136
                                } else {
 
2137
                                        for (int i = startRolloverRow; i <= endRolloverRow; i++) {
 
2138
                                                for (int j = startRolloverColumn; j <= endRolloverColumn; j++) {
 
2139
                                                        TableCellId currCellId = new TableCellId(i, j);
 
2140
                                                        if (rolledOverIndices.contains(currCellId))
 
2141
                                                                continue;
 
2142
                                                        // System.out
 
2143
                                                        // .println("Getting rollover/in tracker for "
 
2144
                                                        // + currCellId);
 
2145
                                                        StateTransitionTracker tracker = getTracker(
 
2146
                                                                        currCellId,
 
2147
                                                                        false,
 
2148
                                                                        getCellState(currCellId).isFacetActive(
 
2149
                                                                                        ComponentStateFacet.SELECTION));
 
2150
                                                        tracker.getModel().setRollover(true);
 
2151
 
 
2152
                                                        rolledOverIndices.add(currCellId);
 
2153
                                                }
 
2154
                                        }
 
2155
                                }
 
2156
                        }
 
2157
                }
 
2158
 
 
2159
                /**
 
2160
                 * Initiates the fade out effect.
 
2161
                 */
 
2162
                private void fadeOutRollover(TableCellId tableCellId) {
 
2163
                        if (rolledOverIndices.contains(tableCellId)) {
 
2164
                                // System.out
 
2165
                                // .println("Getting rollover/out tracker for " + cellId);
 
2166
                                StateTransitionTracker tracker = getTracker(
 
2167
                                                tableCellId,
 
2168
                                                true,
 
2169
                                                getCellState(tableCellId).isFacetActive(
 
2170
                                                                ComponentStateFacet.SELECTION));
 
2171
                                tracker.getModel().setRollover(false);
 
2172
                        }
 
2173
                }
 
2174
 
 
2175
                private void fadeOutAllRollovers() {
 
2176
                        if (rolledOverIndices.size() < 20) {
 
2177
                                for (TableCellId tcid : rolledOverIndices) {
 
2178
                                        fadeOutRollover(tcid);
 
2179
                                }
 
2180
                        }
 
2181
                }
 
2182
        }
 
2183
 
 
2184
        /**
 
2185
         * Returns a comparable ID for the specified location.
 
2186
         * 
 
2187
         * @param row
 
2188
         *            Row index.
 
2189
         * @param column
 
2190
         *            Column index.
 
2191
         * @return Comparable ID for the specified location.
 
2192
         */
 
2193
        public TableCellId getId(int row, int column) {
 
2194
                cellId.column = column;
 
2195
                cellId.row = row;
 
2196
                return cellId;
 
2197
        }
 
2198
 
 
2199
        TableCellId cellId;
 
2200
 
 
2201
        /**
 
2202
         * Synchronizes the current selection state.
 
2203
         * 
 
2204
         * @param enforceNoAnimations
 
2205
         *            Whether to force no animations.
 
2206
         */
 
2207
        // @SuppressWarnings("unchecked")
 
2208
        protected void syncSelection(boolean enforceNoAnimations) {
 
2209
                if (this.table == null) {
 
2210
                        // fix for defect 270 - if the UI delegate is updated
 
2211
                        // by another selection listener, ignore this
 
2212
                        return;
 
2213
                }
 
2214
 
 
2215
                int rows = this.table.getRowCount();
 
2216
                int cols = this.table.getColumnCount();
 
2217
 
 
2218
                int rowLeadIndex = this.table.getSelectionModel()
 
2219
                                .getLeadSelectionIndex();
 
2220
                int colLeadIndex = this.table.getColumnModel().getSelectionModel()
 
2221
                                .getLeadSelectionIndex();
 
2222
                boolean isFocusOwner = this.table.isFocusOwner();
 
2223
 
 
2224
                // fix for defect 209 - selection very slow on large tables with
 
2225
                // column selection set to true and row selection set to false.
 
2226
                // Solution - no selection animations on tables with more than 1000
 
2227
                // cells.
 
2228
                if (!this._hasSelectionAnimations()) {
 
2229
                        stateTransitionMultiTracker.clear();
 
2230
                        // this.prevStateMap.clear();
 
2231
                        table.repaint();
 
2232
 
 
2233
                        // fix for issue 414 - track focus on tables
 
2234
                        // without selection animations
 
2235
                        if (isFocusOwner) {
 
2236
                                this.focusedCellId = new TableCellId(rowLeadIndex, colLeadIndex);
 
2237
                        }
 
2238
                        return;
 
2239
                }
 
2240
 
 
2241
                Set<StateTransitionTracker> initiatedTrackers = new HashSet<StateTransitionTracker>();
 
2242
 
 
2243
                for (int i = 0; i < rows; i++) {
 
2244
                        for (int j = 0; j < cols; j++) {
 
2245
                                TableCellId cellId = new TableCellId(i, j);
 
2246
                                if (this.table.isCellSelected(i, j)) {
 
2247
                                        // check if was selected before
 
2248
                                        if (!this.selectedIndices.containsKey(cellId)) {
 
2249
                                                // start fading in
 
2250
                                                if (!enforceNoAnimations) {
 
2251
                                                        // System.out
 
2252
                                                        // .println("Getting selection/in tracker for "
 
2253
                                                        // + cellId);
 
2254
                                                        StateTransitionTracker tracker = getTracker(
 
2255
                                                                        cellId,
 
2256
                                                                        getCellState(cellId).isFacetActive(
 
2257
                                                                                        ComponentStateFacet.ROLLOVER),
 
2258
                                                                        false);
 
2259
                                                        tracker.getModel().setSelected(true);
 
2260
                                                        // System.out
 
2261
                                                        // .println("Selecting previously unselected "
 
2262
                                                        // + i + ":" + j);
 
2263
                                                        initiatedTrackers.add(tracker);
 
2264
                                                        if (initiatedTrackers.size() > 20) {
 
2265
                                                                stateTransitionMultiTracker.clear();
 
2266
                                                                initiatedTrackers.clear();
 
2267
                                                                enforceNoAnimations = true;
 
2268
                                                        }
 
2269
                                                }
 
2270
 
 
2271
                                                this.selectedIndices.put(cellId,
 
2272
                                                                this.table.getValueAt(i, j));
 
2273
                                        }
 
2274
                                } else {
 
2275
                                        // check if was selected before and still points
 
2276
                                        // to the same element
 
2277
                                        if (this.selectedIndices.containsKey(cellId)) {
 
2278
                                                // corner case when the model returns null
 
2279
                                                Object oldValue = this.selectedIndices.get(cellId);
 
2280
                                                if ((i >= this.table.getModel().getRowCount())
 
2281
                                                                || (j >= this.table.getModel().getColumnCount())) {
 
2282
                                                        // not only the content changed, but the model
 
2283
                                                        // dimensions as well
 
2284
                                                        continue;
 
2285
                                                }
 
2286
                                                Object currValue = this.table.getValueAt(i, j);
 
2287
                                                boolean isSame;
 
2288
                                                if (oldValue == null) {
 
2289
                                                        isSame = (currValue == null);
 
2290
                                                } else {
 
2291
                                                        isSame = oldValue.equals(currValue);
 
2292
                                                }
 
2293
                                                if (isSame) {
 
2294
                                                        // start fading out
 
2295
                                                        if (!enforceNoAnimations) {
 
2296
                                                                // System.out
 
2297
                                                                // .println("Getting selection/out tracker for "
 
2298
                                                                // + cellId);
 
2299
                                                                StateTransitionTracker tracker = getTracker(
 
2300
                                                                                cellId,
 
2301
                                                                                getCellState(cellId).isFacetActive(
 
2302
                                                                                                ComponentStateFacet.ROLLOVER),
 
2303
                                                                                true);
 
2304
                                                                tracker.getModel().setSelected(false);
 
2305
                                                                // System.out
 
2306
                                                                // .println("Unselecting previously selected "
 
2307
                                                                // + i + ":" + j);
 
2308
 
 
2309
                                                                initiatedTrackers.add(tracker);
 
2310
                                                                if (initiatedTrackers.size() > 20) {
 
2311
                                                                        stateTransitionMultiTracker.clear();
 
2312
                                                                        initiatedTrackers.clear();
 
2313
                                                                        enforceNoAnimations = true;
 
2314
                                                                }
 
2315
                                                        }
 
2316
                                                }
 
2317
                                                this.selectedIndices.remove(cellId);
 
2318
                                        }
 
2319
                                }
 
2320
 
 
2321
                                // handle focus animations
 
2322
                                boolean cellHasFocus = isFocusOwner && (i == rowLeadIndex)
 
2323
                                                && (j == colLeadIndex);
 
2324
                                if (cellHasFocus) {
 
2325
                                        // check if it's a different cell
 
2326
                                        if ((this.focusedCellId == null)
 
2327
                                                        || !this.focusedCellId.equals(cellId)) {
 
2328
                                                if (!enforceNoAnimations) {
 
2329
                                                        if (this.focusedCellId != null) {
 
2330
                                                                // fade out the previous focus holder
 
2331
 
 
2332
                                                                ComponentState cellState = getCellState(this.focusedCellId);
 
2333
                                                                // System.out.println("Getting focus/out tracker for "
 
2334
                                                                // + cellId);
 
2335
                                                                StateTransitionTracker tracker = getTracker(
 
2336
                                                                                this.focusedCellId,
 
2337
                                                                                cellState
 
2338
                                                                                                .isFacetActive(ComponentStateFacet.ROLLOVER),
 
2339
                                                                                cellState
 
2340
                                                                                                .isFacetActive(ComponentStateFacet.SELECTION));
 
2341
                                                                tracker.setFocusState(false);
 
2342
                                                        }
 
2343
 
 
2344
                                                        // fade in the current cell (new focus holder)
 
2345
                                                        ComponentState cellState = getCellState(cellId);
 
2346
 
 
2347
                                                        // System.out.println("Getting focus/in tracker for "
 
2348
                                                        // + currId);
 
2349
                                                        StateTransitionTracker tracker = getTracker(
 
2350
                                                                        cellId,
 
2351
                                                                        cellState
 
2352
                                                                                        .isFacetActive(ComponentStateFacet.ROLLOVER),
 
2353
                                                                        cellState
 
2354
                                                                                        .isFacetActive(ComponentStateFacet.SELECTION));
 
2355
                                                        tracker.setFocusState(true);
 
2356
                                                }
 
2357
 
 
2358
                                                if (AnimationConfigurationManager.getInstance()
 
2359
                                                                .isAnimationAllowed(AnimationFacet.FOCUS,
 
2360
                                                                                this.table)) {
 
2361
                                                        // and store it for future checks
 
2362
                                                        this.focusedCellId = new TableCellId(i, j);
 
2363
                                                }
 
2364
                                        }
 
2365
                                } else {
 
2366
                                        // check if previously it held focus
 
2367
                                        if (cellId.equals(this.focusedCellId)) {
 
2368
                                                if (!enforceNoAnimations) {
 
2369
                                                        // fade it out
 
2370
                                                        ComponentState cellState = getCellState(cellId);
 
2371
                                                        // System.out.println("Getting focus/out tracker for "
 
2372
                                                        // + cellId);
 
2373
                                                        StateTransitionTracker tracker = getTracker(
 
2374
                                                                        cellId,
 
2375
                                                                        cellState
 
2376
                                                                                        .isFacetActive(ComponentStateFacet.ROLLOVER),
 
2377
                                                                        cellState
 
2378
                                                                                        .isFacetActive(ComponentStateFacet.SELECTION));
 
2379
                                                        tracker.setFocusState(false);
 
2380
                                                }
 
2381
 
 
2382
                                                this.focusedCellId = null;
 
2383
                                        }
 
2384
                                }
 
2385
                        }
 
2386
                }
 
2387
        }
 
2388
 
 
2389
        /**
 
2390
         * Returns the current state for the specified cell.
 
2391
         * 
 
2392
         * @param cellIndex
 
2393
         *            Cell index.
 
2394
         * @return The current state for the specified cell.
 
2395
         */
 
2396
        public ComponentState getCellState(TableCellId cellIndex) {
 
2397
                boolean isEnabled = this.table.isEnabled();
 
2398
 
 
2399
                StateTransitionTracker tracker = this.stateTransitionMultiTracker
 
2400
                                .getTracker(cellIndex);
 
2401
                if (tracker == null) {
 
2402
                        int row = cellIndex.row;
 
2403
                        int column = cellIndex.column;
 
2404
                        TableCellId cellId = this.getId(row, column);
 
2405
                        boolean isRollover = rolledOverIndices.contains(cellId);
 
2406
                        boolean isSelected;
 
2407
                        boolean hasSelectionAnimations = (this.updateInfo != null) ? this.updateInfo.hasSelectionAnimations
 
2408
                                        : this._hasSelectionAnimations();
 
2409
                        if (hasSelectionAnimations
 
2410
                                        && AnimationConfigurationManager
 
2411
                                                        .getInstance()
 
2412
                                                        .isAnimationAllowed(AnimationFacet.SELECTION, table))
 
2413
                                isSelected = this.selectedIndices.containsKey(cellId);
 
2414
                        else {
 
2415
                                isSelected = this.table.isCellSelected(row, column);
 
2416
                        }
 
2417
                        return ComponentState.getState(isEnabled, isRollover, isSelected);
 
2418
                } else {
 
2419
                        ComponentState fromTracker = tracker.getModelStateInfo()
 
2420
                                        .getCurrModelState();
 
2421
                        return ComponentState.getState(isEnabled,
 
2422
                                        fromTracker.isFacetActive(ComponentStateFacet.ROLLOVER),
 
2423
                                        fromTracker.isFacetActive(ComponentStateFacet.SELECTION));
 
2424
                }
 
2425
        }
 
2426
 
 
2427
        /**
 
2428
         * Returns the current state for the specified cell.
 
2429
         * 
 
2430
         * @param cellId
 
2431
         *            Cell index.
 
2432
         * @return The current state for the specified cell.
 
2433
         */
 
2434
        public StateTransitionTracker.ModelStateInfo getModelStateInfo(
 
2435
                        TableCellId cellId) {
 
2436
                if (this.stateTransitionMultiTracker.size() == 0)
 
2437
                        return null;
 
2438
                StateTransitionTracker tracker = this.stateTransitionMultiTracker
 
2439
                                .getTracker(cellId);
 
2440
                if (tracker == null) {
 
2441
                        return null;
 
2442
                } else {
 
2443
                        return tracker.getModelStateInfo();
 
2444
                }
 
2445
        }
 
2446
 
 
2447
        /**
 
2448
         * Checks whether the table has animations.
 
2449
         * 
 
2450
         * @return <code>true</code> if the table has animations, <code>false</code>
 
2451
         *         otherwise.
 
2452
         */
 
2453
        protected boolean _hasAnimations() {
 
2454
                // fix for defects 164 and 209 - selection
 
2455
                // and deletion are very slow on large tables.
 
2456
                int rowCount = this.table.getRowCount();
 
2457
                int colCount = this.table.getColumnCount();
 
2458
                if (rowCount * colCount >= 500)
 
2459
                        return false;
 
2460
                if (this.table.getColumnSelectionAllowed()
 
2461
                                && !this.table.getRowSelectionAllowed()) {
 
2462
                        if (!this.table.getShowHorizontalLines()
 
2463
                                        && !this.table.getShowVerticalLines())
 
2464
                                return rowCount <= 10;
 
2465
                        return rowCount <= 25;
 
2466
                }
 
2467
                if (!this.table.getColumnSelectionAllowed()
 
2468
                                && this.table.getRowSelectionAllowed()) {
 
2469
                        if (!this.table.getShowHorizontalLines()
 
2470
                                        && !this.table.getShowVerticalLines())
 
2471
                                return colCount <= 10;
 
2472
                        return colCount <= 25;
 
2473
                }
 
2474
                return true;
 
2475
        }
 
2476
 
 
2477
        /**
 
2478
         * Checks whether the table has selection animations.
 
2479
         * 
 
2480
         * @return <code>true</code> if the table has selection animations,
 
2481
         *         <code>false</code> otherwise.
 
2482
         */
 
2483
        protected boolean _hasSelectionAnimations() {
 
2484
                return this._hasAnimations()
 
2485
                                && !LafWidgetUtilities.hasNoAnimations(this.table,
 
2486
                                                AnimationFacet.SELECTION);
 
2487
        }
 
2488
 
 
2489
        /**
 
2490
         * Checks whether the table has rollover animations.
 
2491
         * 
 
2492
         * @return <code>true</code> if the table has rollover animations,
 
2493
         *         <code>false</code> otherwise.
 
2494
         */
 
2495
        protected boolean _hasRolloverAnimations() {
 
2496
                return this._hasAnimations()
 
2497
                                && !LafWidgetUtilities.hasNoAnimations(this.table,
 
2498
                                                AnimationFacet.ROLLOVER);
 
2499
        }
 
2500
 
 
2501
        /**
 
2502
         * Returns the index of the rollover column.
 
2503
         * 
 
2504
         * @return The index of the rollover column.
 
2505
         */
 
2506
        public int getRolloverColumnIndex() {
 
2507
                return this.rolledOverColumn;
 
2508
        }
 
2509
 
 
2510
        /**
 
2511
         * Returns indication whether the specified cell has focus.
 
2512
         * 
 
2513
         * @param row
 
2514
         *            Cell row index.
 
2515
         * @param column
 
2516
         *            Cell column index.
 
2517
         * @return <code>true</code> If the focus is on the specified cell,
 
2518
         *         <code>false</code> otherwise.
 
2519
         */
 
2520
        public boolean isFocusedCell(int row, int column) {
 
2521
                return (this.focusedCellId != null) && (this.focusedCellId.row == row)
 
2522
                                && (this.focusedCellId.column == column);
 
2523
        }
 
2524
 
 
2525
        /*
 
2526
         * (non-Javadoc)
 
2527
         * 
 
2528
         * @see javax.swing.plaf.ComponentUI#update(java.awt.Graphics,
 
2529
         * javax.swing.JComponent)
 
2530
         */
 
2531
        @Override
 
2532
        public void update(Graphics g, JComponent c) {
 
2533
                BackgroundPaintingUtils.updateIfOpaque(g, c);
 
2534
                Graphics2D g2d = (Graphics2D) g.create();
 
2535
                SubstanceStripingUtils.setup(c);
 
2536
                this.updateInfo = new TableUpdateOptimizationInfo();
 
2537
                this.paint(g2d, c);
 
2538
                SubstanceStripingUtils.tearDown(c);
 
2539
                g2d.dispose();
 
2540
                this.updateInfo = null;
 
2541
        }
 
2542
 
 
2543
        /**
 
2544
         * Returns the cell renderer insets of this table. Is for internal use only.
 
2545
         * 
 
2546
         * @return The cell renderer insets of this table.
 
2547
         */
 
2548
        public Insets getCellRendererInsets() {
 
2549
                return this.cellRendererInsets;
 
2550
        }
 
2551
 
 
2552
        // public SubstanceColorScheme getDefaultColorScheme() {
 
2553
        // if (this.updateInfo != null)
 
2554
        // return this.updateInfo.defaultScheme;
 
2555
        // return null;
 
2556
        // }
 
2557
        //
 
2558
        // public SubstanceColorScheme getHighlightColorScheme(ComponentState state)
 
2559
        // {
 
2560
        // if (this.updateInfo != null)
 
2561
        // return updateInfo.getHighlightColorScheme(state);
 
2562
        // return null;
 
2563
        // }
 
2564
 
 
2565
        public boolean hasSelectionAnimations() {
 
2566
                if (this.updateInfo != null)
 
2567
                        return this.updateInfo.hasSelectionAnimations;
 
2568
                return this._hasSelectionAnimations();
 
2569
        }
 
2570
 
 
2571
        public boolean hasRolloverAnimations() {
 
2572
                if (this.updateInfo != null)
 
2573
                        return this.updateInfo.hasRolloverAnimations;
 
2574
                return this._hasRolloverAnimations();
 
2575
        }
 
2576
 
 
2577
        private TableUpdateOptimizationInfo updateInfo;
 
2578
 
 
2579
        private class TableUpdateOptimizationInfo extends UpdateOptimizationInfo {
 
2580
                public boolean hasSelectionAnimations;
 
2581
 
 
2582
                public boolean hasRolloverAnimations;
 
2583
 
 
2584
                public TableUpdateOptimizationInfo() {
 
2585
                        super(table);
 
2586
                        this.hasSelectionAnimations = _hasSelectionAnimations();
 
2587
                        this.hasRolloverAnimations = _hasRolloverAnimations();
 
2588
                }
 
2589
        }
 
2590
 
 
2591
        @Override
 
2592
        public UpdateOptimizationInfo getUpdateOptimizationInfo() {
 
2593
                return this.updateInfo;
 
2594
        }
 
2595
 
 
2596
        private boolean isSubstanceDefaultRenderer(Object instance) {
 
2597
                return (instance instanceof SubstanceDefaultTableCellRenderer)
 
2598
                                || (instance instanceof SubstanceDefaultTableCellRenderer.BooleanRenderer);
 
2599
        }
 
2600
 
 
2601
        private boolean isSubstanceDefaultEditor(TableCellEditor editor) {
 
2602
                return (editor instanceof BooleanEditor);
 
2603
        }
 
2604
 
 
2605
        private Rectangle getCellRectangleForRepaint(int row, int column) {
 
2606
                Rectangle rect = this.table.getCellRect(row, column, true);
 
2607
 
 
2608
                if (!table.getShowHorizontalLines() && !table.getShowVerticalLines()) {
 
2609
                        float extra = SubstanceSizeUtils
 
2610
                                        .getBorderStrokeWidth(SubstanceSizeUtils
 
2611
                                                        .getComponentFontSize(table.getTableHeader()));
 
2612
                        rect.x -= (int) extra;
 
2613
                        rect.width += 2 * (int) extra;
 
2614
                        rect.y -= (int) extra;
 
2615
                        rect.height += 2 * (int) extra;
 
2616
                }
 
2617
                return rect;
 
2618
        }
 
2619
 
 
2620
        private StateTransitionTracker getTracker(final TableCellId tableCellId,
 
2621
                        boolean initialRollover, boolean initialSelected) {
 
2622
                StateTransitionTracker tracker = stateTransitionMultiTracker
 
2623
                                .getTracker(tableCellId);
 
2624
                // System.out.println("TableID " + tableCellId + " has tracker "
 
2625
                // + ((tracker == null) ? "null" : ("@" + tracker.hashCode())));
 
2626
                if (tracker == null) {
 
2627
                        ButtonModel model = new DefaultButtonModel();
 
2628
                        model.setSelected(initialSelected);
 
2629
                        model.setRollover(initialRollover);
 
2630
                        tracker = new StateTransitionTracker(table, model);
 
2631
                        tracker.registerModelListeners();
 
2632
                        tracker.setRepaintCallback(new RepaintCallback() {
 
2633
                                @Override
 
2634
                                public TimelineCallback getRepaintCallback() {
 
2635
                                        return new CellRepaintCallback(table, tableCellId.row,
 
2636
                                                        tableCellId.column);
 
2637
                                }
 
2638
                        });
 
2639
                        tracker.setName("row " + tableCellId.row + ", col "
 
2640
                                        + tableCellId.column);
 
2641
                        // System.out.println("TableID " + tableCellId +
 
2642
                        // " has new tracker @"
 
2643
                        // + tracker.hashCode());
 
2644
                        stateTransitionMultiTracker.addTracker(tableCellId, tracker);
 
2645
                }
 
2646
                return tracker;
 
2647
        }
 
2648
 
 
2649
        public StateTransitionTracker getStateTransitionTracker(TableCellId tableId) {
 
2650
                return this.stateTransitionMultiTracker.getTracker(tableId);
 
2651
        }
 
2652
}