swingx-master/swingx-demos/swingx-demos-content/src/main/java/org/jdesktop/swingx/demos/treetable/TreeTableDemo.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.treetable;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Window;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.table.TableModel;
import javax.swing.tree.TreePath;

import org.jdesktop.application.Action;
import org.jdesktop.swingx.JXPanel;
import org.jdesktop.swingx.JXTreeTable;
import org.jdesktop.swingx.decorator.AbstractHighlighter;
import org.jdesktop.swingx.decorator.ColorHighlighter;
import org.jdesktop.swingx.decorator.ComponentAdapter;
import org.jdesktop.swingx.decorator.HighlightPredicate;
import org.jdesktop.swingx.demos.tree.TreeDemoIconValues.LazyLoadingIconValue;
import org.jdesktop.swingx.demos.tree.XTreeDemo;
import org.jdesktop.swingx.event.AbstractInputEventDispatcher;
import org.jdesktop.swingx.renderer.DefaultTableRenderer;
import org.jdesktop.swingx.renderer.DefaultTreeRenderer;
import org.jdesktop.swingx.renderer.IconValue;
import org.jdesktop.swingx.renderer.StringValue;
import org.jdesktop.swingx.renderer.StringValues;
import org.jdesktop.swingx.table.ColumnFactory;
import org.jdesktop.swingx.table.TableColumnExt;
import org.jdesktop.swingx.treetable.TreeTableModel;
import org.jdesktop.swingx.util.PaintUtils;
import org.jdesktop.swingxset.JXDemoFrame;
import org.jdesktop.swingxset.util.ComponentModels;
import org.jdesktop.swingxset.util.DemoUtils;

import com.sun.swingset3.DemoProperties;

/**
 * A demo for the {@code JXTreeTable}.
 *
 * @author Karl George Schaefer
 */
@DemoProperties(
    value = "JXTreeTable Demo",
    category = "Data",
    description = "Demonstrates JXTreeTable, a tree-based, grid component.",
    sourceFiles = {
            "org/jdesktop/swingx/demos/treetable/TreeTableDemo.java",
            "org/jdesktop/swingx/demos/tree/TreeDemoIconValues.java",
        "org/jdesktop/swingxset/JXDemoFrame.java",
        "org/jdesktop/swingx/demos/treetable/resources/TreeTableDemo.properties"
    }
)
@SuppressWarnings("serial")
public class TreeTableDemo extends JPanel {
    
    @SuppressWarnings("unused")
    private static final Logger LOG = Logger.getLogger(TreeTableDemo.class
            .getName());
    
    private JXTreeTable treeTable;
    private boolean initialized;
    private JButton refreshButton;
    private JButton expandButton;
    private JButton collapseButton;
    private AbstractInputEventDispatcher inputEventDispatcher;
    private AbstractHighlighter mouseOverHighlighter;
    
    public TreeTableDemo() {
        super(new BorderLayout());
        initComponents();
        configureComponents();
        DemoUtils.injectResources(this);
        bind();
    }
    
//---------------- public api for Binding/Action control
    

    @Action
    public void refreshModel() {
        treeTable.setTreeTableModel(createTreeModel());
        expandAll();
        treeTable.packColumn(treeTable.getHierarchicalColumn(), -1);
    }
    
    // <snip> JXTreeTable convenience api
    // expand/collapse all nodes
    @Action
    public void expandAll() {
        treeTable.expandAll();
    }

    @Action
    public void collapseAll() {
        treeTable.collapseAll();
    }
    // </snip>
    
//------------------- config/bind  
    

    private void bind() {
        // <snip>JXTreeTable column customization
        // configure and install a custom columnFactory, arguably data related ;-)
        ColumnFactory factory = new ColumnFactory() {
            String[] columnNameKeys = { "componentType", "componentName", "componentLocation", "componentSize" };

            @Override
            public void configureTableColumn(TableModel model,
                    TableColumnExt columnExt) {
                super.configureTableColumn(model, columnExt);
                if (columnExt.getModelIndex() < columnNameKeys.length) {
                    columnExt.setTitle(DemoUtils.getResourceString(TreeTableDemo.class, 
                            columnNameKeys[columnExt.getModelIndex()]));
                }
            }
            
        };
        treeTable.setColumnFactory(factory);
        // </snip>

    }
   
