/*
* Copyright (C) 2010 Markus Echterhoff <tam@edu.uni-klu.ac.at>
*
* This file is part of EvoPaint.
*
* EvoPaint 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 EvoPaint. If not, see <http://www.gnu.org/licenses/>.
*/
package evopaint.gui.rulesetmanager;
import evopaint.Configuration;
import evopaint.gui.util.DragDropList;
import evopaint.pixel.rulebased.Rule;
import evopaint.pixel.rulebased.RuleSet;
import evopaint.util.CollectionNode;
import evopaint.util.ExceptionHandler;
import evopaint.util.RuleSetNode;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseListener;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.LineBorder;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
/**
*
* @author Markus Echterhoff <tam@edu.uni-klu.ac.at>
*/
public class JRuleList extends JPanel implements TreeSelectionListener, ListDataListener, ListSelectionListener {
private Configuration configuration;
private DragDropList list;
private DefaultListModel model;
private RuleSetNode lastSelectedRuleSetNode;
private boolean dirty;
private JRuleSetTree tree;
private JButton btnEdit;
private JButton btnCopy;
private JButton btnDelete;
public boolean isDirty() {
return dirty;
}
public void clean() {
if (false == dirty) {
return;
}
assert(lastSelectedRuleSetNode != null);
// replace rules in rule set node
RuleSet ruleSet = (RuleSet)lastSelectedRuleSetNode.getUserObject();
ruleSet.setRules(getRules());
lastSelectedRuleSetNode.setUserObject(ruleSet);
// inform the tree listeners about the changes
CollectionNode parentNode = (CollectionNode)lastSelectedRuleSetNode.getParent();
DefaultTreeModel treeModel = (DefaultTreeModel)tree.getModel();
treeModel.nodesChanged(parentNode,
new int [] {parentNode.getIndex(lastSelectedRuleSetNode)});
dirty = false;
}
public List<Rule> getRules() {
List<Rule> rules = new ArrayList<Rule>(model.capacity());
for (int i = 0; i < model.size(); i++) {
rules.add((Rule)model.get(i));
}
return rules;
}
public void replaceSelectedRule(Rule rule) {
model.set(list.getSelectedIndex(), rule);
dirty = true;
}
public Rule getSelectedRule() {
if (list.isSelectionEmpty()) {
return null;
}
return (Rule)list.getSelectedValue();
}
public int locationToIndex(Point location) {
return list.locationToIndex(location);
}
public void valueChanged(TreeSelectionEvent e) {
// if this is a removal event of a dirty rule set, we don't care
// or else we will reveive a nive NullPointException for trying...
if (false == e.isAddedPath()) {
dirty = false;
return;
}
clean();
DefaultMutableTreeNode node = (DefaultMutableTreeNode)e.getPath().getLastPathComponent();
Object userObject = node.getUserObject();
if (userObject == null) {
return;
}
if (false == (userObject instanceof RuleSet)) {
return;
}
// save rule set node for writing it back to disc if changed
lastSelectedRuleSetNode = (RuleSetNode)node;
// save data listeners
ListDataListener [] listeners = model.getListDataListeners();
// create new model for rule set
model = new DefaultListModel();
RuleSet ruleSet = (RuleSet)userObject;
for (Rule rule : ruleSet.getRules()) {
model.addElement(rule);
}
// re-add data listeners
for (ListDataListener l : listeners) {
model.addListDataListener(l);
}
list.setModel(model);
}
public void contentsChanged(ListDataEvent e) {
//System.out.println("dirty");
dirty = true;
}
public void intervalAdded(ListDataEvent e) {
//System.out.println("dirty");
dirty = true;
}
public void intervalRemoved(ListDataEvent e) {
//System.out.println("dirty");
dirty = true;
}
public JRuleList(Configuration configuration, JRuleSetTree tree, ActionListener btnEditListener, MouseListener doubleClickListener) {
this.configuration = configuration;
this.dirty = false;
this.tree = tree;
tree.addTreeSelectionListener(this);
setLayout(new BorderLayout());
setBorder(new LineBorder(Color.GRAY));
setBackground(Color.WHITE);
model = new DefaultListModel();
model.addListDataListener(this);
list = new DragDropList(model);
list.setBorder(null);
list.setCellRenderer(new RuleCellRenderer());
list.addMouseListener(doubleClickListener);
list.addListSelectionListener(this);
list.setSelectionForeground(Color.BLACK);
list.setSelectionBackground(new Color(0xe9eff8));
// DO NOT GIVE THIS LIST A PREFERRED SIZE, IT WILL FUCK UP THE H-SCROLLBAR
// DON'T: // ruleList.setPreferredSize(new Dimension(100,100));
final JScrollPane scrollPaneForRuleList = new JScrollPane(list,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPaneForRuleList.setBorder(null);
scrollPaneForRuleList.setViewportBorder(null);
add(scrollPaneForRuleList, BorderLayout.CENTER);
final JPanel controlPanel = new JPanel();
controlPanel.setBackground(new Color(0xF2F2F5));
btnEdit = new JButton(new ImageIcon(getClass().getResource("icons/button-edit.png")));
btnEdit.setToolTipText("Opens the selected rule in the Rule Editor");
btnEdit.setEnabled(false);
btnEdit.addActionListener(btnEditListener);
JButton btnAdd = new JButton(new ImageIcon(getClass().getResource("icons/button-add.png")));
btnAdd.setToolTipText("Adds a new rule and opens it in the Rule Editor");
btnAdd.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Rule newRule = new Rule();
model.addElement(newRule);
list.setSelectedValue(newRule, true);
btnEdit.doClick();
}
});
controlPanel.add(btnAdd);
controlPanel.add(btnEdit);
btnCopy = new JButton(new ImageIcon(getClass().getResource("icons/button-copy.png")));
btnCopy.setToolTipText("Copies the selected rule");
btnCopy.setEnabled(false);
btnCopy.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (list.isSelectionEmpty()) {
return;
}
int index = list.getSelectedIndex();
final Rule protoRule = (Rule)model.get(index);
Rule newRule = null;
try {
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(outByteStream);
out.writeObject(protoRule);
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(outByteStream.toByteArray()));
newRule = (Rule) in.readObject();
} catch (ClassNotFoundException ex) {
ExceptionHandler.handle(ex, true);
} catch (IOException ex) {
ExceptionHandler.handle(ex, true);
}
model.addElement(newRule);
}
});
controlPanel.add(btnCopy);
btnDelete = new JButton(new ImageIcon(getClass().getResource("icons/button-delete.png")));
btnDelete.setToolTipText("Deletes the selected rule");
btnDelete.setEnabled(false);
btnDelete.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (list.isSelectionEmpty()) {
return;
}
int index = list.getSelectedIndex();
model.remove(index);
if (index > 0) {
list.setSelectedIndex(index - 1);
} else if (model.size() > 0) {
list.setSelectedIndex(0);
}
}
});
controlPanel.add(btnDelete);
add(controlPanel, BorderLayout.SOUTH);
}
public void valueChanged(ListSelectionEvent e) {
// user interaction is said to cause multiple selection events
// this magic method sorts out uninteresting ones
if (e.getValueIsAdjusting()) {
return;
}
if (list.getSelectedIndex() == -1) { // no rule selected
btnEdit.setEnabled(false);
btnCopy.setEnabled(false);
btnDelete.setEnabled(false);
return;
}
btnEdit.setEnabled(true);
btnCopy.setEnabled(true);
btnDelete.setEnabled(true);
}
private class RuleCellRenderer extends DefaultListCellRenderer {
@Override
public Component getListCellRendererComponent(
JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
JLabel ret = (JLabel)super.getListCellRendererComponent(list, ((Rule)value).toHTML(), index, isSelected, cellHasFocus);
ret.setText("<html>" + ret.getText() + "</html>");
return ret;
}
}
}