/*
* 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;
import evopaint.gui.util.ColorIcon;
import evopaint.gui.util.FairyDustIcon;
import evopaint.interfaces.IChanging;
import evopaint.interfaces.IChangeListener;
import evopaint.pixel.PixelColor;
import evopaint.pixel.rulebased.Action;
import evopaint.pixel.rulebased.Rule;
import evopaint.pixel.rulebased.RuleSet;
import java.awt.Color;
import java.awt.Component;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import javax.swing.Icon;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
/**
* Represents the paint our paint brush or fill tool can use to paint on the
* canvas. Paint in evopaint consists of a color and a rule set. This class has
* multiple purposes. For once it is the paint managing unit, residing in the
* configuration, second it can be initialized to be a concrete Paint object
* to paint with and third it contains the paint history including its
* graphical representation. Classes who are interested in changes of paint
* should implement the <code>IPaintChangeListener</code> interface and
* subscribe to the managing unit of this class residing in the configuration
* to be noticed whenever the current paint changes.
*
* @author Markus Echterhoff <tam@edu.uni-klu.ac.at>
*/
public class Paint implements IChanging {
public static final int COLOR = 0;
public static final int FAIRY_DUST = 1;
public static final int EXISTING_COLOR = 2;
public static final int RULE_SET = 0;
public static final int NO_RULE_SET = 1;
public static final int EXISTING_RULE_SET = 2;
private Configuration configuration;
private LinkedList<Paint> paintHistory;
private Paint currentPaint;
private JPopupMenu paintHistoryMenu;
private List<IChangeListener> changeListeners;
private int colorMode;
private int ruleSetMode;
private PixelColor color;
private RuleSet ruleSet;
public Paint(Configuration configuration) {
this.configuration = configuration;
this.paintHistory = new LinkedList<Paint>();
this.paintHistoryMenu = new JPopupMenu();
this.changeListeners = new ArrayList<IChangeListener>();
this.currentPaint = new Paint(configuration, Paint.COLOR, Paint.NO_RULE_SET, new PixelColor(0xFF0000), null);
}
private Paint(Configuration configuration, int colorMode, int ruleSetMode, PixelColor color, RuleSet ruleSet) {
this.configuration = configuration;
this.colorMode = colorMode;
this.ruleSetMode = ruleSetMode;
this.color = color;
this.ruleSet = ruleSet;
}
public void showHistory(Component invoker, Point location) {
if (paintHistory.size() < 1) {
return;
}
paintHistoryMenu.removeAll();
for (final Paint paint : paintHistory) {
JMenuItem menuItem = paint.toMenuItem();
menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
changePaint(paint);
rememberCurrent();
}
});
paintHistoryMenu.add(menuItem);
}
paintHistoryMenu.show(invoker, location.x, location.y);
}
public PixelColor getCurrentColor() {
return currentPaint.color;
}
public int getCurrentColorMode() {
return currentPaint.colorMode;
}
public int getCurrentRuleSetMode() {
return currentPaint.ruleSetMode;
}
public RuleSet getCurrentRuleSet() {
return currentPaint.ruleSet;
}
public void changeCurrentColor(PixelColor color) {
changePaint(new Paint(configuration, Paint.COLOR,
currentPaint.ruleSetMode, color, currentPaint.ruleSet));
}
public void changeCurrentColorMode(int colorMode) {
changePaint(new Paint(configuration, colorMode, currentPaint.ruleSetMode,
currentPaint.color, currentPaint.ruleSet));
}
public void changeCurrentRuleSet(RuleSet ruleSet) {
for (Rule rule : ruleSet.getRules()) {
configuration.usedActions.add(rule.getAction()); // they will never be modified or deleted, so no synchronization necessary
}
changePaint(new Paint(configuration, currentPaint.colorMode,
Paint.RULE_SET, currentPaint.color, ruleSet));
}
public void changeCurrentRuleSetMode(int ruleSetMode) {
changePaint(new Paint(configuration, currentPaint.colorMode,
ruleSetMode, currentPaint.color, currentPaint.ruleSet));
}
public void rememberCurrent() {
// reset position in history if history contains paint
if (paintHistory.size() > 1 &&
false == paintHistory.getFirst().equals(currentPaint) &&
paintHistory.contains(currentPaint)) {
//System.out.println("contained");
paintHistory.remove(currentPaint);
paintHistory.addFirst(currentPaint);
}
// else add it as top most element and remove the last if we have too many
else if (false == (colorMode == Paint.EXISTING_COLOR && ruleSet == null) &&
false == paintHistory.contains(currentPaint)) {
//System.out.println("new");
paintHistory.addFirst(currentPaint);
if (paintHistory.size() > configuration.paintHistorySize) {
paintHistory.removeLast();
}
}
}
private void changePaint(Paint newPaint) {
currentPaint = newPaint;
for (IChangeListener changeObserver : changeListeners) {
changeObserver.changed();
}
}
public void addChangeListener(IChangeListener subscriber) {
if (this.changeListeners.contains(subscriber)) {
return;
}
this.changeListeners.add(subscriber);
}
public void removeChangeListener(IChangeListener subscriber) {
this.changeListeners.remove(subscriber);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Paint other = (Paint) obj;
if (this.colorMode != other.colorMode) {
return false;
}
if (this.ruleSetMode != other.ruleSetMode) {
return false;
}
if (this.color != other.color && (this.color == null || !this.color.equals(other.color))) {
return false;
}
if (this.ruleSet != other.ruleSet && (this.ruleSet == null) || this.ruleSet != other.ruleSet) { // we want object comparison only... || !this.ruleSet.equals(other.ruleSet)
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 5;
hash = 97 * hash + this.colorMode;
hash = 97 * hash + this.ruleSetMode;
hash = 97 * hash + (this.color != null ? this.color.hashCode() : 0);
hash = 97 * hash + (this.ruleSet != null ? this.ruleSet.hashCode() : 0);
return hash;
}
private JMenuItem toMenuItem() {
Icon icon = null;
switch (colorMode) {
case COLOR: icon = new ColorIcon(16, 16, new Color(color.getInteger()));
break;
case FAIRY_DUST: icon = new FairyDustIcon(configuration, 16, 16);
break;
case EXISTING_COLOR: icon = null;
break;
default: assert(false);
}
String ruleSetName = null;
switch (ruleSetMode) {
case RULE_SET: ruleSetName = ruleSet.getName();
break;
case NO_RULE_SET:
break;
case EXISTING_RULE_SET: ruleSetName = "<use existing>";
break;
default: assert(false);
}
JMenuItem ret = new JMenuItem(ruleSetName, icon);
return ret;
}
}