    private void configureComponents() {
        // <snip> JXTreeTable rendering
        // StringValue provides node text, used in hierarchical column
        StringValue sv = new StringValue() {
            
            @Override
            public String getString(Object value) {
                if (value instanceof Component) {
                    Component component = (Component) value;
                    String simpleName = component.getClass().getSimpleName();
                    if (simpleName.length() == 0){
                        // anonymous class
                        simpleName = component.getClass().getSuperclass().getSimpleName();
                    }
                    return simpleName;
                }
                return StringValues.TO_STRING.getString(value);
            }
        };
        // </snip>
        
        // StringValue for lazy icon loading
        StringValue keyValue = new StringValue() {
            
            @Override
            public String getString(Object value) {
                if (value == null) return "";
                String simpleClassName = value.getClass().getSimpleName();
                if (simpleClassName.length() == 0){
                    // anonymous class
                    simpleClassName = value.getClass().getSuperclass().getSimpleName();
                }
                return simpleClassName + ".png";
            }
        };
        // <snip> JXTreeTable rendering
        // IconValue provides node icon (same as in XTreeDemo)
        IconValue iv = new LazyLoadingIconValue(XTreeDemo.class, keyValue, "fallback.png");
        // create and set a tree renderer using the custom Icon-/StringValue
        treeTable.setTreeCellRenderer(new DefaultTreeRenderer(iv, sv));
        // string representation for use of Dimension/Point class
        StringValue locSize = new StringValue() {
            
            @Override
            public String getString(Object value) {
                int x;
                int y;
                if (value instanceof Dimension) {
                    x = ((Dimension) value).width;
                    y = ((Dimension) value).height;
                } else if (value instanceof Point) {
                    x = ((Point) value).x;
                    y = ((Point) value).y;
                } else {
                    return StringValues.TO_STRING.getString(value);
                }
                return "(" + x + ", " + y + ")";
            }
        };
        treeTable.setDefaultRenderer(Point.class, new DefaultTableRenderer(locSize, JLabel.CENTER));
        treeTable.setDefaultRenderer(Dimension.class, treeTable.getDefaultRenderer(Point.class));
        // </snip>
        
        mouseOverHighlighter = new ColorHighlighter(HighlightPredicate.NEVER, 
                PaintUtils.setSaturation(Color.MAGENTA, 0.3f), null);
        treeTable.addHighlighter(mouseOverHighlighter);
        
        treeTable.setColumnControlVisible(true);
        
        
        refreshButton.setAction(DemoUtils.getAction(this, "refreshModel"));
        expandButton.setAction(DemoUtils.getAction(this, "expandAll"));
        collapseButton.setAction(DemoUtils.getAction(this, "collapseAll"));
        
        // Demo specific config
        DemoUtils.setSnippet("JXTreeTable convenience api", expandButton, collapseButton);
        DemoUtils.setSnippet("JXTreeTable rendering", treeTable);
    }

    
    // <snip> Input-/FocusEvent notification 
    // update Highlighter's predicate to highlight the tree row
    // which contains the component under the current mouse position
    protected void updateHighlighter(Component component) {
        mouseOverHighlighter.setHighlightPredicate(HighlightPredicate.NEVER);
        if (component != null) {
        
            List<Component> pathList = new ArrayList<Component>();
            while (component != null) {
                pathList.add(0, component);
                component = component.getParent();
            }
            final TreePath treePath = new TreePath(pathList.toArray());
            treeTable.scrollPathToVisible(treePath);
            HighlightPredicate predicate = new HighlightPredicate() {
                
                @Override
                public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
                    return adapter.row == treeTable.getRowForPath(treePath);
                }
            };
            mouseOverHighlighter.setHighlightPredicate(predicate);
            // </snip>

        }
    }

    /**
     * Overridden to create and install the component tree model.
     */
    @Override
    public void addNotify() {
        super.addNotify();
        if (!initialized) {
            initialized = true;
            refreshModel();
        }
        installInputEventListener();
    }

    @Override
    public void removeNotify() {
        uninstallInputEventListener();
        super.removeNotify();
    }
    // <snip> Input-/FocusEvent notification 
    // install custom dispatcher with demo frame
    private void installInputEventListener() {
        if (!(SwingUtilities.getWindowAncestor(this) instanceof JXDemoFrame)) return;
        JXDemoFrame window = (JXDemoFrame) SwingUtilities.getWindowAncestor(this);
        if (inputEventDispatcher == null) {
            inputEventDispatcher = new AbstractInputEventDispatcher() {
                // updates Highlighter for mouseEntered/mouseExited
                // of all components in the frame's container hierarchy
                @Override
                protected void processMouseEvent(MouseEvent e) {
                    if (MouseEvent.MOUSE_ENTERED == e.getID()) {
                        updateHighlighter(e.getComponent());
                    } else if (MouseEvent.MOUSE_EXITED == e.getID()) {
                        updateHighlighter(null);
                    }
                }
                
            };
        }
        window.setInputEventDispatcher(inputEventDispatcher); 
        // </snip>

    }

    private void uninstallInputEventListener() {
        if (!(SwingUtilities.getWindowAncestor(this) instanceof JXDemoFrame)) return;
        JXDemoFrame window = (JXDemoFrame) SwingUtilities.getWindowAncestor(this);
        window.setInputEventDispatcher(null);
        updateHighlighter(null);
        
    }

    private TreeTableModel createTreeModel() {
       Window window = SwingUtilities.getWindowAncestor(this);
       return ComponentModels.getTreeTableModel(window != null ? window : this);
    }

    //----------------- init
    
    private void initComponents() {
        treeTable = new JXTreeTable();
        treeTable.setName("componentTreeTable");
        add(new JScrollPane(treeTable));

        JComponent control = new JXPanel();
        refreshButton = new JButton();
        refreshButton.setName("refreshButton");

        expandButton = new JButton();
        expandButton.setName("expandTreeTableButton");
        
        collapseButton = new JButton();
        collapseButton.setName("collapseTreeTableButton");
        
//        control.add(refreshButton);
        control.add(expandButton);
        control.add(collapseButton);
        add(control, BorderLayout.SOUTH);

    }
    
    /**
     * 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(TreeTableDemo.class.getAnnotation(DemoProperties.class).value());
                
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.getContentPane().add(new TreeTableDemo());
                frame.setPreferredSize(new Dimension(800, 600));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }


}

