swingx-master/swingx-demos/swingx-demos-content/src/main/java/org/jdesktop/swingx/demos/xlist/XListDemo.java
/*
 * $Id$
 *
 * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.jdesktop.swingx.demos.xlist;

import static org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Comparator;

import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SortOrder;
import javax.swing.SwingUtilities;

import org.jdesktop.application.Action;
import org.jdesktop.beansbinding.BeanProperty;
import org.jdesktop.beansbinding.Binding;
import org.jdesktop.beansbinding.BindingGroup;
import org.jdesktop.beansbinding.Bindings;
import org.jdesktop.swingx.JXCollapsiblePane;
import org.jdesktop.swingx.JXList;
import org.jdesktop.swingx.JXPanel;
import org.jdesktop.swingx.JXTitledSeparator;
import org.jdesktop.swingx.binding.DisplayInfo;
import org.jdesktop.swingx.binding.DisplayInfoConverter;
import org.jdesktop.swingx.binding.LabelHandler;
import org.jdesktop.swingx.decorator.ColorHighlighter;
import org.jdesktop.swingx.decorator.ComponentAdapter;
import org.jdesktop.swingx.decorator.HighlightPredicate;
import org.jdesktop.swingx.decorator.Highlighter;
import org.jdesktop.swingx.decorator.PainterHighlighter;
import org.jdesktop.swingx.demos.search.Contributor;
import org.jdesktop.swingx.demos.search.Contributors;
import org.jdesktop.swingx.painter.MattePainter;
import org.jdesktop.swingx.renderer.DefaultListRenderer;
import org.jdesktop.swingx.renderer.StringValue;
import org.jdesktop.swingx.renderer.StringValues;
import org.jdesktop.swingx.rollover.RolloverProducer;
import org.jdesktop.swingx.sort.DefaultSortController;
import org.jdesktop.swingx.util.PaintUtils;
import org.jdesktop.swingxset.util.DemoUtils;
import org.jdesktop.swingxset.util.DisplayValues;

import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.factories.Borders;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import com.sun.swingset3.DemoProperties;

/**
 * A demo for the {@code JXList}.
 *
 * @author Karl George Schaefer
 */
//TODO implement
@DemoProperties(
    value = "JXList Demo",
    category = "Data",
    description = "Demonstrates JXList, an enhanced list component.",
    sourceFiles = {
        "org/jdesktop/swingx/demos/xlist/XListDemo.java",
        "org/jdesktop/swingx/demos/xlist/resources/XListDemo.properties"
    }
)
@SuppressWarnings("serial")
public class XListDemo extends JXPanel {
    
    private JXList list;
    private JComboBox comparatorCombo;
    private JButton toggleSortOrder;
    private JButton resetSortOrder;
    private JCheckBox rolloverEnabledBox;
    private JComboBox highlighterCombo;

    public XListDemo() {
        super(new BorderLayout());
        initComponents();
        configureComponents();
        DemoUtils.injectResources(this);
        bind();
    }

//---------------- public api for Binding/Action control
    
    @Action
    // <snip> JXList sorting
    //  api to toggle sorts
    public void toggleSortOrder() {
        list.toggleSortOrder();
    }
    // </snip>

    @Action
    public void resetSortOrder() {
        list.resetSortOrder();
    }
    
    public void setComparator(Comparator<?> comparator) {
        // <snip> JXList sorting
        //  configure the comparator to use in sorting
        list.setComparator(comparator);
        if (list.getSortOrder() != SortOrder.UNSORTED) {
            // PENDING missing refresh api?
            ((DefaultSortController<?>) list.getRowSorter()).sort();
        }
        // </snip>
    }
    
    
    public void setRolloverHighlighter(Highlighter hl) {
        list.setHighlighters(hl);
    }
 
    public void setRolloverEnabled(boolean enabled) {
        list.setRolloverEnabled(enabled);
        list.setToolTipText(list.isRolloverEnabled() ? 
                DemoUtils.getResourceString(getClass(), "stickyRolloverToolTip") : null);
    }
//------------------- ui configuration    
    private void configureComponents() {
        
        // <snip> JXList rendering
        // custom String representation: concat various element fields
        StringValue sv = new StringValue() {

            @Override
            public String getString(Object value) {
                if (value instanceof Contributor) {
                    Contributor c = (Contributor) value;
                    return c.getFirstName() + " " + c.getLastName() + " (" + c.getMerits() + ")";
                }
                return StringValues.TO_STRING.getString(value);
            }
            
        };
        // PENDING JW: add icon (see demos in swingx)
        // set a renderer configured with the custom string converter
        list.setCellRenderer(new DefaultListRenderer(sv));
        // </snip>
        
        // PENDING JW: add visual clue to currentl sortorder
        toggleSortOrder.setAction(DemoUtils.getAction(this, "toggleSortOrder"));
        resetSortOrder.setAction(DemoUtils.getAction(this, "resetSortOrder"));
        
        comparatorCombo.setRenderer(
                new DefaultListRenderer(DisplayValues.DISPLAY_INFO_DESCRIPTION));
        highlighterCombo.setRenderer(
                new DefaultListRenderer(DisplayValues.DISPLAY_INFO_DESCRIPTION));
        
        // demo specific config
        DemoUtils.setSnippet("JXList sorting", toggleSortOrder, resetSortOrder, comparatorCombo);
        DemoUtils.setSnippet("JXList rollover support", rolloverEnabledBox, highlighterCombo);
        DemoUtils.setSnippet("JXList rendering", list);
    }

