/* * Copyright (c) 2005-2016 Substance Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * o Neither the name of Substance Kirill Grouchnikov nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.pushingpixels.substance.internal.ui; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.swing.ButtonModel; import javax.swing.DefaultButtonModel; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.ListCellRenderer; import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicListUI; import org.pushingpixels.lafwidget.LafWidgetUtilities; import org.pushingpixels.lafwidget.animation.AnimationFacet; import org.pushingpixels.lafwidget.utils.RenderingUtils; import org.pushingpixels.substance.api.ColorSchemeAssociationKind; import org.pushingpixels.substance.api.ComponentState; import org.pushingpixels.substance.api.ComponentStateFacet; import org.pushingpixels.substance.api.SubstanceColorScheme; import org.pushingpixels.substance.api.SubstanceLookAndFeel; import org.pushingpixels.substance.api.renderers.SubstanceDefaultListCellRenderer; import org.pushingpixels.substance.internal.animation.StateTransitionMultiTracker; import org.pushingpixels.substance.internal.animation.StateTransitionTracker; import org.pushingpixels.substance.internal.painter.BackgroundPaintingUtils; import org.pushingpixels.substance.internal.painter.HighlightPainterUtils; import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities; import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities; import org.pushingpixels.substance.internal.utils.SubstanceStripingUtils; import org.pushingpixels.substance.internal.utils.UpdateOptimizationAware; import org.pushingpixels.substance.internal.utils.UpdateOptimizationInfo; import org.pushingpixels.trident.Timeline.TimelineState; import org.pushingpixels.trident.callback.UIThreadTimelineCallbackAdapter; /** * UI for lists in <b>Substance</b> look and feel. * * @author Kirill Grouchnikov */ public class SubstanceListUI extends BasicListUI implements UpdateOptimizationAware { /** * Holds the list of currently selected indices. */ protected Map<Integer, Object> selectedIndices; /** * Holds the currently rolled-over index, or -1 is there is none such. */ protected int rolledOverIndex; /** * Property listener that listens to the * {@link SubstanceLookAndFeel#WATERMARK_TO_BLEED} property. */ protected PropertyChangeListener substancePropertyChangeListener; /** * Listener for transition animations on list selections. */ protected ListSelectionListener substanceListSelectionListener; /** * Listener for transition animations on list rollovers. */ protected RolloverFadeListener substanceFadeRolloverListener; private ComponentListener substanceComponentListener; private StateTransitionMultiTracker<Integer> stateTransitionMultiTracker; private ListDataListener substanceListDataListener; private class SubstanceListSelectionListener implements ListSelectionListener { public void valueChanged(final ListSelectionEvent e) { // fix for issue 469/474 - update the inner structures // in a separate event SwingUtilities.invokeLater(() -> { handleListSelectionChange(e); if (list != null) { list.repaint(); } }); } private void handleListSelectionChange(ListSelectionEvent e) { if (list == null) { // fix for issue 464 - misbehaving app listener can change // look-and-feel without giving this listener a chance to // react return; } // optimization on large lists and large selections if (LafWidgetUtilities.hasNoAnimations(list, AnimationFacet.SELECTION)) return; // no selection animations on non-Substance renderers if (!(list.getCellRenderer() instanceof SubstanceDefaultListCellRenderer)) { syncModelContents(); return; } Set<StateTransitionTracker> initiatedTrackers = new HashSet<StateTransitionTracker>(); boolean fadeCanceled = false; for (int i = e.getFirstIndex(); i <= e.getLastIndex(); i++) { if (i >= list.getModel().getSize()) continue; if (list.isSelectedIndex(i)) { // check if was selected before if (!selectedIndices.containsKey(i)) { // start fading in // System.out.println("Fade in on index " + i); selectedIndices.put(i, list.getModel().getElementAt(i)); if (!fadeCanceled) { StateTransitionTracker tracker = getTracker(i, (i == rolledOverIndex), false); tracker.getModel().setSelected(true); initiatedTrackers.add(tracker); if (initiatedTrackers.size() > 25) { stateTransitionMultiTracker.clear(); initiatedTrackers.clear(); fadeCanceled = true; } } } } else { // check if was selected before and still points to the // same element if (selectedIndices.containsKey(i)) { if (selectedIndices.get(i) == list.getModel() .getElementAt(i)) { // start fading out // System.out // .println("Fade out on index " + i); if (!fadeCanceled) { StateTransitionTracker tracker = getTracker(i, (i == rolledOverIndex), true); tracker.getModel().setSelected(false); initiatedTrackers.add(tracker); if (initiatedTrackers.size() > 25) { stateTransitionMultiTracker.clear(); initiatedTrackers.clear(); fadeCanceled = true; } } } selectedIndices.remove(i); } } } } } private final class SubstanceListDataListener implements ListDataListener { private void _syncModelContents() { // fix for issue 469/474 - update the inner structures // in a separate event SwingUtilities.invokeLater(() -> syncModelContents()); } @Override public void intervalRemoved(ListDataEvent e) { _syncModelContents(); } @Override public void intervalAdded(ListDataEvent e) { _syncModelContents(); } @Override public void contentsChanged(ListDataEvent e) { _syncModelContents(); } } /** * Listener for fade animations on list rollovers. * * @author Kirill Grouchnikov */ private class RolloverFadeListener implements MouseListener, MouseMotionListener { public void mouseClicked(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mouseExited(MouseEvent e) { // if (SubstanceCoreUtilities.toBleedWatermark(list)) // return; fadeOutRolloverIndication(); // System.out.println("Nulling RO index"); resetRolloverIndex(); } public void mouseMoved(MouseEvent e) { if (!list.isEnabled()) return; handleMove(e); } public void mouseDragged(MouseEvent e) { if (!list.isEnabled()) return; handleMove(e); } /** * Handles various mouse move events and initiates the fade animation if * necessary. * * @param e * Mouse event. */ private void handleMove(MouseEvent e) { // no rollover effects on non-Substance renderers if (!(list.getCellRenderer() instanceof SubstanceDefaultListCellRenderer)) { fadeOutRolloverIndication(); resetRolloverIndex(); return; } int roIndex = list.locationToIndex(e.getPoint()); if ((roIndex >= 0) && (roIndex < list.getModel().getSize())) { // test actual hit if (!list.getCellBounds(roIndex, roIndex) .contains(e.getPoint())) { roIndex = -1; } } if ((roIndex < 0) || (roIndex >= list.getModel().getSize())) { fadeOutRolloverIndication(); // System.out.println("Nulling RO index"); resetRolloverIndex(); } else { // check if this is the same index if ((rolledOverIndex >= 0) && (rolledOverIndex == roIndex)) return; fadeOutRolloverIndication(); // rollover on a new row StateTransitionTracker tracker = getTracker(roIndex, false, list.isSelectedIndex(roIndex)); tracker.getModel().setRollover(true); rolledOverIndex = roIndex; } } } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent) */ public static ComponentUI createUI(JComponent comp) { SubstanceCoreUtilities.testComponentCreationThreadingViolation(comp); return new SubstanceListUI(); } /** * Creates a UI delegate for list. */ public SubstanceListUI() { super(); rolledOverIndex = -1; selectedIndices = new HashMap<Integer, Object>(); this.stateTransitionMultiTracker = new StateTransitionMultiTracker<Integer>(); } /* * (non-Javadoc) * * @see javax.swing.plaf.basic.BasicListUI#installDefaults() */ @Override protected void installDefaults() { super.installDefaults(); if (SubstanceCoreUtilities.toDrawWatermark(list)) { list.setOpaque(false); } syncModelContents(); } @Override protected void uninstallDefaults() { selectedIndices.clear(); super.uninstallDefaults(); } @Override public void uninstallUI(JComponent c) { this.stateTransitionMultiTracker.clear(); super.uninstallUI(c); } /** * Repaints a single cell during the fade animation cycle. * * @author Kirill Grouchnikov */ protected class CellRepaintCallback extends UIThreadTimelineCallbackAdapter { /** * Associated list. */ protected JList list; /** * Associated (animated) cell index. */ protected int cellIndex; /** * Creates a new animation repaint callback. * * @param list * Associated list. * @param cellIndex * Associated (animated) cell index. */ public CellRepaintCallback(JList list, int cellIndex) { this.list = list; this.cellIndex = cellIndex; } @Override public void onTimelineStateChanged(TimelineState oldState, TimelineState newState, float durationFraction, float timelinePosition) { repaintCell(); } @Override public void onTimelinePulse(float durationFraction, float timelinePosition) { // System.out.println(cellIndex + " at " + timelinePosition); repaintCell(); } /** * Repaints the associated cell. */ private void repaintCell() { // SwingUtilities.invokeLater(new Runnable() { // public void run() { if (SubstanceListUI.this.list == null) { // may happen if the LAF was switched in the meantime return; } try { maybeUpdateLayoutState(); int cellCount = list.getModel().getSize(); if ((cellCount > 0) && (cellIndex < cellCount)) { // need to retrieve the cell rectangle since the // cells can be moved while animating Rectangle rect = SubstanceListUI.this.getCellBounds(list, cellIndex, cellIndex); // System.out.println("Repainting " + cellIndex // + " at " + rect); list.repaint(rect); } } catch (RuntimeException re) { return; } } // }); // } } @Override protected void installListeners() { super.installListeners(); // Add listener for the selection animation substanceListSelectionListener = new SubstanceListSelectionListener(); list.getSelectionModel().addListSelectionListener( substanceListSelectionListener); substanceFadeRolloverListener = new RolloverFadeListener(); list.addMouseMotionListener(substanceFadeRolloverListener); list.addMouseListener(substanceFadeRolloverListener); substancePropertyChangeListener = (final PropertyChangeEvent evt) -> { if (SubstanceLookAndFeel.WATERMARK_VISIBLE.equals(evt .getPropertyName())) { list.setOpaque(!SubstanceCoreUtilities .toDrawWatermark(list)); } if ("model".equals(evt.getPropertyName())) { SwingUtilities.invokeLater(() -> { ListModel oldModel = (ListModel) evt.getOldValue(); if (oldModel != null) { oldModel.removeListDataListener(substanceListDataListener); } ListModel newModel = (ListModel) evt.getNewValue(); substanceListDataListener = new SubstanceListDataListener(); newModel.addListDataListener(substanceListDataListener); syncModelContents(); }); } if ("selectionModel".equals(evt.getPropertyName())) { // fix for issue 475 - wire the listener on the new // selection model SwingUtilities.invokeLater(new Runnable() { @Override public void run() { ListSelectionModel oldModel = (ListSelectionModel) evt .getOldValue(); if (oldModel != null) { oldModel.removeListSelectionListener(substanceListSelectionListener); } ListSelectionModel newModel = (ListSelectionModel) evt .getNewValue(); substanceListSelectionListener = new SubstanceListSelectionListener(); newModel.addListSelectionListener(substanceListSelectionListener); syncModelContents(); } }); } }; list.addPropertyChangeListener(substancePropertyChangeListener); this.substanceComponentListener = new ComponentAdapter() { @Override public void componentMoved(ComponentEvent e) { // clear the rollover indexes since these are no longer // in sync with the mouse location fadeOutRolloverIndication(); resetRolloverIndex(); } }; this.list.addComponentListener(this.substanceComponentListener); this.substanceListDataListener = new SubstanceListDataListener(); this.list.getModel() .addListDataListener(this.substanceListDataListener); } @Override protected void uninstallListeners() { this.list.getModel().removeListDataListener( this.substanceListDataListener); this.substanceListDataListener = null; list.getSelectionModel().removeListSelectionListener( substanceListSelectionListener); substanceListSelectionListener = null; list.removeMouseMotionListener(substanceFadeRolloverListener); list.removeMouseListener(substanceFadeRolloverListener); substanceFadeRolloverListener = null; list.removePropertyChangeListener(substancePropertyChangeListener); substancePropertyChangeListener = null; this.list.removeComponentListener(this.substanceComponentListener); this.substanceComponentListener = null; super.uninstallListeners(); } /* * (non-Javadoc) * * @see javax.swing.plaf.basic.BasicListUI#paintCell(java.awt.Graphics, int, * java.awt.Rectangle, javax.swing.ListCellRenderer, javax.swing.ListModel, * javax.swing.ListSelectionModel, int) */ @Override protected void paintCell(Graphics g, int row, Rectangle rowBounds, ListCellRenderer cellRenderer, ListModel dataModel, ListSelectionModel selModel, int leadIndex) { Object value = dataModel.getElementAt(row); boolean cellHasFocus = list.hasFocus() && (row == leadIndex); boolean isSelected = selModel.isSelectedIndex(row); Component rendererComponent = cellRenderer .getListCellRendererComponent(list, value, row, isSelected, cellHasFocus); if (!(rendererComponent instanceof SubstanceDefaultListCellRenderer)) { // if it's not Substance renderer - ask the Basic delegate to paint // it. super.paintCell(g, row, rowBounds, cellRenderer, dataModel, selModel, leadIndex); return; } boolean isWatermarkBleed = updateInfo.toDrawWatermark; int cx = rowBounds.x; int cy = rowBounds.y; int cw = rowBounds.width; int ch = rowBounds.height; // if (isFileList) { // // Shrink renderer to preferred size. This is mostly used on Windows // // where selection is only shown around the file name, instead of // // across the whole list cell. // int w = Math // .min(cw, rendererComponent.getPreferredSize().width + 4); // if (!isLeftToRight) { // cx += (cw - w); // } // cw = w; // } Graphics2D g2d = (Graphics2D) g.create(); g2d.setComposite(LafWidgetUtilities.getAlphaComposite(list, g)); if (!isWatermarkBleed) { Color background = rendererComponent.getBackground(); // optimization - only render background if it's different // from the list background if ((background != null) && (!list.getBackground().equals(background) || this.updateInfo.isInDecorationArea)) { g2d.setColor(background); g2d.fillRect(cx, cy, cw, ch); } } else { BackgroundPaintingUtils.fillAndWatermark(g2d, this.list, rendererComponent.getBackground(), new Rectangle(cx, cy, cw, ch)); } StateTransitionTracker.ModelStateInfo modelStateInfo = getModelStateInfo( row, rendererComponent); Map<ComponentState, StateTransitionTracker.StateContributionInfo> activeStates = ((modelStateInfo == null) ? null : modelStateInfo.getStateContributionMap()); ComponentState currState = ((modelStateInfo == null) ? getCellState( row, rendererComponent) : modelStateInfo.getCurrModelState()); // if the renderer is disabled, do not show any highlights boolean hasHighlights = false; if (rendererComponent.isEnabled()) { if (activeStates != null) { for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> stateEntry : activeStates .entrySet()) { hasHighlights = (this.updateInfo .getHighlightAlpha(stateEntry.getKey()) * stateEntry.getValue().getContribution() > 0.0f); if (hasHighlights) break; } } else { hasHighlights = (this.updateInfo.getHighlightAlpha(currState) > 0.0f); } } JList.DropLocation dropLocation = list.getDropLocation(); if (dropLocation != null && !dropLocation.isInsert() && dropLocation.getIndex() == row) { // mark drop location SubstanceColorScheme fillScheme = SubstanceColorSchemeUtilities .getColorScheme(list, ColorSchemeAssociationKind.HIGHLIGHT_TEXT, currState); SubstanceColorScheme borderScheme = SubstanceColorSchemeUtilities .getColorScheme(list, ColorSchemeAssociationKind.HIGHLIGHT_BORDER, currState); Rectangle cellRect = new Rectangle(cx, cy, cw, ch); HighlightPainterUtils.paintHighlight(g2d, this.rendererPane, rendererComponent, cellRect, 0.8f, null, fillScheme, borderScheme); } else { if (hasHighlights) { Rectangle cellRect = new Rectangle(cx, cy, cw, ch); if (activeStates == null) { float alpha = this.updateInfo.getHighlightAlpha(currState); if (alpha > 0.0f) { SubstanceColorScheme fillScheme = this.updateInfo .getHighlightColorScheme(currState); SubstanceColorScheme borderScheme = this.updateInfo .getHighlightBorderColorScheme(currState); g2d.setComposite(LafWidgetUtilities.getAlphaComposite( list, alpha, g)); HighlightPainterUtils.paintHighlight(g2d, this.rendererPane, rendererComponent, cellRect, 0.8f, null, fillScheme, borderScheme); g2d.setComposite(LafWidgetUtilities.getAlphaComposite( list, g)); } } else { for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> stateEntry : activeStates .entrySet()) { ComponentState activeState = stateEntry.getKey(); float alpha = this.updateInfo .getHighlightAlpha(activeState) * stateEntry.getValue().getContribution(); if (alpha == 0.0f) continue; SubstanceColorScheme fillScheme = this.updateInfo .getHighlightColorScheme(activeState); SubstanceColorScheme borderScheme = this.updateInfo .getHighlightBorderColorScheme(activeState); g2d.setComposite(LafWidgetUtilities.getAlphaComposite( list, alpha, g)); HighlightPainterUtils.paintHighlight(g2d, this.rendererPane, rendererComponent, cellRect, 0.8f, null, fillScheme, borderScheme); g2d.setComposite(LafWidgetUtilities.getAlphaComposite( list, g)); } } } } // System.out.println(row + ":" + rendererComponent.getBackground()); rendererPane.paintComponent(g2d, rendererComponent, list, cx, cy, cw, ch, true); g2d.dispose(); } public StateTransitionTracker getStateTransitionTracker(int row) { return this.stateTransitionMultiTracker.getTracker(row); } /** * Returns the current state for the specified cell. * * @param cellIndex * Cell index. * @param rendererComponent * Renderer component for the specified cell index. * @return The current state for the specified cell. */ public ComponentState getCellState(int cellIndex, Component rendererComponent) { boolean isEnabled = this.list.isEnabled(); if (rendererComponent != null) { isEnabled = isEnabled && rendererComponent.isEnabled(); } StateTransitionTracker tracker = this.stateTransitionMultiTracker .getTracker(cellIndex); if (tracker == null) { boolean isRollover = (rolledOverIndex >= 0) && (rolledOverIndex == cellIndex); boolean isSelected = selectedIndices.containsKey(cellIndex); return ComponentState.getState(isEnabled, isRollover, isSelected); } else { ComponentState fromTracker = tracker.getModelStateInfo() .getCurrModelState(); return ComponentState.getState(isEnabled, fromTracker.isFacetActive(ComponentStateFacet.ROLLOVER), fromTracker.isFacetActive(ComponentStateFacet.SELECTION)); } } public StateTransitionTracker.ModelStateInfo getModelStateInfo(int row, Component rendererComponent) { if (this.stateTransitionMultiTracker.size() == 0) return null; StateTransitionTracker tracker = this.stateTransitionMultiTracker .getTracker(row); if (tracker == null) { return null; } else { return tracker.getModelStateInfo(); } } /** * Resets the rollover index. */ public void resetRolloverIndex() { rolledOverIndex = -1; } /** * Initiates the fade out effect. */ private void fadeOutRolloverIndication() { if (rolledOverIndex < 0) return; StateTransitionTracker tracker = getTracker(rolledOverIndex, true, list.isSelectedIndex(rolledOverIndex)); tracker.getModel().setRollover(false); } /* * (non-Javadoc) * * @see javax.swing.plaf.ComponentUI#update(java.awt.Graphics, * javax.swing.JComponent) */ @Override public void update(Graphics g, JComponent c) { BackgroundPaintingUtils.updateIfOpaque(g, c); Graphics2D g2d = (Graphics2D) g.create(); RenderingUtils.installDesktopHints(g2d, c); SubstanceStripingUtils.setup(c); this.updateInfo = new UpdateOptimizationInfo(c); this.paint(g2d, c); SubstanceStripingUtils.tearDown(c); g2d.dispose(); this.updateInfo = null; } // /** // * Returns the current default color scheme. This method is for internal // use // * only. // * // * @return The current default color scheme. // */ // public SubstanceColorScheme getDefaultColorScheme() { // if (this.updateInfo != null) // return this.updateInfo.defaultScheme; // return null; // } // // public SubstanceColorScheme getHighlightColorScheme(ComponentState state) // { // if (this.updateInfo != null) // return updateInfo.getHighlightColorScheme(state); // return null; // } private UpdateOptimizationInfo updateInfo; // private class UpdateOptimizationInfo { // public boolean toDrawWatermark; // // private Map<ComponentState, SubstanceColorScheme> highlightSchemeMap; // // private Map<ComponentState, SubstanceColorScheme> borderSchemeMap; // // private Map<ComponentState, Float> highlightAlphaMap; // // public SubstanceColorScheme defaultScheme; // // public DecorationAreaType decorationAreaType; // // public boolean isInDecorationArea; // // public UpdateOptimizationInfo() { // this.toDrawWatermark = SubstanceCoreUtilities.toDrawWatermark(list); // this.defaultScheme = SubstanceColorSchemeUtilities.getColorScheme( // list, ComponentState.DEFAULT); // this.highlightAlphaMap = new EnumMap<ComponentState, Float>( // ComponentState.class); // this.highlightSchemeMap = new EnumMap<ComponentState, // SubstanceColorScheme>( // ComponentState.class); // this.borderSchemeMap = new EnumMap<ComponentState, SubstanceColorScheme>( // ComponentState.class); // this.decorationAreaType = SubstanceLookAndFeel // .getDecorationType(list); // // SubstanceSkin skin = SubstanceCoreUtilities.getSkin(list); // this.isInDecorationArea = (this.decorationAreaType != null) // && skin // .isRegisteredAsDecorationArea(this.decorationAreaType) // && SubstanceCoreUtilities.isOpaque(list); // } // // public SubstanceColorScheme getHighlightColorScheme(ComponentState state) // { // if (!this.highlightSchemeMap.containsKey(state)) { // this.highlightSchemeMap.put(state, // SubstanceColorSchemeUtilities.getColorScheme(list, // ColorSchemeAssociationKind.HIGHLIGHT, state)); // } // return this.highlightSchemeMap.get(state); // } // // public SubstanceColorScheme getHighlightBorderColorScheme( // ComponentState state) { // if (!this.borderSchemeMap.containsKey(state)) { // this.borderSchemeMap.put(state, SubstanceColorSchemeUtilities // .getColorScheme(list, // ColorSchemeAssociationKind.HIGHLIGHT_BORDER, // state)); // } // return this.borderSchemeMap.get(state); // } // // public float getHighlightAlpha(ComponentState state) { // if (!this.highlightAlphaMap.containsKey(state)) { // this.highlightAlphaMap.put(state, SubstanceColorSchemeUtilities // .getHighlightAlpha(list, state)); // } // return this.highlightAlphaMap.get(state); // } // } private void syncModelContents() { if (list == null) return; stateTransitionMultiTracker.clear(); selectedIndices.clear(); for (int i = 0; i < list.getModel().getSize(); i++) { if (list.isSelectedIndex(i)) { selectedIndices.put(i, list.getModel().getElementAt(i)); } } list.repaint(); } private StateTransitionTracker getTracker(final int row, boolean initialRollover, boolean initialSelected) { StateTransitionTracker tracker = stateTransitionMultiTracker .getTracker(row); if (tracker == null) { ButtonModel model = new DefaultButtonModel(); model.setSelected(initialSelected); model.setRollover(initialRollover); tracker = new StateTransitionTracker(list, model); tracker.registerModelListeners(); tracker.setRepaintCallback(() -> new CellRepaintCallback(list, row)); tracker.setName("row " + row); stateTransitionMultiTracker.addTracker(row, tracker); } return tracker; } @Override public UpdateOptimizationInfo getUpdateOptimizationInfo() { return this.updateInfo; } }