001: /*
002: * $Id: PainterVisualCheck.java,v 1.5 2007/01/25 13:19:01 kleopatra Exp $
003: *
004: * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
005: * Santa Clara, California 95054, U.S.A. All rights reserved.
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020: *
021: */
022: package org.jdesktop.swingx.renderer;
023:
024: import java.awt.Color;
025: import java.awt.Component;
026: import java.awt.event.ActionEvent;
027: import java.awt.event.MouseAdapter;
028: import java.awt.event.MouseEvent;
029: import java.beans.PropertyChangeEvent;
030: import java.beans.PropertyChangeListener;
031: import java.util.ArrayList;
032: import java.util.Collections;
033: import java.util.List;
034: import java.util.logging.Logger;
035:
036: import javax.swing.AbstractButton;
037: import javax.swing.Action;
038: import javax.swing.DefaultListModel;
039: import javax.swing.ImageIcon;
040: import javax.swing.JCheckBox;
041: import javax.swing.JLabel;
042: import javax.swing.KeyStroke;
043: import javax.swing.ListModel;
044: import javax.swing.table.TableModel;
045:
046: import org.jdesktop.swingx.InteractiveTestCase;
047: import org.jdesktop.swingx.JXFrame;
048: import org.jdesktop.swingx.JXList;
049: import org.jdesktop.swingx.JXPanel;
050: import org.jdesktop.swingx.JXTable;
051: import org.jdesktop.swingx.action.AbstractActionExt;
052: import org.jdesktop.swingx.action.ActionContainerFactory;
053: import org.jdesktop.swingx.decorator.ComponentAdapter;
054: import org.jdesktop.swingx.decorator.ConditionalHighlighter;
055: import org.jdesktop.swingx.decorator.Highlighter;
056: import org.jdesktop.swingx.decorator.AlternateRowHighlighter.UIAlternateRowHighlighter;
057: import org.jdesktop.swingx.painter.ImagePainter;
058: import org.jdesktop.swingx.painter.Painter;
059: import org.jdesktop.swingx.painter.gradient.BasicGradientPainter;
060: import org.jdesktop.swingx.table.ColumnControlButton;
061: import org.jdesktop.test.AncientSwingTeam;
062:
063: /**
064: * Experiments with highlighters using painters.<p>
065: *
066: * Links
067: * <ul>
068: * <li> <a href="">Sneak preview II - Transparent Highlighter</a>
069: * </ul>
070: *
071: * @author Jeanette Winzenburg
072: */
073: public class PainterVisualCheck extends InteractiveTestCase {
074: private static final Logger LOG = Logger
075: .getLogger(PainterVisualCheck.class.getName());
076:
077: public static void main(String args[]) {
078: // setSystemLF(true);
079: PainterVisualCheck test = new PainterVisualCheck();
080: try {
081: test.runInteractiveTests();
082: // test.runInteractiveTests(".*Icon.*");
083: } catch (Exception e) {
084: System.err
085: .println("exception when executing interactive tests:");
086: e.printStackTrace();
087: }
088: }
089:
090: /**
091: * Use GradientPainter for value-based background highlighting
092: * Use SwingX extended default renderer.
093: */
094: public void interactiveTableGradientHighlight() {
095: TableModel model = new AncientSwingTeam();
096: JXTable table = new JXTable(model);
097: // DefaultTableRenderer renderer = new DefaultTableRenderer();
098: // selection should shine through in white part
099: final Painter painter = new BasicGradientPainter(0.0f, 0.0f,
100: Color.YELLOW, 0.75f, (float) 0.5, GradientHighlighter
101: .getTransparentColor(Color.WHITE, 0));
102: ConditionalHighlighter gradientHighlighter = new ConditionalHighlighter(
103: null, null, -1, -1) {
104:
105: @Override
106: public Component highlight(Component renderer,
107: ComponentAdapter adapter) {
108: boolean highlight = needsHighlight(adapter);
109: if (highlight && (renderer instanceof PainterAware)) {
110: ((PainterAware) renderer).setPainter(painter);
111: return renderer;
112: }
113: return renderer;
114: }
115:
116: @Override
117: protected boolean test(ComponentAdapter adapter) {
118: return adapter.getValue().toString().contains("y");
119: }
120:
121: };
122: table.addHighlighter(gradientHighlighter);
123: // table.setDefaultRenderer(Object.class, renderer);
124: JXFrame frame = showWithScrollingInFrame(table,
125: "painter-aware renderer with value-based highlighting");
126: getStatusBar(frame)
127: .add(
128: new JLabel(
129: "gradient background of cells with value's containing 'y'"));
130: }
131:
132: //------------------------ Transparent painter aware button as rendering component
133:
134: /**
135: * Use a custom button controller to show both checkbox icon and text to
136: * render Actions in a JXList. Apply striping and a simple gradient highlighter.
137: */
138: public void interactiveTableWithListColumnControl() {
139: TableModel model = new AncientSwingTeam();
140: JXTable table = new JXTable(model);
141: JXList list = new JXList();
142: Highlighter highlighter = new UIAlternateRowHighlighter();
143: table.addHighlighter(highlighter);
144: list.setHighlighters(highlighter, new GradientHighlighter());
145: // quick-fill and hook to table columns' visibility state
146: configureList(list, table, false);
147: // a custom rendering button controller showing both checkbox and text
148: ButtonProvider wrapper = new ButtonProvider() {
149: @Override
150: protected AbstractButton createRendererComponent() {
151: return new JRendererCheckBox();
152: }
153:
154: @Override
155: protected void format(CellContext context) {
156: if (!(context.getValue() instanceof AbstractActionExt)) {
157: super .format(context);
158: return;
159: }
160: rendererComponent
161: .setSelected(((AbstractActionExt) context
162: .getValue()).isSelected());
163: rendererComponent.setText(((AbstractActionExt) context
164: .getValue()).getName());
165: }
166:
167: };
168: wrapper.setHorizontalAlignment(JLabel.LEADING);
169: list.setCellRenderer(new DefaultListRenderer(wrapper));
170: JXFrame frame = showWithScrollingInFrame(table, list,
171: "checkbox list-renderer - striping and gradient");
172: addStatusMessage(
173: frame,
174: "fake editable list: space/doubleclick on selected item toggles column visibility");
175: frame.pack();
176: }
177:
178: /**
179: * A Highlighter which applies a simple yellow to white-transparent
180: * gradient to a PainterAware rendering component. The yellow can
181: * be toggled to half-transparent.
182: */
183: public static class GradientHighlighter extends Highlighter {
184:
185: private Painter painter;
186:
187: private boolean yellowTransparent;
188:
189: /**
190: */
191: public GradientHighlighter() {
192: super (Color.YELLOW, null);
193: }
194:
195: /**
196: * @param yellowTransparent
197: */
198: public void setYellowTransparent(boolean yellowTransparent) {
199: if (this .yellowTransparent == yellowTransparent)
200: return;
201: this .yellowTransparent = yellowTransparent;
202: painter = null;
203: fireStateChanged();
204: }
205:
206: @Override
207: public Component highlight(Component renderer,
208: ComponentAdapter adapter) {
209: if (renderer instanceof PainterAware) {
210: Painter painter = getPainter(0.7f);
211: ((PainterAware) renderer).setPainter(painter);
212:
213: } else {
214: renderer.setBackground(Color.YELLOW.darker());
215: }
216: return renderer;
217: }
218:
219: private Painter getPainter(float end) {
220: if (painter == null) {
221: Color startColor = getTransparentColor(Color.YELLOW,
222: yellowTransparent ? 125 : 254);
223: Color endColor = getTransparentColor(Color.WHITE, 0);
224: painter = new BasicGradientPainter(0.0f, 0.0f,
225: startColor, end, 0.f, endColor);
226: }
227: return painter;
228: }
229:
230: private static Color getTransparentColor(Color base,
231: int transparency) {
232: return new Color(base.getRed(), base.getGreen(), base
233: .getBlue(), transparency);
234: }
235:
236: }
237:
238: // ------------------------
239:
240: /**
241: * Use highlighter with background image painter. Shared by table and list.
242: */
243: public void interactiveIconPainterHighlight() {
244: TableModel model = new AncientSwingTeam();
245: JXTable table = new JXTable(model);
246: ComponentProvider<JLabel> controller = new LabelProvider(
247: JLabel.RIGHT);
248: table.getColumn(0).setCellRenderer(
249: new DefaultTableRenderer(controller));
250: ImageIcon icon = new ImageIcon(JXPanel.class
251: .getResource("resources/images/kleopatra.jpg"));
252: final ImagePainter imagePainter = new ImagePainter(icon
253: .getImage());
254: Highlighter gradientHighlighter = new Highlighter() {
255:
256: @Override
257: public Component highlight(Component renderer,
258: ComponentAdapter adapter) {
259: if ((adapter.column == 0)
260: && (renderer instanceof PainterAware)) {
261: ((PainterAware) renderer).setPainter(imagePainter);
262: }
263: return renderer;
264: }
265:
266: };
267: Highlighter alternateRowHighlighter = new UIAlternateRowHighlighter();
268: table.addHighlighter(alternateRowHighlighter);
269: table.addHighlighter(gradientHighlighter);
270: // re-use component controller and highlighter in a JXList
271: JXList list = new JXList(createListNumberModel(), true);
272: list.setCellRenderer(new DefaultListRenderer(controller));
273: list.addHighlighter(alternateRowHighlighter);
274: list.addHighlighter(gradientHighlighter);
275: list.toggleSortOrder();
276: final JXFrame frame = showWithScrollingInFrame(table, list,
277: "image highlighting plus striping");
278: frame.pack();
279: }
280:
281: // ----------------- Transparent gradient on default (swingx) rendering label
282:
283: /**
284: * Use transparent gradient painter for value-based background highlighting
285: * with SwingX extended default renderer. Shared by table and list with
286: * striping.
287: */
288: public void interactiveNumberProportionalGradientHighlightPlusStriping() {
289: TableModel model = new AncientSwingTeam();
290: JXTable table = new JXTable(model);
291: ComponentProvider<JLabel> controller = new LabelProvider(
292: JLabel.RIGHT);
293: // table.setDefaultRenderer(Number.class, new DefaultTableRenderer(
294: // controller));
295: final ValueBasedGradientHighlighter gradientHighlighter = createTransparentGradientHighlighter();
296: Highlighter alternateRowHighlighter = new UIAlternateRowHighlighter();
297: table.addHighlighter(alternateRowHighlighter);
298: table.addHighlighter(gradientHighlighter);
299: // re-use component controller and highlighter in a JXList
300: JXList list = new JXList(createListNumberModel(), true);
301: list.setCellRenderer(new DefaultListRenderer(controller));
302: list.addHighlighter(alternateRowHighlighter);
303: list.addHighlighter(gradientHighlighter);
304: list.toggleSortOrder();
305: final JXFrame frame = showWithScrollingInFrame(table, list,
306: "transparent value relative highlighting plus striping");
307: addStatusMessage(frame, "uses a PainterAwareLabel in renderer");
308: // crude binding to play with options - the factory is incomplete...
309: getStatusBar(frame).add(
310: createTransparencyToggle(gradientHighlighter));
311: frame.pack();
312: }
313:
314: /**
315: * Use transparent gradient painter for value-based background highlighting
316: * with SwingX extended default renderer. Shared by table and list with
317: * background color.
318: */
319: public void interactiveNumberProportionalGradientHighlight() {
320: TableModel model = new AncientSwingTeam();
321: JXTable table = new JXTable(model);
322: table.setBackground(Highlighter.ledgerBackground
323: .getBackground());
324: ComponentProvider<JLabel> controller = new LabelProvider(
325: JLabel.RIGHT);
326: // table.setDefaultRenderer(Number.class, new DefaultTableRenderer(
327: // controller));
328: ValueBasedGradientHighlighter gradientHighlighter = createTransparentGradientHighlighter();
329: table.addHighlighter(gradientHighlighter);
330: // re-use component controller and highlighter in a JXList
331: JXList list = new JXList(createListNumberModel(), true);
332: list.setBackground(table.getBackground());
333: list.setCellRenderer(new DefaultListRenderer(controller));
334: list.addHighlighter(gradientHighlighter);
335: list.toggleSortOrder();
336: JXFrame frame = showWithScrollingInFrame(table, list,
337: "transparent value relative highlighting");
338: addStatusMessage(frame,
339: "uses the default painter-aware label in renderer");
340: // crude binding to play with options - the factory is incomplete...
341: getStatusBar(frame).add(
342: createTransparencyToggle(gradientHighlighter));
343: frame.pack();
344: }
345:
346: // -------------------- Value-based transparent gradient highlighter
347:
348: /**
349: * A Highlighter which applies a value-proportional gradient to PainterAware
350: * rendering components if the value is a Number. The gradient is a simple
351: * yellow to white-transparent paint. The yellow can be toggled to
352: * half-transparent.
353: */
354: public static class ValueBasedGradientHighlighter extends
355: ConditionalHighlighter {
356: float maxValue = 100;
357:
358: private Painter painter;
359:
360: private boolean yellowTransparent;
361:
362: /**
363: */
364: public ValueBasedGradientHighlighter() {
365: super (null, null, -1, -1);
366: }
367:
368: /**
369: * @param yellowTransparent
370: */
371: public void setYellowTransparent(boolean yellowTransparent) {
372: if (this .yellowTransparent == yellowTransparent)
373: return;
374: this .yellowTransparent = yellowTransparent;
375: fireStateChanged();
376: }
377:
378: @Override
379: public Component highlight(Component renderer,
380: ComponentAdapter adapter) {
381: boolean highlight = needsHighlight(adapter);
382: if (highlight && (renderer instanceof PainterAware)) {
383: float end = getEndOfGradient((Number) adapter
384: .getValue());
385: if (end > 1) {
386: renderer.setBackground(Color.YELLOW.darker());
387: } else if (end > 0.02) {
388: Painter painter = getPainter(end);
389: ((PainterAware) renderer).setPainter(painter);
390: }
391: return renderer;
392: }
393: return renderer;
394: }
395:
396: private Painter getPainter(float end) {
397: Color startColor = getTransparentColor(Color.YELLOW,
398: yellowTransparent ? 125 : 254);
399: Color endColor = getTransparentColor(Color.WHITE, 0);
400: painter = new BasicGradientPainter(0.0f, 0.0f, startColor,
401: end, 0.f, endColor);
402: return painter;
403: }
404:
405: private Color getTransparentColor(Color base, int transparency) {
406: return new Color(base.getRed(), base.getGreen(), base
407: .getBlue(), transparency);
408: }
409:
410: private float getEndOfGradient(Number number) {
411: float end = number.floatValue() / maxValue;
412: return end;
413: }
414:
415: @Override
416: protected boolean test(ComponentAdapter adapter) {
417: return adapter.getValue() instanceof Number;
418: }
419:
420: }
421:
422: /**
423: * creates and returns a highlighter with a value-based transparent gradient
424: * if the cell content type is a Number.
425: *
426: * @return
427: */
428: private ValueBasedGradientHighlighter createTransparentGradientHighlighter() {
429: return new ValueBasedGradientHighlighter();
430: }
431:
432: /**
433: * Creates and returns a checkbox to toggle the gradient's yellow
434: * transparency.
435: *
436: * @param gradientHighlighter
437: * @return
438: */
439: private JCheckBox createTransparencyToggle(
440: final ValueBasedGradientHighlighter gradientHighlighter) {
441: ActionContainerFactory factory = new ActionContainerFactory();
442: // toggle opaque optimatization
443: AbstractActionExt toggleTransparent = new AbstractActionExt(
444: "yellow transparent") {
445:
446: public void actionPerformed(ActionEvent e) {
447: gradientHighlighter.setYellowTransparent(isSelected());
448: }
449:
450: };
451: toggleTransparent.setStateAction();
452: JCheckBox box = new JCheckBox();
453: factory.configureButton(box, toggleTransparent, null);
454: return box;
455: }
456:
457: //----------------- Utility
458: /**
459: *
460: * @return a ListModel wrapped around the AncientSwingTeam's Number column.
461: */
462: private ListModel createListNumberModel() {
463: AncientSwingTeam tableModel = new AncientSwingTeam();
464: int colorColumn = 3;
465: DefaultListModel model = new DefaultListModel();
466: for (int i = 0; i < tableModel.getRowCount(); i++) {
467: model.addElement(tableModel.getValueAt(i, colorColumn));
468: }
469: return model;
470: }
471:
472: /**
473: * Fills the list with a collection of actions (as returned from the
474: * table's column control). Binds space and double-click to toggle
475: * the action's selected state.
476: *
477: * note: this is just an example to show-off the button renderer in a list!
478: * ... it's very dirty!!
479: *
480: * @param list
481: * @param table
482: */
483: private void configureList(final JXList list, final JXTable table,
484: boolean useRollover) {
485: final List<Action> actions = new ArrayList();
486: ColumnControlButton columnControl = new ColumnControlButton(
487: table, null) {
488:
489: @Override
490: protected void addVisibilityActionItems() {
491: actions
492: .addAll(Collections
493: .unmodifiableList(getColumnVisibilityActions()));
494: }
495:
496: };
497: list.setModel(createListeningListModel(actions));
498: // action toggling selected state of selected list item
499: final Action toggleSelected = new AbstractActionExt(
500: "toggle column visibility") {
501:
502: public void actionPerformed(ActionEvent e) {
503: if (list.isSelectionEmpty())
504: return;
505: AbstractActionExt selectedItem = (AbstractActionExt) list
506: .getSelectedValue();
507: selectedItem.setSelected(!selectedItem.isSelected());
508: }
509:
510: };
511: if (useRollover) {
512: list.setRolloverEnabled(true);
513: } else {
514: // bind action to space
515: list.getInputMap().put(KeyStroke.getKeyStroke("SPACE"),
516: "toggleSelectedActionState");
517: }
518: list.getActionMap().put("toggleSelectedActionState",
519: toggleSelected);
520: // bind action to double-click
521: MouseAdapter adapter = new MouseAdapter() {
522:
523: @Override
524: public void mouseClicked(MouseEvent e) {
525: if (e.getClickCount() == 2) {
526: toggleSelected.actionPerformed(null);
527: }
528: }
529:
530: };
531: list.addMouseListener(adapter);
532:
533: }
534:
535: /**
536: * Creates and returns a ListModel containing the given actions.
537: * Registers a PropertyChangeListener with each action to get
538: * notified and fire ListEvents.
539: *
540: * @param actions the actions to add into the model.
541: * @return the filled model.
542: */
543: private ListModel createListeningListModel(
544: final List<Action> actions) {
545: final DefaultListModel model = new DefaultListModel() {
546:
547: DefaultListModel reallyThis = this ;
548:
549: @Override
550: public void addElement(Object obj) {
551: super .addElement(obj);
552: ((Action) obj).addPropertyChangeListener(l);
553:
554: }
555:
556: PropertyChangeListener l = new PropertyChangeListener() {
557:
558: public void propertyChange(PropertyChangeEvent evt) {
559: int index = indexOf(evt.getSource());
560: if (index >= 0) {
561: fireContentsChanged(reallyThis, index, index);
562: }
563: }
564:
565: };
566: };
567: for (Action action : actions) {
568: model.addElement(action);
569: }
570: return model;
571: }
572:
573: }
|