// Copyright (c) 1995, 1996 Regents of the University of California. // All rights reserved. // // This software was developed by the Arcadia project // at the University of California, Irvine. // // Redistribution and use in source and binary forms are permitted // provided that the above copyright notice and this paragraph are // duplicated in all such forms and that any documentation, // advertising materials, and other materials related to such // distribution and use acknowledge that the software was developed // by the University of California, Irvine. The name of the // University may not be used to endorse or promote products derived // from this software without specific prior written permission. // THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR // IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED // WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. // File: DiagramElement.java // Classes: DiagramElement // Original Author: ics125b spring 1996 Modifications : Kedar Patankar // $Id: DiagramElement.java,v 1.1.1.1 1997/02/27 20:52:31 chandra Exp $ package uci.graphedit; import java.util.Observer; import java.util.Observable; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import java.awt.Point; import java.awt.Graphics; import java.awt.Event; import java.awt.Rectangle; /** A class to represent objects that can be part of a * diagram. Examples of DiagramElement's are lines, text, rectangles, * and perspectives on Net-level objects like Nodes and Arcs.

* DiagramElement's are both Observer's and Observable's. They * observer other objects that they depend on, e.g. because those * objects are sub-elements of a group. They notify their observers * (e.g., instances of LayerDiagram) when they change state. Also, * they often pass along change notifications from their observees to * their observers.

