/* * Copyright (C) 2004 The Concord Consortium, Inc., * 10 Concord Crossing, Concord, MA 01742 * * Web Site: http://www.concord.org * Email: info@concord.org * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * END LICENSE */ package org.concord.swing; import java.awt.Component; import java.awt.Container; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.util.Hashtable; import java.util.Vector; import javax.swing.JComponent; public class ViewEditor extends JComponent { protected int mouseX; protected int mouseY; protected int dragX; protected int dragY; protected boolean selected; protected boolean dragged = false; protected Vector selectionList = new Vector(); protected Vector viewList = new Vector(); protected Vector freeList = new Vector(); protected Hashtable componentToEditor = new Hashtable(); protected Hashtable editorToComponent = new Hashtable(); protected SelectionBoundary boundary = new SelectionBoundary(this); protected SelectionHandle handle = null; protected Component targetComponent; protected Container container; protected Component popupComponent; protected MouseAdapter mouseAdapter = new MouseAdapter() { boolean popup = false; public void mousePressed(MouseEvent event) { boolean multiple = false; mouseX = event.getX(); mouseY = event.getY(); handle = findHandle(mouseX, mouseY); if (handle == null) { deselect(); } select(multiple); if (handle == null) { for (int i = 0; i < viewList.size(); i++) { ViewEditor editor = (ViewEditor) viewList.elementAt(i); Rectangle b = editor.getBounds(); int x = mouseX; int y = mouseY; if (b.contains(mouseX, mouseY)) { editor.select(multiple); } } } ViewEditor.this.getParent().repaint(); popup = event.isPopupTrigger(); } public void mouseReleased(MouseEvent event) { if ((popup || event.isPopupTrigger()) && (popupComponent instanceof Component)) popupComponent.setVisible(true); else if (dragged && (getParent() instanceof ViewEditor)) { setBounds(boundary.getBounds()); dragged = false; ViewEditor.this.getParent().repaint(); } } }; protected MouseMotionAdapter mouseMotionAdapter = new MouseMotionAdapter() { public void mouseDragged(MouseEvent event) { dragAction(event); } }; protected ComponentAdapter targetAdapter = new ComponentAdapter() { public void componentResized(ComponentEvent event) { if (targetComponent instanceof Component) { targetComponent.setSize(ViewEditor.this.getSize()); ViewEditor.this.getParent().repaint(); } } public void componentMoved(ComponentEvent event) { if (targetComponent instanceof Component) { targetComponent.setLocation(ViewEditor.this.getLocation()); ViewEditor.this.getParent().repaint(); } } }; protected ComponentAdapter containerAdapter = new ComponentAdapter() { public void componentResized(ComponentEvent event) { if (targetComponent instanceof Component) { ViewEditor.this.setSize(targetComponent.getSize()); ViewEditor.this.repaint(); } } public void componentMoved(ComponentEvent event) { if (targetComponent instanceof Component) { ViewEditor.this.setLocation(targetComponent.getLocation()); ViewEditor.this.repaint(); } } }; protected SelectionHandle findHandle(int x, int y) { handle = null; for (int i = 0; i < selectionList.size(); i++) { ViewEditor view = (ViewEditor) selectionList.elementAt(i); handle = view.getBoundary().findHandle(x, y); if (handle != null) { break; } } return handle; } public void deselect() { for (int i = 0; i < selectionList.size(); i++) { ((ViewEditor) selectionList.elementAt(i)).deselect(); } selectionList.removeAllElements(); selected = false; } public void select(boolean multiple) { if (getParent() instanceof ViewEditor) { ViewEditor parentView = (ViewEditor) getParent(); Vector list = parentView.selectionList; if (! multiple) { for (int i = 0; i < list.size(); i++) { ((ViewEditor) list.elementAt(i)).selected = false; } list.removeAllElements(); selected = true; parentView.select(multiple); } list.addElement(this); } } public boolean isSelected() { return selected; } public void delete() { if (getParent() instanceof ViewEditor) { ViewEditor parentView = (ViewEditor) getParent(); parentView.remove(this); Vector list = (Vector) viewList.clone(); for (int i = 0; i < list.size(); i++) { ViewEditor childView = (ViewEditor) list.elementAt(i); childView.delete(); } parentView.viewList.removeElement(this); parentView.selectionList.removeElement(this); viewList.removeAllElements(); selectionList.removeAllElements(); boundary = null; handle = null; } } protected void moveAction(ViewEditor view, int deltaX, int deltaY) { dragged = true; if (view.selected) { Rectangle b = view.getBounds(); view.setLocation(b.x + deltaX, b.y + deltaY); } } protected void dragAction(MouseEvent event) { int x = event.getX(); int y = event.getY(); int deltaX = x - mouseX; int deltaY = y - mouseY; if (handle != null) { if (handle.getPosition() == SelectionHandle.DRAG) { ViewEditor view = handle.parent.getParent(); moveAction(view, deltaX, deltaY); } else { handle.scale(deltaX, deltaY); } } else { for (int i = 0; i < viewList.size(); i++) { ViewEditor view = (ViewEditor) viewList.elementAt(i); moveAction(view, deltaX, deltaY); } } mouseX = x; mouseY = y; getParent().repaint(); } protected void paintComponent(Graphics g) { for (int i = 0; i < viewList.size(); i++) { ViewEditor view = (ViewEditor) viewList.elementAt(i); view.boundary.draw(g); } } public ViewEditor(Container container) { this(); setContainer(container); } public ViewEditor() { super(); setOpaque(false); } public void addNotify() { super.addNotify(); Container container = getParent(); if (container instanceof ViewEditor) { if (targetComponent instanceof Component) setBounds(targetComponent.getBounds()); addComponentListener(targetAdapter); } else { ViewEditor editor; for (int i = 0; i < viewList.size(); i++) { editor = (ViewEditor) viewList.elementAt(i); editor.setTarget(null); editor.removeComponentListener(targetAdapter); freeList.addElement(editor); } viewList.removeAllElements(); Component [] components = container.getComponents(); for (int i = 0; i < components.length; i++) { if (components[i] == this) continue; if (freeList.size() > 0) editor = (ViewEditor) freeList.remove(0); else editor = new ViewEditor(); editor.setTarget(components[i]); add(editor); viewList.addElement(editor); } } } public void setContainer(Container container) { if (this.container instanceof Container) { this.container.removeComponentListener(containerAdapter); this.container.remove(ViewEditor.this); removeMouseListener(mouseAdapter); removeMouseMotionListener(mouseMotionAdapter); } setTarget(container); this.container = container; if (this.container instanceof Container) { this.container.addComponentListener(containerAdapter); ViewEditor.this.addMouseListener(mouseAdapter); ViewEditor.this.addMouseMotionListener(mouseMotionAdapter); ViewEditor.this.setBounds(this.container.getBounds()); } } public void setPopupComponent(Component popup) { popupComponent = popup; } public void addComponentListener(ComponentListener listener) { if (getParent() instanceof ViewEditor) { super.addComponentListener(listener); } else { for (int i = 0; i < viewList.size(); i++) { ViewEditor editor = (ViewEditor) viewList.elementAt(i); editor.addComponentListener(listener); } } } public void removeComponentListener(ComponentListener listener) { if (getParent() instanceof ViewEditor) { super.removeComponentListener(listener); } else { for (int i = 0; i < viewList.size(); i++) { ViewEditor editor = (ViewEditor) viewList.elementAt(i); editor.removeComponentListener(listener); } } } public Container getContainer() { return container; } public void setTarget(Component component) { targetComponent = component; } public Component getTarget() { return targetComponent; } public Vector getViewList() { return viewList; } public void setBounds(Rectangle bounds) { super.setBounds(bounds); if (boundary instanceof SelectionBoundary) { boundary.setBounds(bounds); } } public void setBounds(int x, int y, int width, int height) { super.setBounds(x, y, width, height); if (boundary instanceof SelectionBoundary) { boundary.setBounds(x, y, width, height); } } public SelectionBoundary getBoundary() { return boundary; } public void setHandleSize(int width, int height) { boundary.setHandleSize(width, height); } public static class SelectionHandle extends Rectangle { public static final int TOP_LEFT = 0; public static final int TOP_MIDDLE = 1; public static final int TOP_RIGHT = 2; public static final int MIDDLE_RIGHT = 3; public static final int BOTTOM_RIGHT = 4; public static final int BOTTOM_MIDDLE = 5; public static final int BOTTOM_LEFT = 6; public static final int MIDDLE_LEFT = 7; public static final int DRAG = 8; public static final int HANDLE_WIDTH = 6; public static final int HANDLE_HEIGHT = 6; public static final int MIN_WIDTH = HANDLE_WIDTH - 2; public static final int MIN_HEIGHT = HANDLE_HEIGHT - 2; public static final Rectangle [] scaleFactors = { new Rectangle( 1, 1, -1, -1), new Rectangle( 0, 1, 0, -1), new Rectangle( 0, 1, 1, -1), new Rectangle( 0, 0, 1, 0), new Rectangle( 0, 0, 1, 1), new Rectangle( 0, 0, 0, 1), new Rectangle( 1, 0, -1, 1), new Rectangle( 1, 0, -1, 0), new Rectangle( 1, 1, 1, 1) }; protected Rectangle factors; protected int position = -1; protected SelectionBoundary parent; protected Rectangle dragBounds = new Rectangle(); public SelectionHandle(SelectionBoundary parent, int position, int w, int h) { this.width = w; this.height = h; this.position = position; this.parent = parent; factors = scaleFactors[position]; } public void scale(int dx, int dy) { ViewEditor view = parent.getParent(); Rectangle b = view.getBounds(); b.width += factors.width * dx; b.height += factors.height * dy; if (b.width < MIN_WIDTH) { b.width = MIN_WIDTH; } else { b.x += factors.x * dx; } if (b.height < MIN_HEIGHT) { b.height = MIN_HEIGHT; } else { b.y += factors.y * dy; } view.setBounds(b.x, b.y, b.width, b.height); } public void setHandleBounds(int x, int y, int w, int h) { switch (position) { case TOP_LEFT: this.x = x - width - 1; this.y = y - height - 1; break; case TOP_MIDDLE: this.x = x + w / 2 + 1 - width / 2; this.y = y - height - 1; break; case TOP_RIGHT: this.x = x + w + 2; this.y = y - height - 1; break; case MIDDLE_RIGHT: this.x = x + w + 2; this.y = y + h / 2 + 1 - height / 2; break; case BOTTOM_RIGHT: this.x = x + w + 2; this.y = y + h + 2; break; case BOTTOM_MIDDLE: this.x = x + w / 2 + 1 - width / 2; this.y = y + h + 2; break; case BOTTOM_LEFT: this.x = x - width - 1; this.y = y + h + 2; break; case MIDDLE_LEFT: this.x = x - width - 1; this.y = y + h / 2 + 1 - height / 2; break; case DRAG: dragBounds.x = x - width - 1; dragBounds.y = y - height - 1; dragBounds.width = w + 2 * width + 2; dragBounds.height = h + 2 * height + 2; break; } } public boolean contains(int x, int y) { if (position == DRAG) { return dragBounds.contains(x, y); } return super.contains(x, y); } public int getPosition() { return position; } } public static class SelectionBoundary extends Rectangle { protected int handleWidth = SelectionHandle.HANDLE_WIDTH; protected int handleHeight = SelectionHandle.HANDLE_HEIGHT; protected SelectionHandle [] handles = new SelectionHandle[9]; protected ViewEditor parent; public SelectionBoundary(ViewEditor view) { parent = view; } public void setBounds(Rectangle b) { setBounds(b.x, b.y, b.width, b.height); } public void setHandleSize(int width, int height) { handleWidth = width; handleHeight = height; for (int i = 0; i < handles.length; i++) { if (handles[i] == null) { handles[i] = new SelectionHandle(this, i, handleWidth, handleHeight); } else { handles[i].setSize(handleWidth, handleHeight); } } } public void setBounds(int x, int y, int width, int height) { x--; y--; width++; height++; super.setBounds(x, y, width, height); setHandleSize(handleWidth, handleHeight); for (int i = 0; i < handles.length; i++) { handles[i].setHandleBounds(x, y, width, height); } } public Rectangle getBounds() { Rectangle b = parent.getBounds(); b.x = x + 1; b.y = y + 1; b.width = width - 1; b.height = height - 1; return b; } public void translate(int dx, int dy) { super.translate(dx, dy); Rectangle b = getBounds(); for (int i = 0; i < handles.length; i++) { handles[i].setHandleBounds(b.x + dx, b.y + dy, width, height); } } public ViewEditor getParent() { return parent; } public SelectionHandle findHandle(int x, int y) { for (int i = 0; i < handles.length; i++) { if (handles[i].contains(x, y)) { return handles[i]; } } return null; } public void draw(Graphics g) { g.drawRect(x, y, width, height); if (parent.selected) { for (int i = 0; i < handles.length; i++) { Rectangle b = handles[i]; if (i == SelectionHandle.DRAG) { b = handles[i].dragBounds; g.drawRect(b.x, b.y, b.width, b.height); } else g.fillRect(b.x, b.y, b.width, b.height); } } } } }