package com.kreative.paint.tool;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Cursor;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.RoundRectangle2D;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.Border;
import com.kreative.paint.ToolContext;
import com.kreative.paint.form.CustomOption;
import com.kreative.paint.form.Form;
import com.kreative.paint.form.IntegerEnumOption;
import com.kreative.paint.util.CursorUtils;
public class MagicMarkerTool extends AbstractPaintTool implements ToolOptions.Custom {
// <dead-alewives>I wanna implement MAGIC MARKER!</dead-alewives>
private static final int K = 0xFF000000;
private static final int W = 0xFFFFFFFF;
private static final Image icon = ToolUtilities.makeIcon(
16, 16,
new int[] {
0,0,0,0,0,0,0,0,0,0,0,K,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,K,K,K,0,0,0,
0,0,0,0,0,0,0,0,0,K,K,K,K,K,0,0,
0,0,0,0,0,0,0,0,K,K,K,K,K,K,K,0,
0,0,0,0,0,0,0,K,K,K,K,K,K,K,0,0,
0,0,0,0,0,0,K,K,K,K,K,K,K,0,0,0,
0,0,0,0,0,K,K,K,K,K,K,K,0,0,0,0,
0,0,0,0,K,K,K,K,K,K,K,0,0,0,0,0,
0,0,0,K,0,K,K,K,K,K,0,0,0,0,0,0,
0,0,K,K,K,0,K,K,K,0,0,0,0,0,0,0,
0,K,K,K,K,K,0,K,0,0,0,0,0,0,0,0,
0,K,K,K,K,K,K,0,0,0,0,0,0,0,0,0,
0,K,K,K,K,K,0,0,0,0,0,0,0,0,0,0,
0,K,K,K,K,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
}
);
private static final Image icon1 = ToolUtilities.makeIcon(
16, 16,
new int[] {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,K,K,0,0,0,0,0,
0,0,0,0,0,0,0,0,K,K,K,K,0,0,0,0,
0,0,0,0,0,0,0,K,K,K,K,K,0,0,0,0,
0,0,0,0,0,0,K,K,K,K,K,0,0,0,0,0,
0,0,0,0,0,K,K,K,K,K,0,0,0,0,0,0,
0,0,0,0,K,K,K,K,K,0,0,0,0,0,0,0,
0,0,0,0,K,K,K,K,0,0,0,0,0,0,0,0,
0,0,0,0,K,K,K,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
}
);
private static final Image icon2 = ToolUtilities.makeIcon(
16, 16,
new int[] {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,K,K,K,0,0,0,0,0,
0,0,0,0,0,0,0,K,K,K,K,K,0,0,0,0,
0,0,0,0,0,0,K,K,K,K,K,K,K,0,0,0,
0,0,0,0,0,K,K,K,K,K,K,K,K,0,0,0,
0,0,0,0,K,K,K,K,K,K,K,K,K,0,0,0,
0,0,0,0,K,K,K,K,K,K,K,K,0,0,0,0,
0,0,0,0,K,K,K,K,K,K,K,0,0,0,0,0,
0,0,0,0,K,K,K,K,K,K,0,0,0,0,0,0,
0,0,0,0,0,K,K,K,K,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
}
);
private static final Image icon3 = ToolUtilities.makeIcon(
16, 16,
new int[] {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,K,K,0,0,0,
0,0,0,0,0,0,0,0,0,0,K,K,K,K,0,0,
0,0,0,0,0,0,0,0,0,K,K,K,K,K,K,0,
0,0,0,0,0,0,0,0,K,K,K,K,K,K,K,K,
0,0,0,0,0,0,0,K,K,K,K,K,K,K,K,K,
0,0,0,0,0,0,K,K,K,K,K,K,K,K,K,0,
0,0,0,0,0,K,K,K,K,K,K,K,K,K,0,0,
0,0,0,0,K,K,K,K,K,K,K,K,K,0,0,0,
0,0,0,K,K,K,K,K,K,K,K,K,0,0,0,0,
0,0,K,K,K,K,K,K,K,K,K,0,0,0,0,0,
0,K,K,K,K,K,K,K,K,K,0,0,0,0,0,0,
K,K,K,K,K,K,K,K,K,0,0,0,0,0,0,0,
K,K,K,K,K,K,K,K,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
}
);
private static final Image icon4 = ToolUtilities.makeIcon(
16, 16,
new int[] {
0,0,0,0,0,0,0,0,0,K,K,0,0,0,0,0,
0,0,0,0,0,0,0,0,K,K,K,K,0,0,0,0,
0,0,0,0,0,0,0,K,K,K,K,K,K,0,0,0,
0,0,0,0,0,0,K,K,K,K,K,K,K,K,0,0,
0,0,0,0,0,K,K,K,K,K,K,K,K,K,K,0,
0,0,0,0,K,K,K,K,K,K,K,K,K,K,K,K,
0,0,0,K,K,K,K,K,K,K,K,K,K,K,K,K,
0,0,K,K,K,K,K,K,K,K,K,K,K,K,K,K,
0,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,
K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,0,
K,K,K,K,K,K,K,K,K,K,K,K,K,K,0,0,
K,K,K,K,K,K,K,K,K,K,K,K,K,0,0,0,
K,K,K,K,K,K,K,K,K,K,K,K,0,0,0,0,
K,K,K,K,K,K,K,K,K,K,K,0,0,0,0,0,
K,K,K,K,K,K,K,K,K,K,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
}
);
private static final Cursor curs1 = CursorUtils.makeCursor(
18, 18,
new int[] {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,W,W,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,W,K,K,W,0,0,0,0,0,
0,0,0,0,0,0,0,0,W,K,K,K,K,W,0,0,0,0,
0,0,0,0,0,0,0,W,K,K,K,K,K,W,0,0,0,0,
0,0,0,0,0,0,W,K,K,K,K,K,W,0,0,0,0,0,
0,0,0,0,0,W,K,K,K,K,K,W,0,0,0,0,0,0,
0,0,0,0,W,K,K,K,K,K,W,0,0,0,0,0,0,0,
0,0,0,0,W,K,K,K,K,W,0,0,0,0,0,0,0,0,
0,0,0,0,W,K,K,K,W,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,W,W,W,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
},
6, 11,
"MagicMarker1"
);
private static final Cursor curs2 = CursorUtils.makeCursor(
18, 18,
new int[] {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,W,W,W,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,W,K,K,K,W,0,0,0,0,0,
0,0,0,0,0,0,0,W,K,K,K,K,K,W,0,0,0,0,
0,0,0,0,0,0,W,K,K,K,K,K,K,K,W,0,0,0,
0,0,0,0,0,W,K,K,K,K,K,K,K,K,W,0,0,0,
0,0,0,0,W,K,K,K,K,K,K,K,K,K,W,0,0,0,
0,0,0,0,W,K,K,K,K,K,K,K,K,W,0,0,0,0,
0,0,0,0,W,K,K,K,K,K,K,K,W,0,0,0,0,0,
0,0,0,0,W,K,K,K,K,K,K,W,0,0,0,0,0,0,
0,0,0,0,0,W,K,K,K,K,W,0,0,0,0,0,0,0,
0,0,0,0,0,0,W,W,W,W,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
},
8, 8,
"MagicMarker2"
);
private static final Cursor curs3 = CursorUtils.makeCursor(
18, 18,
new int[] {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,W,W,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,W,K,K,W,0,0,0,
0,0,0,0,0,0,0,0,0,0,W,K,K,K,K,W,0,0,
0,0,0,0,0,0,0,0,0,W,K,K,K,K,K,K,W,0,
0,0,0,0,0,0,0,0,W,K,K,K,K,K,K,K,K,W,
0,0,0,0,0,0,0,W,K,K,K,K,K,K,K,K,K,W,
0,0,0,0,0,0,W,K,K,K,K,K,K,K,K,K,W,0,
0,0,0,0,0,W,K,K,K,K,K,K,K,K,K,W,0,0,
0,0,0,0,W,K,K,K,K,K,K,K,K,K,W,0,0,0,
0,0,0,W,K,K,K,K,K,K,K,K,K,W,0,0,0,0,
0,0,W,K,K,K,K,K,K,K,K,K,W,0,0,0,0,0,
0,W,K,K,K,K,K,K,K,K,K,W,0,0,0,0,0,0,
W,K,K,K,K,K,K,K,K,K,W,0,0,0,0,0,0,0,
W,K,K,K,K,K,K,K,K,W,0,0,0,0,0,0,0,0,
0,W,W,W,W,W,W,W,W,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
},
2, 15,
"MagicMarker3"
);
private static final Cursor curs4 = CursorUtils.makeCursor(
18, 18,
new int[] {
0,0,0,0,0,0,0,0,0,0,W,W,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,W,K,K,W,0,0,0,0,0,
0,0,0,0,0,0,0,0,W,K,K,K,K,W,0,0,0,0,
0,0,0,0,0,0,0,W,K,K,K,K,K,K,W,0,0,0,
0,0,0,0,0,0,W,K,K,K,K,K,K,K,K,W,0,0,
0,0,0,0,0,W,K,K,K,K,K,K,K,K,K,K,W,0,
0,0,0,0,W,K,K,K,K,K,K,K,K,K,K,K,K,W,
0,0,0,W,K,K,K,K,K,K,K,K,K,K,K,K,K,W,
0,0,W,K,K,K,K,K,K,K,K,K,K,K,K,K,K,W,
0,W,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,W,
W,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,W,0,
W,K,K,K,K,K,K,K,K,K,K,K,K,K,K,W,0,0,
W,K,K,K,K,K,K,K,K,K,K,K,K,K,W,0,0,0,
W,K,K,K,K,K,K,K,K,K,K,K,K,W,0,0,0,0,
W,K,K,K,K,K,K,K,K,K,K,K,W,0,0,0,0,0,
W,K,K,K,K,K,K,K,K,K,K,W,0,0,0,0,0,0,
0,W,W,W,W,W,W,W,W,W,W,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
},
2, 15,
"MagicMarker4"
);
public ToolCategory getCategory() {
return ToolCategory.MISC;
}
protected Image getBWIcon() {
return icon;
}
private int markerTip = 0;
private int paperType = 0;
public boolean toolSelected(ToolEvent e) {
markerTip = e.tc().getCustom(MagicMarkerTool.class, "markerTip", Integer.class, 0);
paperType = e.tc().getCustom(MagicMarkerTool.class, "paperType", Integer.class, 0);
return false;
}
public boolean toolSettingsChanged(ToolEvent e) {
markerTip = e.tc().getCustom(MagicMarkerTool.class, "markerTip", Integer.class, 0);
paperType = e.tc().getCustom(MagicMarkerTool.class, "paperType", Integer.class, 0);
return false;
}
private void setMarkerTip(ToolContext tc, int markerTip) {
tc.setCustom(MagicMarkerTool.class, "markerTip", this.markerTip = markerTip);
}
private void setPaperType(ToolContext tc, int paperType) {
tc.setCustom(MagicMarkerTool.class, "paperType", this.paperType = paperType);
}
private Random random = new Random();
private int curW, curH;
private int maxW, maxH;
private int minW, minH;
private boolean rr, aa;
public boolean mousePressed(ToolEvent e) {
e.beginTransaction(getName());
Graphics2D g = e.getPaintGraphics();
e.getPaintSettings().applyFill(g);
switch (markerTip) {
case 0: curW = maxW = 9; curH = maxH = 9; minW = 1; minH = 1; break;
case 1: curW = maxW = 10; curH = maxH = 11; minW = 2; minH = 3; break;
case 2: curW = maxW = 16; curH = maxH = 12; minW = 8; minH = 4; break;
case 3: curW = maxW = 13; curH = maxH = 13; minW = 7; minH = 7; break;
}
rr = (markerTip >= 2);
aa = (paperType < 3);
paint(g, g.getComposite(), e.getX(), e.getY(), curW, curH);
return true;
}
public boolean mouseHeld(ToolEvent e) {
Graphics2D g = e.getPaintGraphics();
e.getPaintSettings().applyFill(g);
curW++; curH++;
paint(g, g.getComposite(), e.getX(), e.getY(), curW, curH);
return true;
}
public boolean mouseDragged(ToolEvent e) {
Graphics2D g = e.getPaintGraphics();
e.getPaintSettings().applyFill(g);
float x = e.getX();
float y = e.getY();
float px = e.getPreviousX();
float py = e.getPreviousY();
int i = (int)Math.hypot(px - x, py - y) / 4;
int lastW = Math.min(curW, maxW);
int lastH = Math.min(curH, maxH);
curW = Math.max(maxW - i, minW);
curH = Math.max(maxH - i, minH);
drag(g, g.getComposite(), px, py, x, y, lastW, lastH, curW, curH);
return true;
}
public boolean mouseReleased(ToolEvent e) {
mouseDragged(e);
e.commitTransaction();
return true;
}
private void drag(
Graphics2D g,
Composite cx,
float sx, float sy,
float dx, float dy,
float sw, float sh,
float dw, float dh
) {
Composite altcx = (aa && cx instanceof AlphaComposite) ?
AlphaComposite.getInstance(
((AlphaComposite)cx).getRule(),
((AlphaComposite)cx).getAlpha() / 2
) : cx;
int m = (int)Math.ceil(Math.max(Math.abs(dx - sx), Math.abs(dy - sy)));
for (int i = 1; i <= m; i++) {
float x = sx + ((dx - sx) * i) / m;
float y = sy + ((dy - sy) * i) / m;
float w = sw + ((dw - sw) * i) / m;
float h = sh + ((dh - sh) * i) / m;
if (random.nextBoolean()) {
w = Math.max(w - 2, minW);
h = Math.max(h - 2, minH);
}
paint(g, (random.nextBoolean() ? altcx : cx), x, y, w, h);
}
}
private void paint(
Graphics2D g,
Composite cx,
float x, float y,
float w, float h
) {
x = Math.round(x - w / 2);
y = Math.round(y - h / 2);
w = Math.round(w);
h = Math.round(h);
Shape s = rr ?
new RoundRectangle2D.Float(x, y, w, h, 6, 6) :
new Ellipse2D.Float(x, y, w, h);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aa ?
RenderingHints.VALUE_ANTIALIAS_ON :
RenderingHints.VALUE_ANTIALIAS_OFF);
g.setComposite(cx);
g.fill(s);
}
public boolean keyPressed(ToolEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_4: setMarkerTip(e.tc(), 0); break;
case KeyEvent.VK_3: setMarkerTip(e.tc(), 1); break;
case KeyEvent.VK_2: setMarkerTip(e.tc(), 2); break;
case KeyEvent.VK_1: setMarkerTip(e.tc(), 3); break;
case KeyEvent.VK_5: setPaperType(e.tc(), 0); break;
case KeyEvent.VK_6: setPaperType(e.tc(), 1); break;
case KeyEvent.VK_7: setPaperType(e.tc(), 2); break;
case KeyEvent.VK_8: setPaperType(e.tc(), 3); break;
}
return false;
}
public Cursor getCursor(ToolEvent e) {
switch (markerTip) {
case 0: return curs1;
case 1: return curs2;
case 2: return curs3;
case 3: return curs4;
}
return null;
}
public int getMouseHeldInterval() {
switch (paperType) {
case 0: return (curH < 32) ? 5 : 10;
case 1: return (curH < 32) ? 8 : 16;
case 2: return 38;
case 3: return 0;
}
return 0;
}
public boolean shiftConstrainsCoordinates() {
return true;
}
public boolean doubleClickForOptions() {
return true;
}
public Form getCustomOptionsForm(final ToolContext tc) {
Form f = new Form();
f.add(new CustomOption<MarkerTipPanel>() {
public String getName() { return ToolUtilities.messages.getString("magicmarker.options.MarkerTip"); }
public MarkerTipPanel makeUI(boolean mini) { return new MarkerTipPanel(tc); }
public void update(MarkerTipPanel ui) { ui.updateSelection(); }
});
f.add(new IntegerEnumOption() {
public String getName() { return ToolUtilities.messages.getString("magicmarker.options.PaperType"); }
public int getValue() { return paperType; }
public void setValue(int v) { setPaperType(tc, v); }
public int[] values() { return new int[]{ 0, 1, 2, 3 }; }
public String getLabel(int v) {
switch (v) {
case 0: return ToolUtilities.messages.getString("magicmarker.options.PaperType.Soft");
case 1: return ToolUtilities.messages.getString("magicmarker.options.PaperType.Medium");
case 2: return ToolUtilities.messages.getString("magicmarker.options.PaperType.Hard");
case 3: return ToolUtilities.messages.getString("magicmarker.options.PaperType.Screen");
default: return null;
}
}
});
return f;
}
private class MarkerTipPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final Set<MarkerTipLabel> labels;
public MarkerTipPanel(ToolContext tc) {
super(new GridLayout(1,0));
labels = new HashSet<MarkerTipLabel>();
MarkerTipLabel l;
l = new MarkerTipLabel(tc, 0, icon1); add(l); labels.add(l);
l = new MarkerTipLabel(tc, 1, icon2); add(l); labels.add(l);
l = new MarkerTipLabel(tc, 2, icon3); add(l); labels.add(l);
l = new MarkerTipLabel(tc, 3, icon4); add(l); labels.add(l);
}
public void updateSelection() {
for (MarkerTipLabel l : labels) l.updateSelection();
}
}
private class MarkerTipLabel extends JLabel {
private static final long serialVersionUID = 1L;
private final ToolContext tc;
private final int i;
public MarkerTipLabel(ToolContext tc, int i, Image icon) {
super(new ImageIcon(icon));
this.tc = tc; this.i = i;
updateSelection();
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
setMarkerTip(MarkerTipLabel.this.tc, MarkerTipLabel.this.i);
}
});
}
public void updateSelection() {
if (markerTip == i) {
Border inner = BorderFactory.createEmptyBorder(2, 2, 2, 2);
Border outer = BorderFactory.createLineBorder(Color.black, 2);
setBorder(BorderFactory.createCompoundBorder(outer, inner));
} else {
setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
}
}
}
}