/* 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.designer;
import java.awt.AWTEvent;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JComponent;
/**
* Draws the boundaries of a child component and handles mouse events / actions.
*
* @author Christian Schneider
*/
public class ComponentBounds extends JComponent
{
/**
*
*/
private static final long serialVersionUID = 8688680759876963575L;
private static final float[] dash = { 6, 3 };
private static final BasicStroke dashStroke = new BasicStroke(1,
BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1, dash, 0 );
private JComponent component;
private boolean selected = false;
public ComponentBounds(JComponent component)
{
this.component = component;
setToolTipText(component.getClass().getName());
setOpaque(false);
install();
}
public boolean isSelected()
{
return selected;
}
public void setSelected(boolean selected)
{
if (this.selected != selected)
{
this.selected = selected;
repaint();
}
}
public ComponentView getView()
{
try
{
return (ComponentView) getParent();
}
catch (ClassCastException e)
{
return null;
}
}
protected void install()
{
component.putClientProperty(ComponentBounds.class.getName(), this);
BasicEventHandler.getInstance(this, true).install(this);
updateBounds();
}
public void uninstall()
{
component.putClientProperty(ComponentBounds.class.getName(), null);
BasicEventHandler.getInstance(this, false).uninstall(this);
}
public static ComponentBounds cbFor(JComponent c)
{
try
{
return (ComponentBounds) c.getClientProperty(ComponentBounds.class.getName());
}
catch (ClassCastException e)
{
return null;
}
}
protected void updateBounds()
{
Rectangle newBounds = component.getBounds();
Rectangle oldBounds = getBounds();
if (!newBounds.equals(oldBounds))
{
setBounds(newBounds);
}
}
protected void paintComponent(Graphics g)
{
g.setColor(selected ? Color.RED : Color.BLACK);
drawOutline(g, 0, 0, getWidth(), getHeight());
if (selected)
{
g.drawRect(0, 0, 4, 4);
g.drawRect(getWidth()-4-1, 0, 4, 4);
g.drawRect(0, getHeight()-4-1, 4, 4);
g.drawRect(getWidth()-4-1, getHeight()-4-1, 4, 4);
}
}
protected void paintChildren(Graphics g)
{
// no op
}
/**
* Draws a dash stroke outline.
*/
public static void drawOutline(Graphics g, int x, int y, int width, int height)
{
Graphics2D g2 = (Graphics2D) g;
Stroke oldStroke = g2.getStroke();
g2.setStroke(dashStroke);
g2.drawRect(0, 0, width-1, height-1);
g2.setStroke(oldStroke);
}
/**
* Handles all events.
*/
private static class BasicEventHandler
implements ComponentListener, MouseListener, MouseMotionListener
{
private Rectangle mousePressedComponentBounds;
private Point mousePressedLocation;
public static BasicEventHandler getInstance(ComponentBounds cb, boolean create)
{
BasicEventHandler eh = null;
try
{
eh = (BasicEventHandler) cb.getClientProperty(BasicEventHandler.class.getName());
}
catch (ClassCastException e)
{
// no op
}
if (eh == null && create)
{
eh = new BasicEventHandler();
}
return eh;
}
public void install(ComponentBounds cb)
{
cb.putClientProperty(BasicEventHandler.class.getName(), this);
cb.component.addComponentListener(this);
cb.addMouseListener(this);
cb.addMouseMotionListener(this);
}
public void uninstall(ComponentBounds cb)
{
cb.putClientProperty(BasicEventHandler.class.getName(), null);
cb.component.removeComponentListener(this);
cb.removeMouseListener(this);
cb.removeMouseMotionListener(this);
}
public void componentMoved(ComponentEvent e)
{
ComponentBounds cb = cbSource(e);
if (cb != null)
cb.updateBounds();
}
public void componentResized(ComponentEvent e)
{
ComponentBounds cb = cbSource(e);
if (cb != null)
cb.updateBounds();
}
public void componentHidden(ComponentEvent e)
{
// no op
}
public void componentShown(ComponentEvent e)
{
// no op
}
private ComponentBounds cbSource(AWTEvent e)
{
try
{
return (ComponentBounds) e.getSource();
}
catch (ClassCastException ex)
{
Object o = e.getSource();
if (o instanceof JComponent)
return cbFor((JComponent)o);
return null;
}
}
public void mouseClicked(MouseEvent e)
{
// no op
}
public void mouseEntered(MouseEvent e)
{
// no op
}
public void mouseExited(MouseEvent e)
{
// no op
}
public void mousePressed(MouseEvent e)
{
ComponentBounds cb = cbSource(e);
if (cb == null)
{
mousePressedComponentBounds = null;
mousePressedLocation = null;
}
else
{
ComponentView cv = cb.getView();
if (cv != null)
{
ComponentBounds currentSelection = cv.getSelectedComponent();
if (currentSelection != cb)
{
if (currentSelection!=null)
currentSelection.setSelected(false);
cv.setSelectedComponent(cb);
cb.setSelected(true);
}
}
mousePressedComponentBounds = cb.getBounds();
mousePressedLocation = e.getPoint();
}
}
public void mouseReleased(MouseEvent e)
{
// no op
}
public void mouseDragged(MouseEvent e)
{
ComponentBounds cb = cbSource(e);
Point p = mousePressedLocation;
Rectangle r = mousePressedComponentBounds;
if (p == null || r == null || cb == null)
return;
int moveX = -p.x+e.getX();
int moveY = -p.y+e.getY();
Rectangle newBounds = cb.component.getBounds();
newBounds.setLocation(cb.component.getX()+moveX,
cb.component.getY()+moveY);
Container parent = cb.component.getParent();
if (newBounds.x+newBounds.width>=parent.getWidth())
newBounds.x = parent.getWidth()-newBounds.width;
if (newBounds.x<0)
newBounds.x = 0;
if (newBounds.y+newBounds.height>=parent.getHeight())
newBounds.y = parent.getHeight()-newBounds.height;
if (newBounds.y<0)
newBounds.y = 0;
cb.component.setBounds(newBounds);
}
public void mouseMoved(MouseEvent e)
{
// no op
}
}
}