001: /*
002: * Copyright (c) 2005-2008 Substance Kirill Grouchnikov. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of Substance Kirill Grouchnikov nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030: package org.jvnet.substance;
031:
032: import java.awt.*;
033: import java.awt.event.*;
034: import java.beans.PropertyChangeEvent;
035: import java.beans.PropertyChangeListener;
036: import java.util.*;
037:
038: import javax.swing.*;
039: import javax.swing.event.ListSelectionEvent;
040: import javax.swing.event.ListSelectionListener;
041: import javax.swing.plaf.ComponentUI;
042: import javax.swing.plaf.basic.BasicListUI;
043:
044: import org.jvnet.lafwidget.LafWidgetUtilities;
045: import org.jvnet.lafwidget.animation.*;
046: import org.jvnet.lafwidget.layout.TransitionLayout;
047: import org.jvnet.substance.painter.highlight.SubstanceHighlightUtils;
048: import org.jvnet.substance.painter.text.SubstanceTextPainter;
049: import org.jvnet.substance.theme.SubstanceTheme;
050: import org.jvnet.substance.utils.*;
051:
052: /**
053: * UI for lists in <b>Substance</b> look and feel.
054: *
055: * @author Kirill Grouchnikov
056: */
057: public class SubstanceListUI extends BasicListUI {
058: /**
059: * Holds the list of currently selected indices.
060: */
061: protected Map<Integer, Object> selectedIndices;
062:
063: /**
064: * Holds the currently rolled-over index, or -1 is there is none such.
065: */
066: protected int rolledOverIndex;
067:
068: /**
069: * Property listener that listens to the
070: * {@link SubstanceLookAndFeel#WATERMARK_TO_BLEED} property.
071: */
072: protected PropertyChangeListener substancePropertyChangeListener;
073:
074: /**
075: * Local cache of JList's client property "List.isFileList"
076: */
077: protected boolean isFileList;
078:
079: /**
080: * Local cache of JList's component orientation property
081: */
082: protected boolean isLeftToRight;
083:
084: /**
085: * Delegate for painting the background of list rows.
086: */
087: private static SubstanceFillBackgroundDelegate backgroundDelegate = new SubstanceFillBackgroundDelegate();
088:
089: /**
090: * Listener for fade animations on list selections.
091: */
092: protected ListSelectionListener substanceFadeSelectionListener;
093:
094: /**
095: * Listener for fade animations on list rollovers.
096: */
097: protected RolloverFadeListener substanceFadeRolloverListener;
098:
099: /**
100: * Map of previous fade states (for state-aware theme transitions).
101: */
102: private Map<Integer, ComponentState> prevStateMap;
103:
104: /**
105: * Map of next fade states (for state-aware theme transitions).
106: */
107: private Map<Integer, ComponentState> nextStateMap;
108:
109: /**
110: * Listener for fade animations on list rollovers.
111: *
112: * @author Kirill Grouchnikov
113: */
114: private class RolloverFadeListener implements MouseListener,
115: MouseMotionListener {
116: public void mouseClicked(MouseEvent e) {
117: }
118:
119: public void mouseEntered(MouseEvent e) {
120: }
121:
122: public void mousePressed(MouseEvent e) {
123: }
124:
125: public void mouseReleased(MouseEvent e) {
126: }
127:
128: public void mouseExited(MouseEvent e) {
129: // if (SubstanceCoreUtilities.toBleedWatermark(list))
130: // return;
131:
132: fadeOut();
133: // System.out.println("Nulling RO index");
134: resetRolloverIndex();
135: // rolledOverIndex = -1;
136: // list.putClientProperty(ROLLED_OVER_INDEX, null);
137: }
138:
139: public void mouseMoved(MouseEvent e) {
140: // if (SubstanceCoreUtilities.toBleedWatermark(list))
141: // return;
142: if (!list.isEnabled())
143: return;
144: handleMove(e);
145: }
146:
147: public void mouseDragged(MouseEvent e) {
148: // if (SubstanceCoreUtilities.toBleedWatermark(list))
149: // return;
150:
151: if (!list.isEnabled())
152: return;
153: handleMove(e);
154: }
155:
156: /**
157: * Handles various mouse move events and initiates the fade animation if
158: * necessary.
159: *
160: * @param e
161: * Mouse event.
162: */
163: private void handleMove(MouseEvent e) {
164: boolean fadeAllowed = FadeConfigurationManager
165: .getInstance().fadeAllowed(FadeKind.ROLLOVER, list);
166: if (!fadeAllowed) {
167: fadeOut();
168: resetRolloverIndex();
169: // rolledOverIndex = -1;
170: // list.putClientProperty(ROLLED_OVER_INDEX, null);
171: return;
172: }
173:
174: int roIndex = list.locationToIndex(e.getPoint());
175: if ((roIndex >= 0) && (roIndex < list.getModel().getSize())) {
176: // test actual hit
177: if (!list.getCellBounds(roIndex, roIndex).contains(
178: e.getPoint())) {
179: roIndex = -1;
180: }
181: }
182: if ((roIndex < 0) || (roIndex >= list.getModel().getSize())) {
183: fadeOut();
184: // System.out.println("Nulling RO index");
185: resetRolloverIndex();
186: // rolledOverIndex = -1;
187: // list.putClientProperty(ROLLED_OVER_INDEX, null);
188: } else {
189: // check if this is the same index
190: // Integer currRoIndex = (Integer) list
191: // .getClientProperty(ROLLED_OVER_INDEX);
192: if ((rolledOverIndex >= 0)
193: && (rolledOverIndex == roIndex))
194: return;
195:
196: fadeOut();
197: FadeTracker.getInstance().trackFadeIn(
198: FadeKind.ROLLOVER, list, roIndex, false,
199: new CellRepaintCallback(list, roIndex));
200: // System.out.println("Setting RO index to " + roIndex);
201: rolledOverIndex = roIndex;
202: // list.putClientProperty(ROLLED_OVER_INDEX, roIndex);
203: }
204: }
205:
206: /**
207: * Initiates the fade out effect.
208: */
209: private void fadeOut() {
210: // Integer prevRoIndex = (Integer) list
211: // .getClientProperty(ROLLED_OVER_INDEX);
212: // if (prevRoIndex == null)
213: // return;
214: if (rolledOverIndex < 0)
215: return;
216:
217: FadeTracker.getInstance().trackFadeOut(FadeKind.ROLLOVER,
218: list, rolledOverIndex, false,
219: new CellRepaintCallback(list, rolledOverIndex));
220: }
221: }
222:
223: /*
224: * (non-Javadoc)
225: *
226: * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
227: */
228: public static ComponentUI createUI(JComponent list) {
229: return new SubstanceListUI();
230: }
231:
232: /**
233: * Creates a UI delegate for list.
234: */
235: public SubstanceListUI() {
236: super ();
237: prevStateMap = new HashMap<Integer, ComponentState>();
238: nextStateMap = new HashMap<Integer, ComponentState>();
239: rolledOverIndex = -1;
240: selectedIndices = new HashMap<Integer, Object>();
241: }
242:
243: /*
244: * (non-Javadoc)
245: *
246: * @see javax.swing.plaf.basic.BasicListUI#installDefaults()
247: */
248: @Override
249: protected void installDefaults() {
250: super .installDefaults();
251: isFileList = Boolean.TRUE.equals(list
252: .getClientProperty("List.isFileList"));
253: isLeftToRight = list.getComponentOrientation().isLeftToRight();
254:
255: if (SubstanceCoreUtilities.toBleedWatermark(list)) {
256: list.setOpaque(false);
257: }
258:
259: for (int i = 0; i < list.getModel().getSize(); i++) {
260: if (list.isSelectedIndex(i)) {
261: selectedIndices.put(i, list.getModel().getElementAt(i));
262: prevStateMap.put(i, ComponentState.SELECTED);
263: }
264: }
265: // this.list.putClientProperty(SubstanceListUI.SELECTED_INDICES,
266: // selected);
267: }
268:
269: @Override
270: protected void uninstallDefaults() {
271: selectedIndices.clear();
272: // this.list.putClientProperty(SubstanceListUI.SELECTED_INDICES, null);
273:
274: super .uninstallDefaults();
275: }
276:
277: /**
278: * Repaints a single cell during the fade animation cycle.
279: *
280: * @author Kirill Grouchnikov
281: */
282: protected class CellRepaintCallback extends FadeTrackerAdapter {
283: /**
284: * Associated list.
285: */
286: protected JList list;
287:
288: /**
289: * Associated (animated) cell index.
290: */
291: protected int cellIndex;
292:
293: /**
294: * Creates a new animation repaint callback.
295: *
296: * @param list
297: * Associated list.
298: * @param cellIndex
299: * Associated (animated) cell index.
300: */
301: public CellRepaintCallback(JList list, int cellIndex) {
302: this .list = list;
303: this .cellIndex = cellIndex;
304: }
305:
306: /*
307: * (non-Javadoc)
308: *
309: * @see org.jvnet.lafwidget.utils.FadeTracker$FadeTrackerCallback#fadeEnded(org.jvnet.lafwidget.utils.FadeTracker.FadeKind)
310: */
311: @Override
312: public void fadeEnded(FadeKind fadeKind) {
313: if ((SubstanceListUI.this .list == list)
314: && (cellIndex < list.getModel().getSize())) {
315: ComponentState currState = getCellState(cellIndex);
316: if (currState == ComponentState.DEFAULT) {
317: prevStateMap.remove(cellIndex);
318: nextStateMap.remove(cellIndex);
319: } else {
320: prevStateMap.put(cellIndex, currState);
321: nextStateMap.put(cellIndex, currState);
322: }
323: // System.out.println(tabIndex + "->"
324: // + prevStateMap.get(tabIndex).name());
325: }
326: repaintCell();
327: }
328:
329: /*
330: * (non-Javadoc)
331: *
332: * @see org.jvnet.lafwidget.animation.FadeTrackerAdapter#fadeReversed(org.jvnet.lafwidget.animation.FadeKind,
333: * boolean, float)
334: */
335: @Override
336: public void fadeReversed(FadeKind fadeKind, boolean isFadingIn,
337: float fadeCycle10) {
338: if ((SubstanceListUI.this .list == list)
339: && (cellIndex < list.getModel().getSize())) {
340: ComponentState nextState = nextStateMap.get(cellIndex);
341: if (nextState == null) {
342: prevStateMap.remove(cellIndex);
343: } else {
344: prevStateMap.put(cellIndex, nextState);
345: }
346: // System.out.println(tabIndex + "->"
347: // + prevStateMap.get(tabIndex).name());
348: }
349: repaintCell();
350: }
351:
352: /*
353: * (non-Javadoc)
354: *
355: * @see org.jvnet.lafwidget.utils.FadeTracker$FadeTrackerCallback#fadePerformed(org.jvnet.lafwidget.utils.FadeTracker.FadeKind,
356: * float)
357: */
358: @Override
359: public void fadePerformed(FadeKind fadeKind, float fade10) {
360: if ((SubstanceListUI.this .list == list)
361: && (cellIndex < list.getModel().getSize())) {
362: nextStateMap.put(cellIndex, getCellState(cellIndex));
363: }
364: repaintCell();
365: }
366:
367: /**
368: * Repaints the associated cell.
369: */
370: private void repaintCell() {
371: SwingUtilities.invokeLater(new Runnable() {
372: public void run() {
373: if (SubstanceListUI.this .list == null) {
374: // may happen if the LAF was switched in the meantime
375: return;
376: }
377: try {
378: maybeUpdateLayoutState();
379: int cellCount = list.getModel().getSize();
380: if ((cellCount > 0) && (cellIndex < cellCount)) {
381: // need to retrieve the cell rectangle since the
382: // cells can be moved while animating
383: Rectangle rect = SubstanceListUI.this
384: .getCellBounds(list, cellIndex,
385: cellIndex);
386: // System.out.println("Repainting " + cellIndex
387: // + " at " + rect);
388: list.repaint(rect);
389: }
390: } catch (RuntimeException re) {
391: return;
392: }
393: }
394: });
395: }
396: }
397:
398: @Override
399: protected void installListeners() {
400: super .installListeners();
401:
402: // Add listener for the selection animation
403: substanceFadeSelectionListener = new ListSelectionListener() {
404: protected void cancelFades(Set<Long> initiatedFadeSequences) {
405: FadeTracker fadeTrackerInstance = FadeTracker
406: .getInstance();
407: for (long fadeId : initiatedFadeSequences) {
408: fadeTrackerInstance.cancelFadeInstance(fadeId);
409: }
410: }
411:
412: @SuppressWarnings("unchecked")
413: public void valueChanged(ListSelectionEvent e) {
414: // optimization on large lists and large selections
415: if (LafWidgetUtilities.hasNoFades(list,
416: FadeKind.SELECTION))
417: return;
418:
419: Set<Long> initiatedFadeSequences = new HashSet<Long>();
420: boolean fadeCanceled = false;
421:
422: // if (SubstanceCoreUtilities.toBleedWatermark(list))
423: // return;
424:
425: FadeTracker fadeTrackerInstance = FadeTracker
426: .getInstance();
427: // Map<Integer, Object> currSelected = (Map<Integer, Object>)
428: // SubstanceListUI.this.list
429: // .getClientProperty(SubstanceListUI.SELECTED_INDICES);
430: for (int i = e.getFirstIndex(); i <= e.getLastIndex(); i++) {
431: if (i >= list.getModel().getSize())
432: continue;
433: if (list.isSelectedIndex(i)) {
434: // check if was selected before
435: if (!selectedIndices.containsKey(i)) {
436: // start fading in
437: // System.out.println("Fade in on index " + i);
438:
439: if (!fadeCanceled) {
440: long fadeId = fadeTrackerInstance
441: .trackFadeIn(
442: FadeKind.SELECTION,
443: list,
444: i,
445: false,
446: new CellRepaintCallback(
447: list, i));
448: initiatedFadeSequences.add(fadeId);
449: if (initiatedFadeSequences.size() > 25) {
450: cancelFades(initiatedFadeSequences);
451: initiatedFadeSequences.clear();
452: fadeCanceled = true;
453: }
454: }
455:
456: selectedIndices.put(i, list.getModel()
457: .getElementAt(i));
458: }
459: } else {
460: // check if was selected before and still points to the
461: // same element
462: if (selectedIndices.containsKey(i)) {
463: if (selectedIndices.get(i) == list
464: .getModel().getElementAt(i)) {
465: // start fading out
466: // System.out.println("Fade out on index " + i);
467:
468: if (!fadeCanceled) {
469: long fadeId = fadeTrackerInstance
470: .trackFadeOut(
471: FadeKind.SELECTION,
472: list,
473: i,
474: false,
475: new CellRepaintCallback(
476: list, i));
477: initiatedFadeSequences.add(fadeId);
478: if (initiatedFadeSequences.size() > 25) {
479: cancelFades(initiatedFadeSequences);
480: initiatedFadeSequences.clear();
481: fadeCanceled = true;
482: }
483: }
484: }
485: selectedIndices.remove(i);
486: }
487: }
488: }
489: }
490: };
491: list.getSelectionModel().addListSelectionListener(
492: substanceFadeSelectionListener);
493:
494: substanceFadeRolloverListener = new RolloverFadeListener();
495: list.addMouseMotionListener(substanceFadeRolloverListener);
496: list.addMouseListener(substanceFadeRolloverListener);
497:
498: substancePropertyChangeListener = new PropertyChangeListener() {
499: public void propertyChange(PropertyChangeEvent evt) {
500: if (SubstanceLookAndFeel.WATERMARK_TO_BLEED.equals(evt
501: .getPropertyName())) {
502: list.setOpaque(!SubstanceCoreUtilities
503: .toBleedWatermark(list));
504: }
505: }
506: };
507: list.addPropertyChangeListener(substancePropertyChangeListener);
508: }
509:
510: @Override
511: protected void uninstallListeners() {
512: list.getSelectionModel().removeListSelectionListener(
513: substanceFadeSelectionListener);
514: substanceFadeSelectionListener = null;
515:
516: list.removeMouseMotionListener(substanceFadeRolloverListener);
517: list.removeMouseListener(substanceFadeRolloverListener);
518: substanceFadeRolloverListener = null;
519:
520: list
521: .removePropertyChangeListener(substancePropertyChangeListener);
522: substancePropertyChangeListener = null;
523:
524: super .uninstallListeners();
525: }
526:
527: /*
528: * (non-Javadoc)
529: *
530: * @see javax.swing.plaf.basic.BasicListUI#paintCell(java.awt.Graphics, int,
531: * java.awt.Rectangle, javax.swing.ListCellRenderer,
532: * javax.swing.ListModel, javax.swing.ListSelectionModel, int)
533: */
534: @Override
535: protected void paintCell(Graphics g, int row, Rectangle rowBounds,
536: ListCellRenderer cellRenderer, ListModel dataModel,
537: ListSelectionModel selModel, int leadIndex) {
538: Object value = dataModel.getElementAt(row);
539: boolean cellHasFocus = list.hasFocus() && (row == leadIndex);
540: boolean isSelected = selModel.isSelectedIndex(row);
541:
542: final boolean isWatermarkBleed = SubstanceCoreUtilities
543: .toBleedWatermark(list);
544:
545: final Component rendererComponent = cellRenderer
546: .getListCellRendererComponent(list, value, row,
547: isSelected, cellHasFocus);
548:
549: int cx = rowBounds.x;
550: int cy = rowBounds.y;
551: int cw = rowBounds.width;
552: int ch = rowBounds.height;
553:
554: if (isFileList) {
555: // Shrink renderer to preferred size. This is mostly used on Windows
556: // where selection is only shown around the file name, instead of
557: // across the whole list cell.
558: int w = Math.min(cw,
559: rendererComponent.getPreferredSize().width + 4);
560: if (!isLeftToRight) {
561: cx += (cw - w);
562: }
563: cw = w;
564: }
565:
566: // Integer currRoIndex = (Integer) list
567: // .getClientProperty(ROLLED_OVER_INDEX);
568: boolean isRollover = ((rolledOverIndex >= 0) && (rolledOverIndex == row));
569:
570: // Respect the current composite set on the graphics - for
571: // JXPanel alpha channel
572: // float currFactor = 1.0f;
573: // Composite currComposite = ((Graphics2D) g).getComposite();
574: // if (currComposite instanceof AlphaComposite) {
575: // AlphaComposite ac = (AlphaComposite) currComposite;
576: // if (ac.getRule() == AlphaComposite.SRC_OVER)
577: // currFactor = ac.getAlpha();
578: // }
579:
580: Graphics2D g2d = (Graphics2D) g.create();
581: g2d.setComposite(TransitionLayout.getAlphaComposite(list, g));
582: SubstanceTextPainter textPainter = SubstanceLookAndFeel
583: .getCurrentTextPainter();
584: if (textPainter.needsBackgroundImage()) {
585: final Rectangle offsetCellRect = new Rectangle(0, 0, cw, ch);
586: textPainter.init(this .list, offsetCellRect, true);
587: // textPainter.setBackgroundImage(this.list, null, false, 0, 0);
588: textPainter
589: .attachCallback(new SubstanceTextPainter.BackgroundPaintingCallback() {
590: public void paintBackground(Graphics g) {
591: Graphics2D g2d = (Graphics2D) g.create();
592: if (!isWatermarkBleed) {
593: // fill with the renderer background color
594: g2d.setColor(rendererComponent
595: .getBackground());
596: g2d.fillRect(0, 0,
597: offsetCellRect.width,
598: offsetCellRect.height);
599: } else {
600: backgroundDelegate.fillAndWatermark(
601: g2d, list, rendererComponent
602: .getBackground(),
603: offsetCellRect);
604: }
605: g2d.dispose();
606: }
607: });
608: } else {
609: if (!isWatermarkBleed) {
610: // fill with the renderer background color
611: g2d.setColor(rendererComponent.getBackground());
612: g2d.fillRect(cx, cy, cw, ch);
613: } else {
614: backgroundDelegate.fillAndWatermark(g2d, this .list,
615: rendererComponent.getBackground(),
616: new Rectangle(cx, cy, cw, ch));
617: }
618: }
619:
620: ComponentState prevState = getPrevCellState(row);
621: ComponentState currState = getCellState(row);
622:
623: // Compute the alpha values for the animation.
624: float startAlpha = SubstanceThemeUtilities.getHighlightAlpha(
625: list, prevState);
626: float endAlpha = SubstanceThemeUtilities.getHighlightAlpha(
627: list, currState);
628:
629: FadeState state = SubstanceFadeUtilities.getFadeState(list,
630: row, FadeKind.SELECTION, FadeKind.ROLLOVER);
631: float totalAlpha = endAlpha;
632: float fadeCoef = 0.0f;
633: if (state != null) {
634: fadeCoef = state.getFadePosition();
635:
636: // compute the total alpha of the overlays.
637: if (state.isFadingIn()) {
638: totalAlpha = startAlpha + (endAlpha - startAlpha)
639: * fadeCoef / 10.0f;
640: } else {
641: totalAlpha = startAlpha + (endAlpha - startAlpha)
642: * (10.0f - fadeCoef) / 10.0f;
643: }
644:
645: if (state.isFadingIn())
646: fadeCoef = 10.0f - fadeCoef;
647: }
648:
649: final SubstanceTheme prevTheme = SubstanceThemeUtilities
650: .getHighlightTheme(list, prevState);
651: final SubstanceTheme currTheme = SubstanceThemeUtilities
652: .getHighlightTheme(list, currState);
653:
654: // System.out.println(row + ":" + prevTheme.getDisplayName() + "["
655: // + alphaForPrevBackground + "]:" + currTheme.getDisplayName()
656: // + "[" + alphaForCurrBackground + "]");
657:
658: if (textPainter.needsBackgroundImage()) {
659: final float finalTotalAlpha = totalAlpha;
660: final float finalFadeCoef = fadeCoef;
661:
662: // The text GC will be clipped to the renderer bound. To have the
663: // background painted in the correct location, enforce this by
664: // painting the background in 0, 0 position.
665: final Rectangle cellRect = new Rectangle(0, 0, cw, ch);
666: // textPainter.setBackgroundImage(this.list, rendererComponent
667: // .getBackground(), true, cx, cy);
668: textPainter
669: .attachCallback(new SubstanceTextPainter.BackgroundPaintingCallback() {
670: public void paintBackground(Graphics g) {
671: Graphics2D g2d = (Graphics2D) g.create();
672:
673: if (finalTotalAlpha > 0.0f) {
674: g2d.setComposite(TransitionLayout
675: .getAlphaComposite(list,
676: finalTotalAlpha, g));
677: SubstanceHighlightUtils.paintHighlight(
678: g2d, rendererComponent,
679: cellRect, 0.8f, null, currTheme
680: .getColorScheme(),
681: prevTheme.getColorScheme(),
682: finalFadeCoef);
683: g2d.setComposite(TransitionLayout
684: .getAlphaComposite(list, g));
685: }
686: g2d.dispose();
687: }
688: });
689: } else {
690: Rectangle cellRect = new Rectangle(cx, cy, cw, ch);
691: if (totalAlpha > 0.0f) {
692: g2d.setComposite(TransitionLayout.getAlphaComposite(
693: list, totalAlpha, g));
694: SubstanceHighlightUtils.paintHighlight(g2d,
695: rendererComponent, cellRect, 0.8f, null,
696: currTheme.getColorScheme(), prevTheme
697: .getColorScheme(), fadeCoef);
698: g2d.setComposite(TransitionLayout.getAlphaComposite(
699: list, g));
700: }
701: }
702:
703: this .list.putClientProperty(
704: SubstanceCoreUtilities.DO_NOT_FILL_BACKGROUND,
705: Boolean.TRUE);
706: if (rendererComponent instanceof JComponent) {
707: // Play with opacity to make our own gradient background
708: // on selected elements to show.
709: JComponent jRenderer = (JComponent) rendererComponent;
710: synchronized (jRenderer) {
711: boolean newOpaque = !(isSelected || isRollover || (state != null));
712: if (isWatermarkBleed)
713: newOpaque = false;
714:
715: Map<Component, Boolean> opacity = new HashMap<Component, Boolean>();
716: // System.out.println("Pre-painting at index " + row + " [" +
717: // value
718: // + "] " + (rendererComponent.isOpaque() ? "opaque" :
719: // "transparent")
720: // + " with bg " + rendererComponent.getBackground());
721: if (!newOpaque)
722: SubstanceCoreUtilities.makeNonOpaque(jRenderer,
723: opacity);
724: // System.out.println("Painting at index " + row + " [" + value
725: // + "] " + (newOpaque ? "opaque" : "transparent")
726: // + " with bg " + rendererComponent.getBackground());
727: rendererPane.paintComponent(g2d, rendererComponent,
728: list, cx, cy, cw, ch, true);
729: // System.out.println("Painting at index " + row + " [" + value
730: // + "] " + (newOpaque ? "opaque" : "transparent")
731: // + " with bg " + rendererComponent.getBackground());
732: if (!newOpaque)
733: SubstanceCoreUtilities.restoreOpaque(jRenderer,
734: opacity);
735: // System.out.println("Post-painting at index " + row + " [" +
736: // value
737: // + "] " + (rendererComponent.isOpaque() ? "opaque" :
738: // "transparent")
739: // + " with bg " + rendererComponent.getBackground());
740: }
741: } else {
742: rendererPane.paintComponent(g2d, rendererComponent, list,
743: cx, cy, cw, ch, true);
744: }
745: this .list.putClientProperty(
746: SubstanceCoreUtilities.DO_NOT_FILL_BACKGROUND, null);
747: g2d.dispose();
748: }
749:
750: /**
751: * Returns the previous state for the specified cell.
752: *
753: * @param cellIndex
754: * Cell index.
755: * @return The previous state for the specified cell.
756: */
757: public ComponentState getPrevCellState(int cellIndex) {
758: if (prevStateMap.containsKey(cellIndex))
759: return prevStateMap.get(cellIndex);
760: return ComponentState.DEFAULT;
761: }
762:
763: /**
764: * Returns the current state for the specified cell.
765: *
766: * @param cellIndex
767: * Cell index.
768: * @return The current state for the specified cell.
769: */
770: public ComponentState getCellState(int cellIndex) {
771: ButtonModel synthModel = new DefaultButtonModel();
772: synthModel.setEnabled(list.isEnabled());
773: // Integer currRoIndex = (Integer) list
774: // .getClientProperty(ROLLED_OVER_INDEX);
775: synthModel.setRollover((rolledOverIndex >= 0)
776: && (rolledOverIndex == cellIndex));
777: synthModel.setSelected(list.isSelectedIndex(cellIndex));
778: return ComponentState.getState(synthModel, null);
779: }
780:
781: /**
782: * Resets the rollover index.
783: */
784: public void resetRolloverIndex() {
785: rolledOverIndex = -1;
786: }
787:
788: /*
789: * (non-Javadoc)
790: *
791: * @see javax.swing.plaf.ComponentUI#update(java.awt.Graphics,
792: * javax.swing.JComponent)
793: */
794: @Override
795: public void update(Graphics g, JComponent c) {
796: SubstanceFillBackgroundDelegate.GLOBAL_INSTANCE.updateIfOpaque(
797: g, c);
798:
799: Graphics2D g2d = (Graphics2D) g.create();
800: // install state-aware alpha channel (support for skins that use
801: // translucency on disabled states).
802: // SubstanceTheme theme = SubstanceCoreUtilities.getTheme(this.list,
803: // true,
804: // true);
805: // float themeAlpha = theme.getThemeAlpha(this.list,
806: // this.list.isEnabled() ? ComponentState.DEFAULT
807: // : ComponentState.DISABLED_UNSELECTED);
808: // g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
809: // themeAlpha));
810: this.paint(g2d, c);
811: g2d.dispose();
812: }
813: }
|