    @SuppressWarnings("unchecked")
    private void bind() {
        // list properties
        // <snip> JXlist sorting
        // enable auto-create RowSorter
        list.setAutoCreateRowSorter(true);
        list.setModel(Contributors.getContributorListModel());
        //</snip>
        
        // control combos
        comparatorCombo.setModel(createComparators());
        highlighterCombo.setModel(createRolloverHighlighters());
        
        BindingGroup group = new BindingGroup();
        group.addBinding(Bindings.createAutoBinding(READ, 
                rolloverEnabledBox, BeanProperty.create("selected"),
                this, BeanProperty.create("rolloverEnabled")));
        Binding comparatorBinding = Bindings.createAutoBinding(READ, 
                comparatorCombo, BeanProperty.create("selectedItem"),
                this, BeanProperty.create("comparator"));
        comparatorBinding.setConverter(new DisplayInfoConverter<Comparator<?>>());
        group.addBinding(comparatorBinding);
    
        Binding rolloverBinding = Bindings.createAutoBinding(READ, 
                highlighterCombo, BeanProperty.create("selectedItem"),
                this, BeanProperty.create("rolloverHighlighter"));
        rolloverBinding.setConverter(new DisplayInfoConverter<Highlighter>());
        group.addBinding(rolloverBinding);
        
        group.bind();
    }

    private ComboBoxModel createRolloverHighlighters() {
        DefaultComboBoxModel model = new DefaultComboBoxModel();
        // <snip> JXList rollover support
        // simple decorations of rollover row 
        model.addElement(new DisplayInfo<Highlighter>("Background Color",
                new ColorHighlighter(HighlightPredicate.ROLLOVER_ROW, Color.MAGENTA, null)));
        model.addElement(new DisplayInfo<Highlighter>("Foreground Color",
                new ColorHighlighter(HighlightPredicate.ROLLOVER_ROW, null, Color.MAGENTA)));
        // </snip>
        model.addElement(new DisplayInfo<Highlighter>("Related Merit", 
                createExtendedRolloverDecoration()));
        return model;
    }

    private Highlighter createExtendedRolloverDecoration() {
        Color color = PaintUtils.setAlpha(Color.YELLOW, 100);
        final PainterHighlighter hl = new PainterHighlighter(HighlightPredicate.NEVER, 
                new MattePainter(color));
        // <snip> JXList rollover support
        // listen to changes of cell-rollover property
        // and set a Highlighters custom predicate accordingly
        PropertyChangeListener l = new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                Point location = (Point) evt.getNewValue();
                int row = -1;
                if (location != null) {
                    row = location.y;
                }
                hl.setHighlightPredicate(new MeritRangeHighlightPredicate(
                        row < 0 ? null : list.getElementAt(row))); 
            }
            
        };
        list.addPropertyChangeListener(RolloverProducer.ROLLOVER_KEY, l);
        // </snip>
        return hl;
    }

    public static class MeritRangeHighlightPredicate implements HighlightPredicate {

        private Contributor compare;

        public MeritRangeHighlightPredicate(Object object) {
            this.compare = object instanceof Contributor ? (Contributor) object : null;
        }

        @Override
        // <snip> JXList rollover support
        // custom HighlightPredicate which compare the current value
        // against a fixed value and returns true if "near"
        public boolean isHighlighted(Component renderer,
                ComponentAdapter adapter) {
            if (compare == null) return false;
            if (!(adapter.getValue() instanceof Contributor)) return false;
            Contributor contributor = (Contributor) adapter.getValue();
            return contributor.getMerits() >= compare.getMerits() - 5 && 
                contributor.getMerits() <= compare.getMerits() + 5;
        }
        // </snip>
        
    }
    
    private ComboBoxModel createComparators() {
        DefaultComboBoxModel model = new DefaultComboBoxModel();
        // <snip> JXList sorting
        //  null comparator defaults to comparing by the display string
        model.addElement(new DisplayInfo<Comparator<?>>("None (by display string)", 
                null));
        // compare by Comparable as implemented by the elements
        model.addElement(new DisplayInfo<Comparator<?>>("Comparable (by lastname)", 
                DefaultSortController.COMPARABLE_COMPARATOR));
        // custom comparator
        Comparator<Contributor> meritComparator = new Comparator<Contributor>() {

            @Override
            public int compare(Contributor o1, Contributor o2) {
                return o1.getMerits() - o2.getMerits();
            }
        };
        // </snip>
        model.addElement(new DisplayInfo<Comparator<?>>("Custom (by merits)", meritComparator));
        return model;
    }

