/*
* Copyright 2011 Uwe Krueger.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mandelsoft.swing;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.Dimension2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.ToolTipManager;
import javax.swing.border.EmptyBorder;
/**
*
* @author Uwe Krueger
*/
public class BufferedComponent extends JComponent
implements ProportionProvider {
static public final int TEXT_INSETS=2;
static public final int SELECT_INSETS=1;
static public final int CORNER_RECT=20;
// rect correction for double / int width and height
static public final double RECTW=1;
static public final double RECTH=1;
static public boolean debug=false;
static public int toInt(double d)
{
return (int)Math.round(d);
}
static public int toInt(long l)
{
return (int)l;
}
public interface PaintHandler {
void paintComponent(Graphics g);
}
public interface ToolTipHandler {
String getToolTipText(MouseEvent e);
}
private class ContentPane extends JComponent {
private int max=5;
private int limitCnt=max;
public ContentPane()
{ setInheritsPopupMenu(true);
}
@Override
public String getToolTipText(MouseEvent event)
{
if (tooltiphandler!=null) {
return tooltiphandler.getToolTipText(translate(event));
}
return null;
}
@Override
public void paintComponent(Graphics g)
{
Insets o=getInsets();
//System.out.println("paint with "+o+": "+this.getBounds());
if (image!=null) g.drawImage(image, o.left, o.top,
toInt(image.getWidth()*scaleX),
toInt(image.getHeight()*scaleY),null);
for (PaintHandler h:painthandlers) {
h.paintComponent(g.create());
}
if (limitPending) {
//System.out.println("limit cnt = "+(limitCnt-1));
if (--limitCnt<=0) {
limitPending=false;
limitCnt=max;
}
}
limitWindowSize();
}
@Override
public Graphics getGraphics()
{ return createGraphics();
}
public Graphics2D createGraphics()
{ return image.createGraphics();
}
}
static public final int SCALEX=1;
static public final int SCALEY=2;
private BufferedImage image;
private int scalemode;
private Rectangle fullviewrect;
private Graphics2D drawer;
private boolean limitWindowSize=false;
private ContentPane content;
private List<PaintHandler> painthandlers=new ArrayList<PaintHandler>();
private ToolTipHandler tooltiphandler;
private Set<VisibleRect> rects=new HashSet<VisibleRect>();
private MouseHandler mousehandler;
private Point adjust;
private BooleanAttribute selectinvisible;
private BooleanAttribute showdecoration;
private BooleanAttribute pixeltooltip;
private boolean limitPending;
public BufferedComponent()
{ this(1,1);
}
public BufferedComponent(int width, int height)
{ this(new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB));
}
public BufferedComponent(BufferedImage image)
{
setupLocalModels();
setLayout(new BorderLayout());
content=new ContentPane();
content.setDoubleBuffered(false);
content.setAutoscrolls(true);
add(content);
setupImage(image);
mousehandler=new MouseHandler();
setInheritsPopupMenu(true);
setDoubleBuffered(false);
setAutoscrolls(true);
content.addMouseListener(mousehandler);
content.addMouseMotionListener(mousehandler);
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e)
{
//System.out.println("Buffer RS: "+e);
JViewport vp=getViewPort();
if (vp!=null) {
//System.out.println("VP: "+vp.getViewPosition());
//System.out.println("VP: "+vp.getExtentSize());
if (adjust!=null) vp.setViewPosition(adjust);
adjust=null;
}
((BufferedComponent)e.getSource()).limitWindowSize();
}
});
addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt)
{
//System.out.println("->"+evt.getPropertyName()+"="+evt.getNewValue());
if (evt.getPropertyName().equals("border")) {
updatePreferredSize();
}
}
});
}
private void setupLocalModels()
{
selectinvisible=new BooleanAttribute(this,"selectinvisible");
showdecoration=new BooleanAttribute(this,"showdecoration") {
@Override
protected void stateChanged()
{
Set<VisibleRect> set=new HashSet<VisibleRect>();
for (VisibleRect r:rects) {
if (r.isVisible()) set.add(r);
r.setVisible(false);
}
setState();
for (VisibleRect r:set) {
r.setVisible(true);
}
}
};
showdecoration.setState(true);
pixeltooltip=new BooleanAttribute(this,"pixeltooltip") {
@Override
protected void stateChanged()
{
if (tooltiphandler!=null) {
if (isSelected()) {
ToolTipManager.sharedInstance().registerComponent(content);
}
else {
ToolTipManager.sharedInstance().unregisterComponent(content);
}
}
}
};
}
public BooleanAttribute getDecorationModel()
{
return showdecoration;
}
public BooleanAttribute getSelectInvisibleModel()
{
return selectinvisible;
}
public BooleanAttribute getPixelToolTipModel()
{
return pixeltooltip;
}
public void setToolTipHandler(ToolTipHandler h)
{
ToolTipHandler old=tooltiphandler;
tooltiphandler=h;
if ((h==null)!=(old==null)) {
if (h==null) {
ToolTipManager.sharedInstance().unregisterComponent(content);
}
else {
if (pixeltooltip.isSet())
ToolTipManager.sharedInstance().registerComponent(content);
}
}
}
public void addPaintHandler(PaintHandler h)
{
painthandlers.add(h);
}
public void removePaintHandler(PaintHandler h)
{
painthandlers.remove(h);
}
public void updatePreferredSize()
{
//new Throwable().printStackTrace(System.out);
Insets o=getInsets();
Dimension d=new Dimension(toInt(image.getWidth()*scaleX)+o.left+o.right,
toInt(image.getHeight()*scaleY)+o.top+o.bottom);
if (debug) System.out.println("update size: image "+image.getWidth()*scaleX+","+
image.getHeight()*scaleY+" "+o);
setPreferredSize(d);
setMaximumSize(d);
}
private boolean isAdjusting()
{ return adjust!=null;
}
private void setupImage(BufferedImage image)
{
this.image=image;
//System.out.println("setup image "+image.getWidth()+"x"+image.getHeight());
this.fullviewrect=new Rectangle(0,0,toInt(image.getWidth()/scaleX),
toInt(image.getHeight()/scaleY));
this.drawer=null;
updatePreferredSize();
}
public void setImage(BufferedImage image)
{ BufferedImage old=this.image;
discardAllRects();
setupImage(image);
if (old==null || image.getWidth()!=old.getWidth() ||
image.getHeight()!=old.getHeight()) {
revalidate();
limitWindowSize();
}
repaint();
}
public void setScaleMode(int m)
{
if (scalemode!=m) {
int old=scalemode;
if ((m==0)!=(scalemode==0)) {
if (m!=0) content.addMouseWheelListener(mousehandler);
else content.removeMouseWheelListener(mousehandler);
}
scalemode=m;
firePropertyChange("scalemode", old, scalemode);
}
}
public void setScaleMode(boolean b)
{
setScaleMode(b?(SCALEX|SCALEY):0);
}
public JComponent getContentPane()
{ return content;
}
public void setLimitWindowSize(boolean b)
{
if (limitWindowSize!=b) {
limitWindowSize=b;
if (b) limitWindowSize();
firePropertyChange("limitwindowsize", !limitWindowSize, limitWindowSize);
}
}
public boolean isLimitWindowSize()
{ return limitWindowSize;
}
public boolean isShowDecoration()
{
return showdecoration.isSet();
}
public void setShowDecoration(boolean showdecoration)
{
this.showdecoration.setState(showdecoration);
}
public Graphics2D createGraphics()
{ return image.createGraphics();
}
private static class FrameInfo {
Insets insets=new Insets(0,0,0,0);
Container root;
public void setup(Container c)
{
insets.bottom=0;
insets.left=0;
insets.right=0;
insets.top=0;
while (c!=null) {
Insets cur=c.getInsets();
// System.out.println("insets "+c.getClass()+": "+cur);
if (c.getClass().equals(JScrollPane.class)) {
JScrollPane p=(JScrollPane)c;
JScrollBar v=p.getVerticalScrollBar();
//System.out.println(" v=vis="+v.isVisible()+", en="+v.isEnabled()+
// ",w ="+v.getWidth()+", h="+v.getHeight());
if (v.isVisible()) insets.right+=v.getWidth();
JScrollBar h=p.getHorizontalScrollBar();
if (h.isVisible()) insets.bottom+=v.getWidth();
}
insets.bottom+=cur.bottom;
insets.left+=cur.left;
insets.right+=cur.right;
insets.top+=cur.top;
root=c;
if (c instanceof Window) {
c=null;
}
else {
c=c.getParent();
}
}
//System.out.println("ROOT Window is "+root);
}
}
private boolean limitWindowSize()
{ int w=0;
int h=0;
if (!limitWindowSize) return false;
boolean limit=false;
FrameInfo info=new FrameInfo();
info.setup(this.getParent());
int dw=info.insets.left+info.insets.right;
int dh=info.insets.top+info.insets.bottom;
Dimension max=getMaximumSize();
if (info.root==null) {
return false;
}
//System.out.println("dw="+dw+", dh="+dh);
if (info.root.getWidth()-dw>max.getWidth()) {
w=toInt(max.getWidth()+dw);
h=info.root.getHeight();
limit=true;
}
if (info.root.getHeight()-dh>max.getHeight()) {
h=toInt(max.getHeight()+dh);
if (w==0) w=info.root.getWidth();
limit=true;
}
//System.out.println("window "+info.root.getWidth()+","+info.root.getHeight());
//System.out.println("max "+max.getWidth()+","+max.getHeight());
if (limit) {
limitPending=true;
//System.out.println("limit to "+w+","+h);
info.root.setSize(w, h);
}
return limit;
}
public double getProportion()
{ double p=((double)image.getWidth())/image.getHeight();
//System.out.println("deliver proportion "+p);
return p;
}
public BufferedImage getImage()
{ return image;
}
//////////////////////////////////////////////////////////////////////
// Assigned Rects
//////////////////////////////////////////////////////////////////////
private Graphics2D getDrawer()
{ if (drawer==null) {
drawer=content.createGraphics();
drawer.setColor(Color.BLACK);
drawer.setXORMode(Color.WHITE);
drawer.setFont(drawer.getFont().deriveFont(Font.BOLD));
}
return drawer;
}
public VisibleRect createRect(String name)
{ return new VisibleRect(name);
}
public VisibleRect createRect(String name, String label)
{ return new VisibleRect(name,label);
}
public VisibleRect createRect(String name, int x, int y, int w, int h)
{
return new VisibleRect(name,x,y,w,h);
}
public VisibleRect createRect(String name, String label, int x, int y, int w, int h)
{
return new VisibleRect(name,label,x,y,w,h);
}
public VisibleRect createRect(String name, Rectangle r)
{ return new VisibleRect(name,r);
}
public VisibleRect createRect(String name, String label, Rectangle r)
{ return new VisibleRect(name,label,r);
}
public VisibleRect createRect(String name, Object o)
{ return createRect(name,name).setOwner(o);
}
public VisibleRect createRect(String name, String label, Object o)
{ return createRect(name,label).setOwner(o);
}
public VisibleRect createRect(String name, Object o, int x, int y, int w, int h)
{ return createRect(name,x,y,w,h).setOwner(o);
}
public VisibleRect createRect(String name, String label, Object o, int x, int y, int w, int h)
{ return createRect(name,label,x,y,w,h).setOwner(o);
}
public VisibleRect createRect(String name, Object o, Rectangle r)
{ return createRect(name,r).setOwner(o);
}
public VisibleRect createRect(String name, String label, Object o, Rectangle r)
{ return createRect(name,label,r).setOwner(o);
}
public void setSelectInvisible(boolean b)
{ this.selectinvisible.setState(b);
}
public void showAllRects()
{
//System.out.println("show rects:");
for (VisibleRect r:rects) r.setVisible(true);
}
public void hideAllRects()
{
//System.out.println("hide rects:");
//new Throwable().printStackTrace(System.out);
for (VisibleRect r:rects) r.setVisible(false);
}
public void discardAllRects()
{
//System.out.println("discard rects:");
//new Throwable().printStackTrace(System.out);
Set<VisibleRect> set=new HashSet<VisibleRect>(rects);
for (VisibleRect r:set) r.discard();
}
public void discardAllRects(VisibleRectFilter f)
{
//System.out.println("discard filtered rects:");
Set<VisibleRect> set=new HashSet<VisibleRect>(rects);
for (VisibleRect r:set) if (f.match(r)) r.discard();
}
public VisibleRect getRect(String name)
{
for (VisibleRect r:rects) {
if (r.getName()!=null && r.getName().equals(name)) return r;
}
return null;
}
public Iterator<VisibleRect> getRects()
{
return rects.iterator();
}
public VisibleRect findRect(int x, int y)
{
return findRect((double)x,(double)y);
}
public VisibleRect findRect(double x, double y, boolean fixed)
{ return findRect(x,y,fixed,selectinvisible.isSet());
}
public VisibleRect findRect(double x, double y, boolean fixed,
boolean invisible)
{ VisibleRect found=null;
for (VisibleRect r:rects) {
if (r.match(x,y)) {
if ((invisible || r.isVisible()) && (fixed || !r.isFixed())) {
if (found!=null){
if (found.getWidth()*found.getHeight()<=
r.getWidth()*r.getHeight()) continue;
}
found=r;
}
}
}
return found;
}
public VisibleRect findRect(double x, double y)
{ return findRect(x,y,true);
}
public VisibleRect findRect(Point p, boolean fixed, boolean invisible)
{ return findRect(p.getX(), p.getY(), fixed, invisible);
}
public VisibleRect findRect(Point p, boolean fixed)
{ return findRect(p.getX(), p.getY(),fixed);
}
public VisibleRect findRect(Point p)
{ return findRect(p.getX(), p.getY());
}
public VisibleRectBorder findRectBorder(double x, double y, boolean fixed)
{ VisibleRectBorder found=null;
VisibleRectBorder border=new VisibleRectBorder();
for (VisibleRect r:rects) {
if (r.isPointOnRect(toInt(x),toInt(y),SELECT_INSETS,border)) {
if (!border.valid())
throw new IllegalStateException("invalid border");
if (r.isVisible() && (fixed || !r.isFixed())) {
if (found!=null){
if (found.getRect().getWidth()*found.getRect().getHeight()<=
r.getWidth()*r.getHeight()) continue;
}
found=border;
border=new VisibleRectBorder();
}
}
}
return found;
}
public VisibleRectBorder findRectBorder(double x, double y)
{ return findRectBorder(x, y, false);
}
public VisibleRectBorder findRectBorder(Point p, boolean fixed)
{ return findRectBorder(p.getX(), p.getY(),fixed);
}
public VisibleRectBorder findRectBorder(Point p)
{ return findRectBorder(p.getX(), p.getY(),true);
}
//////////////
// Rect filtering
public interface VisibleRectFilter {
public boolean match(VisibleRect r);
}
//////////////
// RectBorder
public static class VisibleRectBorder {
static public final Side LEFT=Side.LEFT;
static public final Side RIGHT=Side.RIGHT;
static public final Side TOP=Side.TOP;
static public final Side BOTTOM=Side.BOTTOM;
static public final Corner TOP_LEFT=RectPointEvent.TOP_LEFT;
static public final Corner TOP_RIGHT=RectPointEvent.TOP_RIGHT;
static public final Corner BOTTOM_LEFT=RectPointEvent.BOTTOM_LEFT;
static public final Corner BOTTOM_RIGHT=RectPointEvent.BOTTOM_RIGHT;
private VisibleRect rect;
private Side side;
private Corner corner;
public VisibleRectBorder()
{
}
public VisibleRectBorder(VisibleRect rect, Side side, Corner corner)
{
this.rect=rect;
this.side=side;
this.corner=corner;
}
public boolean valid()
{ return rect!=null && side!=null && corner!=null;
}
public Corner getCorner()
{ return corner;
}
public VisibleRect getRect()
{ return rect;
}
public Side getSide()
{ return side;
}
public void setCorner(Corner corner)
{ this.corner=corner;
}
public void setRect(VisibleRect rect)
{ this.rect=rect;
}
public void setSide(Side side)
{ this.side=side;
}
public Corner getOppositeCorner()
{ return corner.getOppositeCorner();
}
public Side getOppositeSide()
{ return side.getOppositeSide();
}
public Point getCornerPoint()
{ return getCorner().getPoint(getRect()._getRect());
}
public Point getOppositeCornerPoint()
{ return getOppositeCorner().getPoint(getRect()._getRect());
}
}
//////////////
// VisibleRect
private volatile int id_cnt=0;
public class VisibleRect implements PaintHandler {
private int id;
private String label;
private String name;
private Rectangle _rect;
private boolean visible;
private boolean active;
private boolean fixed;
private Object owner;
private ProportionProvider proportionProvider;
private Stroke line;
private VisibleRect()
{ this.id=id_cnt++;
activate();
}
protected VisibleRect(String name)
{
this(name,name);
}
protected VisibleRect(String name, String label)
{
this(name,label,new Rectangle(0,0,0,0));
}
protected VisibleRect(String name, int x, int y, int w, int h)
{
this(name,new Rectangle(x,y,w,h));
}
protected VisibleRect(String name, String label, int x, int y, int w, int h)
{
this(name,label,new Rectangle(x,y,w,h));
}
protected VisibleRect(String name, Rectangle r)
{
this(name,name,r);
}
protected VisibleRect(String name, String label, Rectangle r)
{ this();
this.name=name;
if (label==null) label=name;
this.label=label;
this._rect=new Rectangle(r);
}
public void setStroke(Stroke line)
{
draw();
this.line=line;
draw();
}
public ProportionProvider getProportionProvider()
{
return proportionProvider;
}
public void setProportionProvider(ProportionProvider proportionProvider)
{
this.proportionProvider=proportionProvider;
}
public void discard()
{
setVisible(false);
active=false;
rects.remove(this);
}
public void activate()
{
activate(false);
}
public void activate(boolean subst)
{
if (!active) {
if (subst && getName()!=null) {
VisibleRect old=getRect(getName());
if (old!=null && old!=this) {
old.discard();
}
}
active=true;
rects.add(this);
}
}
public boolean isVisible()
{ return visible;
}
public boolean isFixed()
{ return fixed;
}
public boolean isActive()
{ return active;
}
synchronized
public int getId()
{ return id;
}
synchronized
public String getName()
{ return name;
}
synchronized
public String getLabel()
{ return label;
}
synchronized
public Object getOwner()
{ return owner;
}
synchronized
public VisibleRect setOwner(Object o)
{ this.owner=o;
return this;
}
synchronized
public Rectangle _getRect()
{ return new Rectangle(_rect);
}
synchronized
public double getY()
{
return _rect.getY();
}
synchronized
public double getX()
{
return _rect.getX();
}
synchronized
public Point getCenter()
{
return new Point(getCenterX(),getCenterY());
}
public int getCenterY()
{
return toInt(_rect.getCenterY()-RECTW/2);
}
public int getCenterX()
{
return toInt(_rect.getCenterX()-RECTH/2);
}
public int getWidth()
{
return toInt(_rect.getWidth());
}
public Dimension getSize()
{
return _rect.getSize();
}
public Point getLocation()
{
return _rect.getLocation();
}
public int getHeight()
{
return toInt(_rect.getHeight());
}
public boolean contains(Rectangle2D r)
{
return _rect.contains(r);
}
public boolean contains(double x, double y, double w, double h)
{
return _rect.contains(x, y, w, h);
}
public boolean contains(Point2D p)
{
return _rect.contains(p);
}
public boolean contains(double x, double y)
{
return _rect.contains(x, y);
}
public boolean match(double x, double y)
{
if (contains(x,y)) return true;
if (toInt(_rect.getWidth())<5 || toInt(_rect.getHeight())<5) {
if (Math.abs(x-getCenterX())<2 || Math.abs(y-getCenterY())<2)
return true;
}
return false;
}
public VisibleRect setFixed(boolean fixed)
{
this.fixed=fixed;
return this;
}
public VisibleRect setName(String name)
{
this.name=name;
if (label==null) setLabel(name);
return this;
}
public VisibleRect setLabel(String label)
{
draw();
this.label=label;
draw();
return this;
}
public void setRect(Rectangle2D r)
{
draw();
_rect.setRect(r);
draw();
}
public void setSize(Dimension2D d)
{ setSize(toInt(d.getWidth()),toInt(d.getHeight()));
}
public void setSize(int w, int h)
{
draw();
_rect.setSize(w,h);
draw();
}
public void setLocation(int x, int y)
{
draw();
_rect.setLocation(x, y);
draw();
}
public void setLocation(Point2D p)
{
setLocation(toInt(p.getX()),toInt(p.getY()));
}
public void grow(int h, int v)
{
draw();
_rect.grow(h, v);
draw();
}
public void translate(int x, int y)
{
draw();
_rect.translate(x, y);
draw();
}
public void setFrameFromCenter(Point2D center, Point2D corner)
{
draw();
_rect.setFrameFromCenter(center, corner);
_rect.setSize(toInt(_rect.getWidth()+RECTW),toInt(_rect.getHeight()+RECTH));
draw();
}
public void setFrameFromDiagonal(Point2D p1, Point2D p2)
{
draw();
_rect.setFrameFromDiagonal(p1, p2);
_rect.setSize(toInt(_rect.getWidth()+RECTW),toInt(_rect.getHeight()+RECTH));
draw();
}
public VisibleRect setVisible(boolean v)
{ if (v!=visible) {
//System.out.println(" vis "+v+" for "+getName());
draw();
visible=v;
draw();
}
else {
//System.out.println(" vis unchanged "+v+" for "+getName());
}
return this;
}
public void paintComponent(Graphics g)
{
throw new UnsupportedOperationException("Not supported yet.");
}
protected void draw()
{
if (!visible) {
//System.out.println(" not visible");
return;
}
//System.out.println(" draw rect "+_rect);
Graphics2D g=getDrawer();
draw(g);
repaint();
}
protected void draw(Graphics2D g)
{
Stroke orig=g.getStroke();
int x=toInt(_rect.getX());
int y=toInt(_rect.getY());
int w=toInt(_rect.getWidth());
int h=toInt(_rect.getHeight());
if (line!=null) g.setStroke(line);
if (w<5 || h<5) {
g.drawLine(toInt(getCenterX()), toInt(getCenterY()-10),
toInt(getCenterX()), toInt(getCenterY()+10));
g.drawLine(toInt(getCenterX()-10), toInt(getCenterY()),
toInt(getCenterX()+10), toInt(getCenterY()));
}
else {
g.drawLine(x, y, x, y+h-1);
g.drawLine(x, y, x+w-1, y);
g.drawLine(x+w-1, y+h-1, x, y+h-1);
g.drawLine(x+w-1, y+h-1, x+w-1, y);
// g.draw(rect); // draw addtional width and height
}
g.setStroke(orig);
if (label!=null && isShowDecoration()) {
// get metrics from the graphics
FontMetrics metrics=g.getFontMetrics();
// get the height of a line of text in this font and render context
int hgt=metrics.getHeight();
// get the advance of my text in this font and render context
int adv=metrics.stringWidth(label);
// calculate the size of a box to hold the text with some padding.
Dimension d=new Dimension(adv, hgt);
// try to find a useful position
// first below bottom right rectPoint
if (!_draw(g, label, d, 1, 1, 1, 1)) {
// second below bottom left rectPoint
if (!_draw(g, label, d, 0, 1, 0, 1)) {
// third above top right rectPoint
if (!_draw(g, label, d, 1, 0, 1, -1)) {
// fourth above top left rectPoint
if (!_draw(g, label, d, 0, 0, 0, -1)) {
// dont't draw at all
}
}
}
}
}
}
private boolean _draw(Graphics2D g, String name, Dimension d,
int fw, int fh, int fdw, int fdh)
{
double py=_rect.getY()+(_rect.getHeight()-1)*fh+(d.getHeight()+TEXT_INSETS)*fdh;
if (0<=py && py <image.getHeight()) {
double px=_rect.getX()+(_rect.getWidth()-1)*fw+TEXT_INSETS*fdw;
double px2=px-(d.getWidth()+TEXT_INSETS)*fdw;
if (0<=px && px<image.getWidth() && px2>0) {
g.drawString(name, toInt(px2),
toInt(py-d.getHeight()*Integer.signum(fdh-1)
-TEXT_INSETS*(fdh+1)));
return true;
}
}
return false;
}
private boolean checkLine(double sx, double ex,
double cy,
double px, double py,
int t, VisibleRectBorder b, Side side)
{
if (sx>ex) {
double tmp=sx;
sx=ex;
ex=tmp;
}
boolean r=(cy-t<=py && py<=cy+t &&
sx-t<=px && px<=ex+t);
if (r && b!=null) {
Corner c;
b.setSide(side);
if (px<(sx+ex)/2) {
c=side.getLowerCorner();
if (c==null) throw new IllegalStateException("LC");
}
else {
c=side.getHigherCorner();
if (c==null) throw new IllegalStateException("HC");
}
b.setCorner(c);
b.setRect(this);
}
return r;
}
public boolean isPointOnRect(int x, int y, int t)
{ return isPointOnRect(x,y,t,null);
}
public boolean isPointOnRect(int x, int y, int t, VisibleRectBorder b)
{
//upper
if (checkLine(getX(), getX()+getWidth()-1,
getY(),
x,y,t,
b, VisibleRectBorder.TOP)) return true;
//lower
if (checkLine(getX(), getX()+getWidth()-1,
getY()+getHeight()-1,
x,y,t,
b, VisibleRectBorder.BOTTOM)) return true;
//left
if (checkLine(getY(), getY()+getHeight()-1,
getX(),
y,x,t,
b, VisibleRectBorder.LEFT)) return true;
//right
if (checkLine(getY(), getY()+getHeight()-1,
getX()+getWidth()-1,
y,x,t,
b, VisibleRectBorder.RIGHT)) return true;
return false;
}
@Override
public String toString()
{ String n=getName();
if (n==null) n="Id "+getId();
return "rectangle "+n+" at ("+getX()+","+getY()+") with size ("+
getWidth()+","+getHeight()+")";
}
//////////////////////////////////////////////////////////////////////////
private List<RectEventListener> listeners=new ArrayList<RectEventListener>();
public void addRectEventListener(RectEventListener l)
{
listeners.add(l);
}
public void removeRectEventListener(RectEventListener l)
{
listeners.remove(l);
}
public boolean hasRectEventListeners()
{
return !listeners.isEmpty();
}
protected void fireRectEvent(MouseEvent e)
{
fireRectEvent(new RectEvent(this, e));
}
protected void fireRectEvent(RectEvent e)
{
for (RectEventListener l:listeners) {
l.buttonClicked(e);
}
}
//////////////////////////////////////////////////////////////////////////
private Set<RectModifiedEventListener> mlisteners=new HashSet<RectModifiedEventListener>();
public void addRectModifiedEventListener(RectModifiedEventListener l)
{
mlisteners.add(l);
}
public void removeRectModifiedEventListener(RectModifiedEventListener l)
{
mlisteners.remove(l);
}
public boolean hasRectModifiedEventListeners()
{
return !mlisteners.isEmpty();
}
protected void fireRectModifiedEvent(int action)
{
fireRectModifiedEvent(new RectModifiedEvent(this, action));
}
protected void fireRectModifiedEvent(RectModifiedEvent e)
{
for (RectModifiedEventListener l:mlisteners) {
l.rectModified(e);
}
}
}
///////////////////////////////////////////////////////////////////////////
// Event handling
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
// own events
public static class ClickEvent extends EventObject {
private MouseEvent evt;
public ClickEvent(Object source, MouseEvent e)
{ super(source);
this.evt=e;
}
public MouseEvent getMouseEvent()
{ return evt;
}
public BufferedComponent getComponent()
{ return (BufferedComponent)
((JComponent)getMouseEvent().getSource()).getParent();
}
public boolean isShiftDown()
{
return evt.isShiftDown();
}
public boolean isMetaDown()
{
return evt.isMetaDown();
}
public boolean isControlDown()
{
return evt.isControlDown();
}
public boolean isAltGraphDown()
{
return evt.isAltGraphDown();
}
public boolean isAltDown()
{
return evt.isAltDown();
}
public int getY()
{
return evt.getY();
}
public int getX()
{
return evt.getX();
}
public Point getPoint()
{
return evt.getPoint();
}
public int getClickCount()
{
return evt.getClickCount();
}
public int getButton()
{
return evt.getButton();
}
public String getDescription()
{ return "button "+getButton()+" "+getClickCount()+"x at ("+
getX()+","+getY()+")";
}
}
//////////////////////////////////////////////////////////////////////////
public static class RectEvent extends ClickEvent {
private VisibleRect rect;
public RectEvent(VisibleRect r,MouseEvent e)
{ super(r,e);
this.rect=r;
}
public VisibleRect getRect()
{ return rect;
}
public Object getOwner()
{ return rect.getOwner();
}
@Override
public String getDescription()
{ String n=rect.getName();
if (n==null) n="id "+rect.getId();
return "rectangle "+n+" with "+super.getDescription();
}
}
public interface RectEventListener {
void buttonClicked(RectEvent e);
}
//////////////////////////////////////////////////////////////////////////
private List<RectEventListener> listeners=new ArrayList<RectEventListener>();
public void addRectEventListener(RectEventListener l)
{ listeners.add(l);
}
public void removeRectEventListener(RectEventListener l)
{ listeners.remove(l);
}
protected void fireRectEvent(VisibleRect r, MouseEvent e)
{
fireRectEvent(new RectEvent(r,e));
}
protected void fireRectEvent(RectEvent e)
{
e.getRect().fireRectEvent(e);
for (RectEventListener l:listeners) {
l.buttonClicked(e);
}
}
//////////////////////////////////////////////////////////////////////////
public static class RectPointEvent extends ClickEvent {
static final public Corner TOP_LEFT= Corner.TOP_LEFT;
static final public Corner TOP_RIGHT= Corner.TOP_RIGHT;
static final public Corner BOTTOM_LEFT= Corner.BOTTOM_LEFT;
static final public Corner BOTTOM_RIGHT=Corner.BOTTOM_RIGHT;
static final public RectanglePoint MIDDLE_LEFT= RectanglePoint.MIDDLE_LEFT;
static final public RectanglePoint MIDDLE_RIGHT= RectanglePoint.MIDDLE_RIGHT;
static final public RectanglePoint MIDDLE_TOP= RectanglePoint.MIDDLE_TOP;
static final public RectanglePoint MIDDLE_BOTTOM=RectanglePoint.MIDDLE_BOTTOM;
private RectanglePoint rectPoint;
public RectPointEvent(MouseEvent evt, RectanglePoint loc)
{ super(evt.getSource(),evt);
this.rectPoint=loc;
}
public Corner getCorner()
{ return isCornerEvent()?(Corner)rectPoint:null;
}
public boolean isCornerEvent()
{
return (rectPoint instanceof Corner);
}
public RectanglePoint getRectanglePoint()
{
return rectPoint;
}
public String getPointName()
{ if (rectPoint==null) return "None";
return rectPoint.getName();
}
@Override
public String getDescription()
{ return getPointName()+" with "+super.getDescription();
}
}
public interface RectPointEventListener {
void rectanglePointClicked(RectPointEvent e);
}
//////////////////////////////////////////////////////////////////////////
private List<RectPointEventListener> clisteners=new ArrayList<RectPointEventListener>();
public void addRectPointEventListener(RectPointEventListener l)
{ clisteners.add(l);
}
public void removeRectPointEventListener(RectPointEventListener l)
{ clisteners.remove(l);
}
protected void fireRectangleEvent(MouseEvent evt, RectanglePoint loc)
{
fireRectangleEvent(new RectPointEvent(evt,loc));
}
protected void fireRectangleEvent(RectPointEvent e)
{ for (RectPointEventListener l:clisteners) {
l.rectanglePointClicked(e);
}
fireRectangleEvent(e.getRectanglePoint(),e);
}
//////////////////////////////////////////////////////////////////////////
private Map<RectanglePoint,List<ActionListener>> points=
new HashMap<RectanglePoint,List<ActionListener>>();
public void addActionListener(ActionListener l, RectanglePoint c)
{
List<ActionListener> list=points.get(c);
if (list==null) {
list=new ArrayList<ActionListener>();
points.put(c,list);
}
list.add(l);
}
public void removeActionListener(ActionListener l, RectanglePoint c)
{
List<ActionListener> list=points.get(c);
if (list!=null) {
list.remove(l);
}
}
public static class RectangleActionEvent extends ActionEvent {
private RectPointEvent p;
public RectangleActionEvent(Object source, int id, String command, long when,
int modifiers, RectPointEvent p)
{ super(source,id,command,when,modifiers);
this.p=p;
}
public RectangleActionEvent(Object source, int id, String command,
int modifiers, RectPointEvent p)
{ super(source,id,command,modifiers);
this.p=p;
}
public RectangleActionEvent(Object source, int id, String command,
RectPointEvent p)
{ super(source,id,command);
this.p=p;
}
public RectPointEvent getRectanglePointEvent()
{
return p;
}
}
protected void fireRectangleEvent(RectanglePoint c, RectPointEvent p)
{ ActionEvent e=new RectangleActionEvent(this,ActionEvent.ACTION_PERFORMED,c.getName(),
System.currentTimeMillis(),0,p);
List<ActionListener> list=points.get(c);
if (list!=null) {
List<ActionListener> selected=new ArrayList<ActionListener>();
for (ActionListener l:list) {
if (l instanceof Action) {
if (!((Action)l).isEnabled()) {
//System.out.println("Action "+l+" disabled");
continue;
}
}
selected.add(l);
}
for (ActionListener l:selected) {
l.actionPerformed(e);
}
}
}
//////////////////////////////////////////////////////////////////////////
public static class RectModifiedEvent extends EventObject {
static public final int RECT_CREATED = 1;
static public final int RECT_MOVED = 2;
static public final int RECT_RESIZED = 3;
private VisibleRect rect;
private int action;
public RectModifiedEvent(VisibleRect source, int action)
{ super(source);
this.rect=source;
this.action=action;
}
public int getAction()
{ return action;
}
public VisibleRect getRect()
{ return rect;
}
}
public interface RectModifiedEventListener {
void rectModified(RectModifiedEvent e);
}
//////////////////////////////////////////////////////////////////////////
private Set<RectModifiedEventListener> mlisteners=new HashSet<RectModifiedEventListener>();
public void addRectModifiedEventListener(RectModifiedEventListener l)
{ mlisteners.add(l);
}
public void removeRectModifiedEventListener(RectModifiedEventListener l)
{ mlisteners.remove(l);
}
protected void fireRectModifiedEvent(VisibleRect rect, int action)
{
fireRectModifiedEvent(new RectModifiedEvent(rect,action));
}
protected void fireRectModifiedEvent(RectModifiedEvent e)
{
e.getRect().fireRectModifiedEvent(e);
for (RectModifiedEventListener l:mlisteners) {
l.rectModified(e);
}
}
///////////////////////////////////////////////////////////////////////////
// scaling
private double scaleX=1.0;
private double scaleY=1.0;
private RectangleSelector selector=null;
private Insets insets=new Insets(0,0,0,0); // not used anymore, always 0
public boolean setScale(double s)
{
JViewport vp=getViewPort();
if (vp!=null) {
Rectangle r=vp.getViewRect();
return setScale(s,new Point2D.Double(r.getCenterX(), r.getCenterY()));
}
else {
return setScale(s,null);
}
}
public boolean setScaleX(double s)
{
JViewport vp=getViewPort();
if (vp!=null) {
Rectangle r=vp.getViewRect();
return setScaleX(s,new Point2D.Double(r.getCenterX(), r.getCenterY()));
}
else {
return setScaleX(s,null);
}
}
public boolean setScaleY(double s)
{
JViewport vp=getViewPort();
if (vp!=null) {
Rectangle r=vp.getViewRect();
return setScaleY(s,new Point2D.Double(r.getCenterX(), r.getCenterY()));
}
else {
return setScaleY(s,null);
}
}
public boolean setScaleX(double s, Point2D p)
{ return setScale(s,scaleY,p);
}
public boolean setScaleY(double s, Point2D p)
{ return setScale(scaleX,s,p);
}
public boolean setScale(double s, Point2D p)
{ return setScale(s,s,p);
}
public boolean setScale(double sx, double sy, Point2D p)
{ Point n=null;
if (sx<=0) throw new IllegalArgumentException("scale "+sx+" must be larger than 0);");
if (sy<=0) throw new IllegalArgumentException("scale "+sy+" must be larger than 0);");
//System.out.println("call set scale to "+sx+"/"+sy+"/"+p);
if (sx!=scaleX || sy!=scaleY) {
double oldX=scaleX;
double oldY=scaleY;
ScaleEvent e=new ScaleEvent(this,sx,sy,oldX,oldY);
for (ScaleEventListener h:slisteners) {
if (!h.succeedScale(e)) return false;
}
scaleX=sx;
scaleY=sy;
JViewport vp=getViewPort();
if (vp!=null && p!=null) {
Rectangle r=vp.getViewRect();
double fX=scaleX/oldX;
double fY=scaleX/oldY;
double x=p.getX()*fX-(p.getX()-r.getX());
double y=p.getY()*fY-(p.getY()-r.getY());
if (x+r.getWidth()>=image.getWidth()*scaleX) {
x=image.getWidth()*scaleX-r.getWidth();
//System.out.println("adjust x");
}
if (y+r.getHeight()>=image.getHeight()*scaleY) {
y=image.getHeight()*scaleY-r.getHeight();
//System.out.println("adjust y");
}
n=new Point(toInt(x<0?0:x),
toInt(y<0?0:y));
adjust=n;
// System.out.println("factor: "+f+
// " state origin: "+r.getX()+","+r.getY()+
// " point: "+p.getX()+","+p.getY()+
// " new origin: "+n.getX()+","+n.getY());
// System.out.println(""+vp.getViewPosition());
}
if (debug) System.out.println("*** set scale "+sx+"/"+sy);
updatePreferredSize();
limitWindowSize();
repaint();
revalidate();
if (n!=null)
vp.setViewPosition(n); //doesn't work always -> defer adhustment
fireScaleEvent(e);
return true;
}
return false;
}
public double getScaleX()
{ return scaleX;
}
public double getScaleY()
{ return scaleY;
}
public double getScale()
{ return scaleX==scaleY?scaleX:(scaleX+scaleY)/2;
}
private Set<ScaleEventListener> slisteners=new HashSet<ScaleEventListener>();
public void addScaleEventListener(ScaleEventListener h)
{
slisteners.add(h);
}
public void removeScaleEventListener(ScaleEventListener h)
{
slisteners.remove(h);
}
private void fireScaleEvent(ScaleEvent e)
{
for (ScaleEventListener h:slisteners) {
h.componentScaled(e);
}
}
///////////////////////////////////////////////////////////////////////////
// intern Event handling
private JViewport getViewPort()
{
try {
return (JViewport)getParent();
}
catch (ClassCastException cce) {
return null;
}
}
public int translateX(int x)
{
x=(int)((x-insets.left)/scaleX);
if (x<0) x=0;
if (x>=image.getWidth()) x=image.getWidth()-1;
return x;
}
public int translateY(int y)
{
y=(int)((y-insets.top)/scaleY);
if (y<0) y=0;
if (y>=image.getHeight()) y=image.getHeight()-1;
return y;
}
public int translateX(MouseEvent e)
{
return translateX(e.getX());
}
public int translateY(MouseEvent e)
{
return translateY(e.getY());
}
public int translateToComponentX(int x)
{
return toInt(x*scaleX+insets.left);
}
public int translateToComponentY(int y)
{
return toInt(y*scaleY+insets.top);
}
public int componentMiddleX(int x)
{
return translateToComponentX(x)+((int)getScaleX())/2;
}
public int componentMiddleY(int y)
{
return translateToComponentY(y)+((int)getScaleY())/2;
}
public MouseEvent translate(MouseEvent e)
{
//getInsets(insets);
if (scaleX==1 && scaleY==1 && insets.left==0 && insets.top==0) return e;
int x=translateX(e);
int y=translateY(e);
e=new MouseEvent((Component)e.getSource(), e.getID(), e.getWhen(), e.getModifiers(),
x, y, e.getXOnScreen(), e.getYOnScreen(),
e.getClickCount(), e.isPopupTrigger(), e.getButton());
return e;
}
//
// translate events for external mouse handlers
//
private class MouseHandler extends MouseAdapter
implements MouseListener, MouseMotionListener {
final Cursor point_cursor=Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
final Cursor def_cursor=Cursor.getDefaultCursor();
private RectanglePoint checkRectPoints(Rectangle r, Point p)
{
if (p.getX()<r.getX()+CORNER_RECT&&
p.getY()<r.getY()+CORNER_RECT) {
return RectPointEvent.TOP_LEFT;
}
else if (p.getX()<r.getX()+CORNER_RECT&&
p.getY()>r.getY()+r.getHeight()-CORNER_RECT) {
return RectPointEvent.BOTTOM_LEFT;
}
else if (p.getX()>=r.getX()+r.getWidth()-CORNER_RECT&&
p.getY()<r.getY()+CORNER_RECT) {
return RectPointEvent.TOP_RIGHT;
}
else if (p.getX()>=r.getX()+r.getWidth()-CORNER_RECT&&
p.getY()>=r.getY()+r.getHeight()-CORNER_RECT) {
return RectPointEvent.BOTTOM_RIGHT;
}
else if (p.getX()<r.getX()+CORNER_RECT&&
p.getY()>=r.getY()+(r.getHeight()-CORNER_RECT)/2 &&
p.getY()<=r.getY()+(r.getHeight()+CORNER_RECT)/2) {
return RectPointEvent.MIDDLE_LEFT;
}
else if (p.getX()>=r.getX()+r.getWidth()-CORNER_RECT&&
p.getY()>=r.getY()+(r.getHeight()-CORNER_RECT)/2 &&
p.getY()<=r.getY()+(r.getHeight()+CORNER_RECT)/2) {
return RectPointEvent.MIDDLE_RIGHT;
}
else if (p.getY()<r.getY()+CORNER_RECT&&
p.getX()>=r.getX()+(r.getWidth()-CORNER_RECT)/2 &&
p.getX()<=r.getX()+(r.getWidth()+CORNER_RECT)/2) {
return RectPointEvent.MIDDLE_TOP;
}
else if (p.getY()>=r.getY()+r.getHeight()-CORNER_RECT&&
p.getX()>=r.getX()+(r.getWidth()-CORNER_RECT)/2 &&
p.getX()<=r.getX()+(r.getWidth()+CORNER_RECT)/2) {
return RectPointEvent.MIDDLE_BOTTOM;
}
return null;
}
public RectanglePoint getRectanglePoint(MouseEvent e)
{
Point p=e.getPoint();
RectanglePoint rect_point;
// check points
//System.out.println("rectPoint check for "+p.getX()+","+p.getY());
//System.out.println(" full "+fullviewrect);
rect_point=checkRectPoints(fullviewrect, p);
// check visible rect
if (rect_point==null) {
//System.out.println(" visible "+content.getVisibleRect());
rect_point=checkRectPoints(content.getVisibleRect(), p);
}
// check possible view port points
if (rect_point==null) {
JViewport vp=getViewPort();
if (vp!=null) {
//System.out.println(" vieport "+vp.getViewRect());
rect_point=checkRectPoints(vp.getViewRect(), p);
}
}
return rect_point;
}
@Override
public void mouseClicked(MouseEvent e)
{ MouseEvent t=translate(e);
if (debug) {
System.out.println("click at "+e.getX()+ ","+e.getY());
System.out.println(" translated "+t.getX()+ ","+t.getY());
}
VisibleRect r=findRect(t.getPoint());
if (r!=null) {
fireRectEvent(r,t);
}
if (e.getButton()==MouseEvent.BUTTON1) {
RectanglePoint rect_point=getRectanglePoint(e);
if (rect_point!=null) fireRectangleEvent(t,rect_point);
}
}
@Override
public void mouseWheelMoved(MouseWheelEvent e)
{
// System.out.println("wheel "+e.getWheelRotation()+" "+e.getScrollAmount()+
// " "+scalemode);
if (scalemode!=0) {
int diff=e.getUnitsToScroll()/e.getScrollAmount();
switch (scalemode) {
case SCALEX:
if (diff>0) {
setScaleX(getScaleX()*1.1, e.getPoint());
}
else {
setScaleX(getScaleX()/1.1, e.getPoint());
}
break;
case SCALEY:
if (diff>0) {
setScaleY(getScaleX()*1.1, e.getPoint());
}
else {
setScaleY(getScaleX()/1.1, e.getPoint());
}
break;
default:
if (diff>0) {
setScale(getScale()*1.1, e.getPoint());
}
else {
setScale(getScale()/1.1, e.getPoint());
}
}
//System.out.println("diff="+diff);
}
}
@Override
public void mouseMoved(MouseEvent e)
{
MouseEvent t;
setCursor(def_cursor);
if (selector!=null) {
t=translate(e);
selector.mouseMoved(t);
}
RectanglePoint rect_point=getRectanglePoint(e);
if (rect_point!=null &&
points.get(rect_point)!=null && !points.get(rect_point).isEmpty()) {
setCursor(point_cursor);
}
}
@Override
public void mouseDragged(MouseEvent e)
{
//System.out.println("drag "+e.getPoint()+"/"+e.getButton());
super.mouseDragged(e);
// assure visibility of drag position in case of scroll panels
JViewport vp=getViewPort();
if (vp!=null) {
Insets parent=getParent().getInsets();
Point p=new Point(e.getX(), //+parent.left,
e.getY() //+parent.top
);
Rectangle r=vp.getViewRect();
//System.out.println("Viewport rect "+r);
if (!r.contains(p)) {
r=new Rectangle(toInt(p.getX()-10), toInt(p.getY()-10), 20, 20);
scrollRectToVisible(r);
//System.out.println("adjust done");
}
}
MouseEvent t;
if (selector!=null) {
t=translate(e);
selector.mouseDragged(t);
}
}
@Override
public void mousePressed(MouseEvent e)
{
MouseEvent t;
if (selector!=null) {
t=translate(e);
selector.mousePressed(t);
}
}
@Override
public void mouseReleased(MouseEvent e)
{
MouseEvent t;
if (selector!=null) {
t=translate(e);
selector.mouseReleased(t);
}
}
}
//////////////////////////////////////////////////////////////////////
// rectangle creation
public void setRectangleSelector(RectangleSelector s)
{
if (selector!=null) selector.uninstall();
selector=s;
if (selector!=null) selector.install(this);
}
public RectangleSelector getRectangleSelector()
{
return selector;
}
public static class RectangleSelector {
private boolean active;
private Point origin;
private VisibleRect rect;
private boolean move;
private int action;
private BufferedComponent comp;
private Stroke line;
static final Cursor move_cursor=Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
static final Cursor cross_cursor=Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
static final Cursor def_cursor=Cursor.getDefaultCursor();
public void install(BufferedComponent comp)
{
if (this.comp!=null) {
throw new IllegalStateException("selector already assigned");
}
this.comp=comp;
}
public void uninstall()
{
active=false;
origin=null;
comp=null;
if (rect!=null) {
rect.discard();
}
rect=null;
}
public void setStroke(Stroke line)
{ this.line=line;
}
protected VisibleRect getVisibleRect()
{
return rect;
}
protected void select(VisibleRect rect, int action)
{
//System.out.println("**** rect selected "+action+": "+rect);
comp.fireRectModifiedEvent(rect,action);
}
private Cursor adjustRect(VisibleRect rect,
Point origin, Point current)
{ Dimension d=adjustDimension(
new Dimension(toInt(current.getX()-origin.getX()),
toInt(current.getY()-origin.getY())));
Point p1=getRectP1(origin,d);
Point p2=getRectP2(origin,d);
rect.setFrameFromDiagonal(p1, p2);
return getCursor(d);
}
protected Cursor getCursor(Dimension d)
{
if (d.getWidth()<0) {
if (d.getHeight()<0) {
return Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR);
}
else {
return Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR);
}
}
else {
if (d.getHeight()<0) {
return Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR);
}
else {
return Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR);
}
}
}
protected Dimension adjustDimension(Dimension d)
{ return d;
}
protected Dimension adjustMove(VisibleRect rect, Dimension d)
{
return d;
}
protected Point getOrigin(VisibleRectBorder b)
{
return b.getOppositeCornerPoint();
}
protected Point getRectP1(Point origin, Dimension d)
{ return origin;
}
protected Point getRectP2(Point origin, Dimension d)
{ Point p=new Point(origin);
p.translate(toInt(d.getWidth()-1),toInt(d.getHeight()-1));
return p;
}
// event processing
public void mousePressed(MouseEvent e)
{
if (e.getButton()==MouseEvent.BUTTON1) active=true;
}
public void mouseReleased(MouseEvent e)
{
//System.out.println("released "+e.getPoint()+"/"+e.getButton());
if (e.getButton()==MouseEvent.BUTTON1) {
active=false;
origin=null;
if (rect!=null) {
VisibleRect selected=rect;
rect=null;
if (debug) System.out.println("reset cursor");
comp.setCursor(Cursor.getDefaultCursor());
select(selected,action);
}
}
}
public void mouseMoved(MouseEvent e)
{
Point current=e.getPoint();
if (e.isShiftDown()) {
VisibleRect r=comp.findRect(current,false,false);
if (r!=null) {
//System.out.println("in rect");
comp.setCursor(cross_cursor);
return;
}
}
else {
VisibleRectBorder b=comp.findRectBorder(current,false);
if (b!=null) {
//System.out.println("on border");
comp.setCursor(cross_cursor);
return;
}
}
}
public void mouseDragged(MouseEvent e)
{
//System.out.println("drag "+e.getPoint()+"/"+e.getButton());
if (active) {
Point current=e.getPoint();
if (!comp.getVisibleRect().contains(current)) return;
if (origin==null) {
// start rectangle
if (move=e.isShiftDown()) {
rect=comp.findRect(current,false,false);
if (rect==null) {
//System.out.println("found no move rect");
active=false;
return;
}
if (debug) System.out.println("found move rect "+rect.getId());
comp.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
origin=current;
action=RectModifiedEvent.RECT_MOVED;
}
else {
VisibleRectBorder b=comp.findRectBorder(current,false);
if (b!=null) {
rect=b.getRect();
origin=getOrigin(b);
action=RectModifiedEvent.RECT_RESIZED;
}
else {
origin=current;
action=RectModifiedEvent.RECT_CREATED;
}
}
}
else {
if (rect==null) {
rect=comp.createRect(null,toInt(e.getX()),toInt(e.getY()),0,0);
rect.setVisible(true);
if (line!=null) rect.setStroke(line);
}
if (move) {
rect.translate(toInt(current.getX()-origin.getX()),
toInt(current.getY()-origin.getY()));
origin=current;
}
else {
//System.out.println("adjust rect "+rect);
Cursor c=adjustRect(rect, origin, current);
if (c!=null) {
comp.setCursor(c);
}
}
}
}
}
}
public static class CenteredRectangleSelector extends RectangleSelector {
@Override
protected Point getRectP1(Point origin, Dimension d)
{
Point p=new Point(origin);
p.translate(-toInt(d.getWidth()),-toInt(d.getHeight()));
return p;
}
@Override
protected Point getOrigin(VisibleRectBorder b)
{ return b.getRect().getCenter();
}
}
public static class ProportionalRectangleSelector extends RectangleSelector {
private ProportionProvider proportion;
protected class ExtendedDimension extends Dimension {
private Dimension adjust;
public ExtendedDimension(int width, int height, Dimension adjust)
{ super(width, height);
this.adjust=adjust;
}
public Dimension getAdjustment()
{ return adjust;
}
}
public ProportionalRectangleSelector(Dimension d)
{ this.proportion=new ProportionProvider.Proportion(d);
}
public ProportionalRectangleSelector(ProportionProvider d)
{ this.proportion=d;
}
public ProportionalRectangleSelector(int w, int h)
{ this(new Dimension(w,h));
}
public ProportionProvider getProportionProvider()
{
ProportionProvider p=null;
if (getVisibleRect()!=null) {
p=getVisibleRect().getProportionProvider();
}
return p==null?proportion:p;
}
public void setProportionProvider(ProportionProvider proportion)
{
this.proportion=proportion;
}
@Override
protected Dimension adjustDimension(Dimension d)
{ double dx=d.getWidth();
double dy=d.getHeight();
double sx=Math.abs(dx);
double sy=Math.abs(dy);
double prop=this.getProportionProvider().getProportion();
double ax=sy*prop;
double ay=sx/prop;
Dimension adjust=new Dimension(0,0);
//System.out.println("PROPORTION "+prop+" for "+d);
if (ax>sx) {
adjust=new Dimension(toInt((ax-sx)*Math.signum(dx)),0);
sx=ax;
}
if (ay>sy) {
adjust=new Dimension(0,toInt((ay-sy)*Math.signum(dy)));
sy=ay;
}
int nx=toInt(sx*Math.signum(dx));
int ny=toInt(sy*Math.signum(dy));
//System.out.println("adjusted "+nx+","+ny);
return new ExtendedDimension(nx,ny,adjust);
}
@Override
protected Cursor getCursor(Dimension d)
{
if (d instanceof ExtendedDimension) {
ExtendedDimension ed=(ExtendedDimension)d;
//System.out.println("adjust "+ed.getAdjustment());
double ax=ed.getAdjustment().getWidth();
if (-20>ax || ax>20) {
if (d.getHeight()>0) {
return Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR);
}
else {
return Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR);
}
}
double ay=ed.getAdjustment().getHeight();
if (-20>ay || ay>20) {
if (d.getWidth()>0) {
return Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR);
}
else {
return Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR);
}
}
}
return super.getCursor(d);
}
}
public static class CenteredProportionalRectangleSelector
extends ProportionalRectangleSelector {
public CenteredProportionalRectangleSelector(int w, int h)
{ super(w, h);
}
public CenteredProportionalRectangleSelector(Dimension d)
{ super(d);
}
public CenteredProportionalRectangleSelector(ProportionProvider d)
{ super(d);
}
@Override
protected Point getOrigin(VisibleRectBorder b)
{ return b.getRect().getCenter();
}
@Override
protected Point getRectP1(Point origin, Dimension d)
{ Point p=new Point(origin);
p.translate(-(toInt(d.getWidth())),-(toInt(d.getHeight())));
return p;
}
}
//////////////////////////////////////////////////////////////////////
// main
//////////////////////////////////////////////////////////////////////
public static void main(String[] args)
{
//Schedule a job for the event dispatch thread:
//creating and showing this application's GUI.
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
JFrame frame=new TestFrame();
frame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
// Test code
System.out.println("***2D 1,1,10,10");
Rectangle2D rect=new Rectangle2D.Double(1,1,10,10);
System.out.println("rect: 1,1,10,10:"+rect);
System.out.println("center "+rect.getCenterX()+","+rect.getCenterY());
rect.setFrameFromDiagonal(new Point(1,1), new Point(3,3));
System.out.println("rect D(I): 1,1 to 3,3:"+rect);
rect.setFrameFromDiagonal(new Point2D.Double(1,1), new Point2D.Double(3,3));
System.out.println("rect D(D): 1,1 to 3,3:"+rect);
System.out.println("***I 1,1,10,10");
rect=new Rectangle(1,1,10,10);
System.out.println("rect: 1,1,10,10:"+rect);
System.out.println("center "+rect.getCenterX()+","+rect.getCenterY());
rect.setFrameFromDiagonal(new Point(1,1), new Point(3,3));
System.out.println("rect D(D): 1,1 to 3,3:"+rect);
rect.setFrameFromDiagonal(new Point2D.Double(1,1), new Point2D.Double(3,3));
System.out.println("rect D(I): 1,1 to 3,3:"+rect);
rect.setFrameFromCenter(new Point(2,2), new Point(3,3));
System.out.println("rect C(I): 2,2 to 3,3:"+rect);
rect.setFrameFromCenter(new Point(2,2), new Point(4,4));
System.out.println("rect C(I): 2,2 to 4,4:"+rect);
rect=new Rectangle2D.Double(1,1,11,11);
System.out.println("*** rect: 1,1,11,11:"+rect);
System.out.println("center "+rect.getCenterX()+","+rect.getCenterY());
System.out.println("bounds "+rect.getBounds());
System.out.println(" contains 0,0: "+rect.contains(0,0));
System.out.println(" contains 1,1: "+rect.contains(1,1));
System.out.println(" contains 10,10: "+rect.contains(10,10));
System.out.println(" contains 11,11: "+rect.contains(11,11));
System.out.println(" contains 12,12: "+rect.contains(12,12));
System.out.println(" contains 13,13: "+rect.contains(13,13));
}
static class TestComponent extends BufferedComponent {
TestComponent()
{ super(200,200);
setup();
setLimitWindowSize(true);
setScaleMode(true);
}
void setup()
{ Graphics2D g;
//
g=createGraphics();
int test=2;
if ((test&4)==4) {
setBorder(new EmptyBorder(10,10,10,10));
}
if ((test&1)==1) {
g.setColor(Color.GREEN);
g.drawRect(0, 0, 199, 199);
g.setColor(Color.BLACK);
g.drawRect(1, 1, 197, 197);
g.setColor(Color.RED);
g.drawRect(2, 2, 195, 195);
g.setColor(Color.BLACK);
g.drawRect(3, 3, 193, 193);
g.setColor(Color.GREEN);
g.drawRect(4, 4, 191, 191);
BufferedImage img=new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
System.out.println(img.getWidth()+"x"+img.getHeight());
Graphics2D gi=img.createGraphics();
int end=9;
gi.setColor(Color.WHITE);
gi.drawLine(0, 0, 0, end);
gi.drawLine(0, 0, end, 0);
gi.drawLine(end, end, 0, end);
gi.drawLine(end, end, end, 0);
g.drawImage(img, 111, 100, null);
g.drawImage(img, 100, 110, null);
Rectangle rect=new Rectangle(100, 100, 10, 10);
g.draw(rect);
g.drawRect(6, 6, 2, 2);
g.setColor(Color.WHITE);
g.drawLine(7, 9, 7, 15);
g.setColor(Color.BLUE);
g.draw(getBounds());
}
if ((test&2)==2) {
g.setColor(Color.BLACK);
g.setXORMode(Color.WHITE);
g.drawRect(10, 10, 20, 20);
g.drawLine(20, 20, 40, 40);
g.drawRect(1, 1, 197, 197);
createRect("TestRect1",15,40,30,40).setVisible(true).setFixed(true);
createRect("TestRect2", 5, 150, 40, 35).setVisible(true);
createRect("TestRect3", 155, 5, 30, 40).setVisible(true);
createRect("TestRect4", 155, 150, 40, 35).setVisible(true);
}
}
}
static class TestFrame extends JFrame {
TestComponent tc;
class MyListener implements RectPointEventListener,
RectEventListener {
RectangleSelector[] sel=new RectangleSelector[]{
new RectangleSelector(),
new CenteredRectangleSelector(),
new ProportionalRectangleSelector(tc),
new CenteredProportionalRectangleSelector(tc)
};
int current=-1;
public void rectanglePointClicked(RectPointEvent e)
{
System.out.println(e.getDescription());
if (e.getClickCount()==2&&e.getButton()==MouseEvent.BUTTON1) {
if (e.getCorner()==RectPointEvent.BOTTOM_LEFT) {
System.out.println(" hide all");
e.getComponent().hideAllRects();
}
if (e.getCorner()==RectPointEvent.BOTTOM_RIGHT) {
System.out.println(" show all");
e.getComponent().showAllRects();
}
}
if (e.getClickCount()==1&&e.getButton()==MouseEvent.BUTTON1) {
if (e.getCorner()==RectPointEvent.TOP_LEFT) {
current++;
if (current>=sel.length) current=0;
System.out.println(" select mode "+current);
e.getComponent().setRectangleSelector(sel[current]);
}
}
}
public void buttonClicked(RectEvent e)
{
System.out.println(e.getDescription());
if (e.getClickCount()==2&&e.getButton()==MouseEvent.BUTTON1) {
System.out.println(" hide "+e.getRect().getName());
e.getRect().setVisible(false);
}
}
}
TestFrame()
{
tc=new TestComponent();
JScrollPane sp=new JScrollPane(tc);
//sp.setBorder(new BevelBorder(BevelBorder.RAISED));
add(sp);
pack();
//setSize(100, 100);
//setMaximumSize(getSize());
System.out.println("SP: "+sp.getInsets());
MyListener l=new MyListener();
tc.addRectEventListener(l);
tc.addRectPointEventListener(l);
}
}
}