* Currently DiagramElement's have a posistion and can calculate a * bounding box, I am thinking of revising that to make the bounding box * the main piece of stored data. * * @see LayerDiagram */ public abstract class DiagramElement extends Observable implements Observer { /** position, or "Pin" of the object */ private Point _position; /** Generic constructor for DiagramElement. I don't think this * actually gets called... */ public DiagramElement() { _position = new Point(0,0); } /** return the position, or "Pin", of this object */ public Point position() { return _position; } /** set the position */ public void position(int x, int y) { _position = new Point(x,y); } /** set the position by calling position(int x, int y) */ public final void position(Point p) { position(p.x, p.y); } /** Retrieve one graphical attribute of this object by * name. Subclasses define possible graphical attribute for their * instances, by default any unknown attribute is null.

* Attributes can be stored in a hashtable or in individual * variables, that is determined in the subclasses.

subclasses * that override this method should call super.getGraphicAttribute() * for attributes not handled in that class. */ public Object getGraphicAttribute(String k) { return null; } /** Set the named graphical attribute to the given value. Subclasses * define the attribute that are meaningful for a given object. By * default the value is not even stored. Subclasses need to handle * storage of attributes that they define, and pass other attributes * to super.setGraphicAttribute(). */ public abstract void setGraphicAttribute(String k, Object v); /** Set multiple graphical attribute by repeatedly calling * setGraphicAttribute() */ public void setGraphicAttributes(Hashtable newAttrs) { Enumeration cur = newAttrs.keys(); while (cur.hasMoreElements()) { String key = (String) cur.nextElement(); Object val = newAttrs.get(key); setGraphicAttribute(key, val); } } /** owner of fig object. Owners are underlying objects that "own" * the graphical DiagramElement's that represent them. For example, * a Perspective and ArcPerspective keep a pointer to the net-level * object that they represent. Also, any Fig can have NetPort as an * owner. */ private Object _owner; protected void owner(Object own) { _owner = own; } protected Object owner() { return _owner; } /** When a graphical object needs to be redraw it is said to be * "damaged". This informs the editor that this ojbect is damaged. * subclasses may implement this method differently, for example * FigList damages each of its nested Fig instances individually */ public void damagedIn(Document ed) { ed.damaged(this); } /** Remove this DiagramElement from the document being edited by the * given editor */ public void removeFrom(Document ed) { Vector v = new Vector(2); v.addElement("remove"); v.addElement(this); setChanged(); notifyObservers(v); } /** Remove this object from view and from all underlying models. By * default it assumed that there are no underlying * models. Subclasses like Perspective and ArcPerspective make the * opposite assumption. * * @see Perspective * @see ArcPerspective */ public void dispose(Document ed) { removeFrom(ed); } /** Draw the object to the screen */ abstract void draw(Graphics g); /** Draw this item as the current selected item. Needs-More-Work: * Eventually this will be eliminated and all the drawing will be * handled in a subclass of Selection. */ abstract void drawSelected(Graphics g); /** reply a new instance of a Selection class as appropriate for * this DiagramElement. */ abstract Selection selectionObject(); /** Reply true if the given point is inside the given * DiagramElement. By default reply true if the pint is in my * bounding box. Subclasses like FigCircle and ArcPerspective do * more specific checks. * * @see FigCircle * @see ArcPerspective */ public boolean inside(int x, int y) { return getBoundingBox().inside(x, y); } /** Reply true if the given point is inside this DiagramElement by * calling inside(int x, int y). */ public final boolean inside(Point pnt) { return inside(pnt.x, pnt.y); } public boolean hasId(int id){return false;} /** Reply true if the object intersects the given rectangle. Used * for selective redrawing and for multiple selections.

* needs-more-work: we probably need a within(Rectangle) operation * to do marquee selection properly. */ boolean intersects(Rectangle rct) { return getBoundingBox().intersects(rct); } /** return the center of the given figure. By default the center is * the center of the bounding box. Subclasses may want to define * something else. */ public Point center() { Rectangle bbox = getBoundingBox(); return new Point(bbox.x + bbox.width/2, bbox.y + bbox.height/2); } final int BORDER = 15; /** Reply a rectangle that arcs shoulc not route through. Basically * this is the bounding box plus some margin around all egdes. */ public Rectangle routingRect() { Rectangle bbox = getBoundingBox(); return new Rectangle(bbox.x - BORDER, bbox.y - BORDER, bbox.width + BORDER*2, bbox.height + BORDER*2); } /** Reply a handle for the given location. If there is no handle * there reply -1. Subclasses define their own handles, if * appropriate. needs-more-work: Handle objects? */ public final int pickHandle(Point p) { return pickHandle(p.x, p.y); } public int pickHandle(int x, int y) { return -1; } /** This indicates that some Action is starting a manipulation on * the receiving DiagramElement and that redrawing must take place * at the objects old location. This is implemented by notifying the * Observer's of this object that it has changed. That will eventually * result in damage regions being added to all editors that are * displaying this object.

* * Needs-more-work: could this be the cause of my redraw * glitches. The area damaged before a move, could be redrawn before * the move takes place! I should first move, then redraw the old and * new areas... */ public void startTrans() { setChanged(); notifyObservers(this); } /** This is called after an Action mondifies a DiagramElement and * the DiagramElement needs to be redrawn in its new position. Each * endTrans shuold be paired with one startTrans(). */ public void endTrans() { setChanged(); notifyObservers(this); } /** change the back-to-front ordering of a DiagramElement in * LayerDiagram. Should the DiagramElement have any say in it? * * @see LayerDiagram#reorder * @see ActionReorder */ public void reorder(int function, Layer view) { view.reorder(this, function); } /** Change the position of the object from were it is * to were it is plus dx or dy. Often called when an object is * dragged. This could be very useful if local-coordinate systems are * used because deltas need less transforming... maybe. */ void translate(int dx,int dy) { _position.x += dx; _position.y += dy; } /** Modify a selected object in response to one of its handles being * dragged. Subclasses define the behavior of their own handles. By * default just drag the whole object around. * * @param handle The ID of the handle being dragged. */ public void dragHandle(int mx, int my, int an_x, int an_y, int handle) { /* by default, assume that there are no handles to modify */ translate(mx + an_x, my + an_y); } /** Return a rectangle that bounds the entire object */ abstract Rectangle getBoundingBox(); /** DiagramElement's can define their own event handling * logic. These handlers are called if the Editor cannot handle an * event, and the mouse if over a DiagramElement */ public boolean keyDown(Event e,int key) { return false; } public boolean mouseMove(Event e,int x,int y) { return false; } public boolean mouseDrag(Event e,int x,int y) { return false; } public boolean mouseDown(Event e,int x,int y) { return false; } public boolean mouseUp(Event e,int x,int y) { return false; } public void update(Observable o, Object arg) { /* If I dont handle it, maybe my observers do */ setChanged(); notifyObservers(arg); } } /* end class DiagramElement */