/* Copyright (C) 2006 Christian Schneider
*
* This file is part of Nomad.
*
* Nomad is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Nomad 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Nomad; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.sf.nmedit.jtheme;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Point;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import net.sf.nmedit.jpatch.ImageSource;
import net.sf.nmedit.jpatch.ModuleDescriptions;
import net.sf.nmedit.jpatch.PModuleDescriptor;
import net.sf.nmedit.jtheme.util.JThemeUtils;
import net.sf.nmedit.nomad.core.swing.ButtonBarBuilder;
import net.sf.nmedit.nomad.core.swing.explorer.ContainerNode;
import net.sf.nmedit.nomad.core.swing.explorer.ExplorerTree;
import net.sf.nmedit.nomad.core.swing.explorer.LeafNode;
public class ModulePane extends JPanel
{
/**
*
*/
private static final long serialVersionUID = 6251114885486092481L;
private static ModulePane instance;
private ExplorerTree tree;
private ModuleDescriptions modules;
private ModulePreview preview;
private JPanel options;
private JCheckBox toggleMore;
private JTextField tfFilter;
private List<ContainerNode> categories = new ArrayList<ContainerNode>();
List<ContainerNode> visibleCategories = new ArrayList<ContainerNode>();
private ModulePane()
{
JPanel top = new JPanel(new BorderLayout());
options = new JPanel();
options.setVisible(false);
options.setLayout(new BorderLayout());
tree = new ExplorerTree();
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
preview = new ModulePreview();
Color c = preview.getBackground();
if (c != null)
preview.setBackground(c.darker());
preview.setBorder(BorderFactory.createLoweredBevelBorder());
Dimension previewSize = new Dimension(80, 60);
preview.setMinimumSize(previewSize);
preview.setPreferredSize(previewSize);
ButtonBarBuilder btnBarBuilder = new ButtonBarBuilder();
toggleMore = new JCheckBox("More");
toggleMore.setSelected(options.isVisible());
toggleMore.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
options.setVisible(toggleMore.isSelected());
options.revalidate();
}});
btnBarBuilder.add(toggleMore);
btnBarBuilder.addBox();
btnBarBuilder.addSpace();
btnBarBuilder.add(tree.createExpandAllAction());
btnBarBuilder.addSpace();
btnBarBuilder.add(tree.createCollapseAllAction());
setLayout(new BorderLayout());
add(top, BorderLayout.NORTH);
top.add(btnBarBuilder.getContainer(), BorderLayout.CENTER);
top.add(options, BorderLayout.SOUTH);
tfFilter = new JTextField();
(new TextFilterEventHandler()).install();
options.add(preview, BorderLayout.CENTER);
ButtonBarBuilder filterBar = new ButtonBarBuilder();
filterBar.add(new JLabel("Filter"));
filterBar.addSpace();
filterBar.add(tfFilter);
filterBar.addSpace();
JButton btnClear = new JButton("Clear");
btnClear.setToolTipText("clear");
btnClear.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
tfFilter.setText("");
}});
filterBar.add(btnClear);
options.add(filterBar.getContainer(), BorderLayout.SOUTH);
add(new JScrollPane(tree), BorderLayout.CENTER);
(new TreeDnDHandler(tree, this)).install();
TreeSelectionHandler selectionHandler = new TreeSelectionHandler();
tree.addTreeSelectionListener(selectionHandler);
}
private class TextFilterEventHandler implements DocumentListener,
Runnable
{
private String prevFilter = "";
public void install()
{
tfFilter.getDocument().addDocumentListener(this);
}
public void changedUpdate(DocumentEvent e)
{
filterUpdated();
}
public void insertUpdate(DocumentEvent e)
{
filterUpdated();
}
public void removeUpdate(DocumentEvent e)
{
filterUpdated();
}
private void filterUpdated()
{
SwingUtilities.invokeLater(this);
}
public void run()
{
String t = tfFilter.getText();
if (t == null) t = "";
else t = t.trim().toLowerCase();
if (prevFilter.equals(t)) return;
prevFilter = t;
filter();
}
private void filter()
{
for (ContainerNode n: visibleCategories)
tree.getRoot().remove(n);
visibleCategories.clear();
for (ContainerNode csrc : categories)
{
ContainerNode filteredContainer = filter(csrc);
if (filteredContainer != null)
{
visibleCategories.add(filteredContainer);
tree.getRoot().add(filteredContainer);
}
}
tree.fireRootChanged();
tree.expandAll();
}
private ContainerNode filter(ContainerNode csrc)
{
ContainerNode cdst = null;
if (matches(csrc.getTitle()))
cdst = new ContainerNode(csrc.getParent(), csrc.getTitle());
for (int i=0;i<csrc.getChildCount();i++)
{
ModuleDescriptorNode nsrc =
(ModuleDescriptorNode) csrc.getChildAt(i);
if (matches(nsrc.getSearchText()))
{
if (cdst == null)
cdst = new ContainerNode(csrc.getParent(), csrc.getTitle());
cdst.addChild(new ModuleDescriptorNode(
cdst, nsrc.getIcon(),
nsrc.getText(),
nsrc.getDescriptor()));
}
}
return cdst;
}
private boolean matches(String text)
{
if (prevFilter.length() == 0)
return true;
text = text.toLowerCase();
if (text.contains(prevFilter))
return true;
else
return false;
}
}
public void setTheme(JTContext context)
{
preview.setUIContext(context);
}
public JTContext getTheme()
{
return preview.getUIContext();
}
private class TreeSelectionHandler implements TreeSelectionListener
{
public void valueChanged(TreeSelectionEvent e)
{
TreePath path = e.getNewLeadSelectionPath();
if (path == null)
return;
Object last = path.getLastPathComponent();
PModuleDescriptor descriptor = null;
if (last instanceof ModuleDescriptorNode)
{
descriptor = ((ModuleDescriptorNode) last).getDescriptor();
}
setSelection(descriptor);
}
private void setSelection(PModuleDescriptor descriptor)
{
preview.setModule(descriptor);
}
}
private static class TreeDnDHandler implements DragGestureListener
{
private ExplorerTree tree;
private DragSource dragSource;
private ModulePane pane;
public TreeDnDHandler(ExplorerTree tree, ModulePane pane)
{
this.tree = tree;
this.pane = pane;
}
public void install()
{
dragSource = DragSource.getDefaultDragSource();
dragSource.createDefaultDragGestureRecognizer(tree, DnDConstants.ACTION_COPY, this);
}
public void dragGestureRecognized(DragGestureEvent dge)
{
Point origin = dge.getDragOrigin();
TreePath path = tree.getPathForLocation(origin.x, origin.y);
if (path != null)
{
Object lpc = path.getLastPathComponent();
if (lpc != null && lpc instanceof ModuleDescriptorNode)
{
PModuleDescriptor descriptor =
((ModuleDescriptorNode)lpc).getDescriptor();
Transferable t = JThemeUtils.createTransferable(pane.getTheme(), descriptor);
dge.startDrag(DragSource.DefaultCopyDrop, t);
}
}
}
}
public static ModulePane getSharedInstance()
{
if (instance == null)
instance = new ModulePane();
return instance;
}
public void setModules(ModuleDescriptions modules)
{
if (this.modules != modules)
{
if (this.modules != null)
uninstall(this.modules);
this.modules = modules;
preview.setModule(null);
preview.setModules(modules);
if (modules != null)
install(modules);
}
}
protected void uninstall(ModuleDescriptions modules)
{
tree.getRoot().removeAllChildren();
tree.fireRootChanged();
}
protected void install(ModuleDescriptions modules)
{
visibleCategories.clear();
categories.clear();
Map<String, List<PModuleDescriptor>> categoryMap = new HashMap<String, List<PModuleDescriptor>>();
buildCategories(categoryMap, modules);
Comparator<PModuleDescriptor> order = new ModuleDescriptorOrder();
List<String> categories = new ArrayList<String>();
categories.addAll(categoryMap.keySet());
Collections.sort(categories);
TreeNode root = tree.getRoot();
for (String cat: categories)
{
List<PModuleDescriptor> catList = categoryMap.get(cat);
Collections.sort(catList, order);
ContainerNode catNode = new ContainerNode(root, cat);
tree.addRootNode(catNode);
ModulePane.this.categories.add(catNode);
visibleCategories.add(catNode);
for (PModuleDescriptor m: catList)
{
ImageSource source = m.get16x16IconSource();
Image img = modules.getImage(source);
Icon icon = img != null ? new ImageIcon(img) : null;
// ImageSource.*/
String text = m.getStringAttribute("fullname");
double cycles = m.getDoubleAttribute("cycles", -1);
if (cycles >=0)
{
text += " ("+(Math.round(cycles*100d)/100d)+"%)";
}
LeafNode n = new ModuleDescriptorNode(catNode, icon, text, m);
catNode.addChild(n);
}
}
}
private void buildCategories(Map<String, List<PModuleDescriptor>> categoryMap, ModuleDescriptions modules)
{
for (PModuleDescriptor module : modules)
{
if (module.isInstanciable())
{
String cat = module.getCategory();
List<PModuleDescriptor> catList = categoryMap.get(cat);
if (catList == null)
{
catList = new ArrayList<PModuleDescriptor>();
categoryMap.put(cat, catList);
}
catList.add(module);
}
}
}
private static class ModuleDescriptorOrder implements Comparator<PModuleDescriptor>
{
public int compare(PModuleDescriptor o1, PModuleDescriptor o2)
{
String n1 = o1.getName();
String n2 = o2.getName();
if (n1 == n2) return 0;
if (n1 == null) return 1;
if (n2 == null) return -1;
return n1.compareTo(n2);
}
}
private static class ModuleDescriptorNode extends LeafNode
{
private PModuleDescriptor descriptor;
public ModuleDescriptorNode(TreeNode parent, Icon icon, String text, PModuleDescriptor m)
{
super(parent, icon, text);
this.setToolTipText(m.getName());
this.descriptor = m;
}
public String getSearchText()
{
return descriptor==null?getText():descriptor.getName() + ";" + descriptor.getStringAttribute("fullname");
}
public PModuleDescriptor getDescriptor()
{
return descriptor;
}
}
}