//-------------------- init ui
    
    private void initComponents() {
        list = new JXList();
        list.setName("list");

        JPanel monthViewContainer = new JXPanel();
        FormLayout formLayout = new FormLayout(
                "5dlu, f:d:g ", // l:4dlu:n, f:d:g", // columns
                "c:d:n " +
                ", t:4dlu:n, f:d:g " +
                ", t:4dlu:n, c:d:n" +
                ", t:4dlu:n, c:d:n" +
                ", t:4dlu:n, c:d:n"
        ); // rows
        PanelBuilder builder = new PanelBuilder(formLayout, monthViewContainer);
        builder.setBorder(Borders.DLU4_BORDER);
//        CellConstraints cl = new CellConstraints();
        CellConstraints cc = new CellConstraints();
        
        JXTitledSeparator areaSeparator = new JXTitledSeparator();
        areaSeparator.setName("listSeparator");
        builder.add(areaSeparator, cc.xywh(1, 1, 2, 1));
        builder.add(new JScrollPane(list), cc.xywh(2, 3, 1, 1));
        
        
        add(monthViewContainer, BorderLayout.CENTER);
        
        JComponent extended = createExtendedConfigPanel();
        add(extended, BorderLayout.EAST);
    }

    private JComponent createExtendedConfigPanel() {
        JXCollapsiblePane painterControl = new JXCollapsiblePane();
        FormLayout formLayout = new FormLayout(
                "5dlu, r:d:n, l:4dlu:n, f:d:g", // , l:4dlu:n, f:d:g", // columns
                "c:d:n " +
                ", t:4dlu:n, c:d:n " +
                ", t:4dlu:n, c:d:n" +
                ", t:4dlu:n, c:d:n" +
                ", t:4dlu:n, c:d:n" +
                ", t:4dlu:n, c:d:n" +
                ", t:4dlu:n, c:d:n" +
                ", t:4dlu:n, c:d:n"
        ); // rows
        PanelBuilder builder = new PanelBuilder(formLayout, painterControl);
        builder.setBorder(Borders.DLU4_BORDER);
        CellConstraints cl = new CellConstraints();
        CellConstraints cc = new CellConstraints();
        
        JXTitledSeparator areaSeparator = new JXTitledSeparator();
        areaSeparator.setName("extendedSeparator");
        builder.add(areaSeparator, cc.xywh(1, 1, 4, 1));
        
        int labelColumn = 2;
        int widgetColumn = labelColumn + 2;
        int currentRow = 3;

        toggleSortOrder = new JButton();
        toggleSortOrder.setName("toggleSortOrder");
        builder.add(toggleSortOrder, cc.xywh(labelColumn, currentRow, 3, 1));
        currentRow += 2;
        
        resetSortOrder = new JButton();
        resetSortOrder.setName("resetSortOrder");
        builder.add(resetSortOrder, cc.xywh(labelColumn, currentRow, 3, 1));
        currentRow += 2;
        
        comparatorCombo = new JComboBox();
        comparatorCombo.setName("comparatorCombo");
        JLabel comparatorComboLabel = builder.addLabel(
                "", cl.xywh(labelColumn, currentRow, 1, 1),
                comparatorCombo, cc.xywh(widgetColumn, currentRow, 1, 1));
        comparatorComboLabel.setName("comparatorComboLabel");
        LabelHandler.bindLabelFor(comparatorComboLabel, comparatorCombo);
        currentRow += 2;
        
        currentRow += 2;
        JXTitledSeparator rolloverSeparator = new JXTitledSeparator();
        rolloverSeparator.setName("rolloverSeparator");
        builder.add(rolloverSeparator, cc.xywh(1, currentRow, 4, 1));
        currentRow += 2;

          rolloverEnabledBox = new JCheckBox();
          rolloverEnabledBox.setName("rolloverBox");
          builder.add(rolloverEnabledBox, cc.xywh(labelColumn, currentRow, 3, 1));
          currentRow += 2;
          
          highlighterCombo = new JComboBox();
          highlighterCombo.setName("highlighterCombo");
          JLabel highlighterComboLabel = builder.addLabel(
                  "", cl.xywh(labelColumn, currentRow, 1, 1),
                  highlighterCombo, cc.xywh(widgetColumn, currentRow, 1, 1));
          highlighterComboLabel.setName("highlighterComboLabel");
          LabelHandler.bindLabelFor(highlighterComboLabel, highlighterCombo);
          currentRow += 2;

        return painterControl;
    }

    /**
     * main method allows us to run as a standalone demo.
     */
    /**
     * main method allows us to run as a standalone demo.
     */
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame(XListDemo.class.getAnnotation(DemoProperties.class).value());
                
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.getContentPane().add(new XListDemo());
                frame.setPreferredSize(new Dimension(800, 600));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

//--------------------- dummy api (to keep bbb happy)
    
    public Comparator<?> getComparator() {
        return null;
    }
    
    public Highlighter getRolloverHighlighter() {
        return null;
    }
    
    public boolean isRolloverEnabeld() {
        return list.isRolloverEnabled();
    }
}
