// 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: Document.java // Classes: Document // Original Author: ics125b spring 1996 Modifications by Kedar Patankar // $Id: Document.java,v 1.1.1.1 1997/02/27 20:52:31 chandra Exp $ package uci.graphedit; import java.awt.Panel; import java.awt.Event; import java.awt.Font; import java.awt.Graphics; import java.awt.Point; import java.awt.Rectangle; import java.awt.Color; import java.awt.Dimension; import java.awt.Frame; import java.util.Observer; import java.util.Observable; import java.util.Hashtable; import java.util.Vector; import java.util.Enumeration; /** This class provides an editor frame for manipulating graphical * documents. The editor is the central class of the graph editing * framework, but it does not contain very much code. It can be this * small because all the net-level models, graphical objects, layers, * editor modes, editor actions, and supporting dialogs and frames are * implemented in their own classes. The framework defines abstract * and example classes for each of those class categories.
* * An editor presents a tree of Layer's. Normally layers contain * DiagramElement's. Some DiagramElement's are linked to * NetPrimitive's. When DiagramElement's are selected the Editor holds * a Selection object. The behavior of the editor is determined by its * current Mode. The Editor acts as a shell for executing Action's * that modify the document or the Editor itself.
* * A major goal of the graph editing framework is to make it easy to * extend the framework for application to a specific domain. It is * very important that new functionality can be added without * modifying what is already there. The fairly small size of the * editor is a good indicator that it is not a bottleneck for * enhancing the framework.
* * @see Layer * @see DiagramElement * @see NetPrimitive * @see Selection * @see Mode * @see Action */ public class Document extends Panel implements Observer { private int curId; /** The user interface mode that the editor is currently * in. Generally modes that the user has to think about are a bad * idea. But even in a very easy to use editor there are plenty of * "spring-loaded" modes that change the way the system interperts * input. For example, when placing a new node, the editor is in * ModePlace, and when dragging a handle of an object the editor is * in ModeModify. In each case moving or dragging the mouse has a * different effect from what happens when the editor is in its * default mode (usually ModeSelect) * * @see ModeModify * @see ModeSelect * @see ModePlace */ private Mode _curMode; /** _selections holds the SelectionMultiple object that describes * all the selection objects for what the user currently has * selected */ private SelectionMultiple _selections; /** Used to remember what NetNode is under the mouse. Used in event * dispatching to pass some events to the NetNode under the mouse. */ private NetNode _currentNode; /** This holds the LayerComposite being displayed in the editor */ private LayerComposite _currentView; /** List of net-level nodes in the connected graph model underlying * the diagram being shown in this editor. Multiple editors should * be able to modify the same net-level model and the changes should * be visible in all editors. This feature is not currently * demonstrated by class Example * * @see Example */ private NetList _net; /** default graphical attributes for newly created objects */ private Hashtable _graphAttrs; /** refers to an object that defines the editors menus and maps menu * events into Actions */ protected EditorMenus menu; /** Each Editor has a RedrawManager that executes in a separate * thread of control to update damaged regions of the display. */ private RedrawManager _redrawer; private Editor _editor; /** default size of the editor window */ public final int DEFAULT_WIDTH = 300; public final int DEFAULT_HEIGHT = 200; private int _Vwidth = 45; private int _Vheight= 30; private boolean _isSaved; private boolean _firstTime; private String _documentName=null; /** True if the next global Mode should remain as the next global * mode even after it has been given to one editor. */ private static boolean _sticky = false; /** Set whether the next global mode should remain as the next * global mode even after it has been given to one editor. */ public void setSticky(boolean b) { _sticky = b; } /** What mode should Editor's go into if the Palette is not * requesting a specific mode? */ private Mode defaultMode() { return new ModeSelect(this,_editor); } /** Set the next global mode */ public void mode(Mode m) { _curMode = m; } /** Set the next global mode, and set it's stickiness */ public void mode(Mode m, boolean s) { _curMode = m; _sticky = s; } /** Reply the next global mode. */ public Mode mode() { if (_curMode == null) _curMode = defaultMode(); return _curMode; } public Mode nextMode() { if (!_sticky) _curMode = defaultMode(); return _curMode; } /** Construct a new Editor to edit the given NetList */ public Document(Editor ed) { _net = new NetList(); _net.addObserver(this); _selections = new SelectionMultiple(); _editor=ed; _redrawer = new RedrawManager(this); _currentView = new LayerComposite(); _currentView.addSublayer(new LayerDiagram()); _currentView.addObserver(this); _graphAttrs = new Hashtable(); setGraphicAttribute("font",new Font("Times Roman",Font.PLAIN,12)); setGraphicAttribute("LineColor", Color.black); setGraphicAttribute("FillColor", Color.white); mode(new ModeSelect(this,_editor)); _isSaved=true; curId=0; _firstTime=false; setBackground(Color.lightGray); } public void isSaved(boolean b) { if(_isSaved==b) return; _isSaved=b; if(_isSaved) { _editor.removeFromOutstanding(_documentName); _editor.settitle("- ["+_documentName+"]"); } else { _editor.addInOutstanding(_documentName); _editor.settitle("- ["+_documentName+" *]"); } } public boolean isSaved(){return _isSaved;} public void setDocName(String s){ _documentName=s;} public String getDocName(){return _documentName;} public boolean firstTime(){return _firstTime;} public void firstTime(boolean b){_firstTime=b;} public void setId(int i){curId=i;} public int nextId(){++curId;return curId;} public void setCursorType(Editor ed){_curMode.setCursorType(ed);} public Editor getEditor(){return _editor;} /** Editors have a set of graphical attributes just like individual * DiagramElement' do. The editor uses these attributes to define * the defaults for newly created DiagramElement's. * @see DiagramElement */ public Hashtable graphAttrs() { return _graphAttrs; } public Object getGraphicAttribute(String k) { return _graphAttrs.get(k); } public void setGraphicAttribute(String k, Object v) { _graphAttrs.put(k,v); } 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); } } public void setFont(Font f){ setGraphicAttribute("font",f); } public Point vertexSize(){ Point p = new Point(_Vwidth,_Vheight); return p; } /** Paints the graphs nodes by calling draw() on view, selections, * and mode. Whenever their is a change to the screen, this method * will eventually be called from the RedrawManager. * @see RedrawManager **/ public synchronized void paint(Graphics g) { _currentView.draw(g); _selections.draw(g); if (_curMode != null) _curMode.draw(g); } /** Calling any one of the following damaged() methods adds a * damaged region (one or more rectangles) to this editors * RedrawManager. */ public void damaged(Rectangle r) { _redrawer.add(r); } public void damaged(DiagramElement de) { if (de != null) _redrawer.add(de.getBoundingBox()); } public void damaged(Selection sel) { if (sel != null) { Rectangle r = sel.getBoundingBox(); if (r != null) _redrawer.add(r); } } /** Mark the entire visible area of this Editor as * damaged. Currently called when a LayerGrid is adjusted. This will * be useful for ActionRefresh if I get around to it. Also some * Actions may perfer to do this instead of keeping track of all * modified objects, but only in cases where most of the visible * area is expected to change anyway. */ public void damageAll() { Dimension size = size(); _redrawer.add(new Rectangle(0,0,size.width,size.height)); } /** Tell my RedrawManager to repair all outstanding damaged regions * now. Generally, you should not call this method explicitly. It * already gets called periodically from another thread of * control. You should call it if you need a very tight user * interface feedback loop... */ public void repairDamage() { _redrawer.repairDamage(); } /** Paint anything that can be seen in the given Rectangle. */ public void paintRect(Rectangle r, Graphics g) { // the following line is useful for debugging redraw logic //g.drawRect(r.x-1, r.y-1, r.width+2, r.height+2); paint(g); } /** Add the given item to this editors selections */ public void selectItem(DiagramElement de) { damaged(_selections); _selections.removeAllElements(); _selections.addElement(de); damaged(de); } /** Remove the given item from this editors selections */ public void deselectItem(DiagramElement de) { if (_selections.contains(de)) { _selections.removeElement(de); damaged(de); } } /** Select the given item if it was not already selected, and * vis-a-versa */ public void toggleItem(DiagramElement de) { damaged(de); if (_selections.contains(de)) _selections.removeElement(de); else _selections.addElement(de); damaged(de); } /** Deslect everything that is currently selected */ public void deselect() { damaged(_selections); _selections.removeAllElements(); } /** Select a collection of DiagramElement's */ public void selectItems(Vector items) { damaged(_selections); _selections.removeAllElements(); _selections.addAllElements(items); damaged(_selections); } /** Toggle the selection of a collection of DiagramElement's */ public void toggleItems(Vector items) { damaged(_selections); _selections.toggleAllElements(items); damaged(_selections); } /** Reply the current selection objects of this editor */ public SelectionMultiple selection() { return _selections; } public DiagramElement selectionsingle() { return _selections.firstelement();} /** The next several methods process user interface events. There * are some events that the editor handles itself, but most useful * work is done by delegating events to the current mode, the * DiagramElement under the mouse or the node under the mouse. Menu * events are always delegated to my EditorMenus object. This scheme * could be improved by using another object that defines the * bindings of arbitrary UI events to Actions.
Please always * keep the number of built-in command and actions to a minimum. */ public boolean keyDown(Event e, int key) { boolean ret = false; // String comp_string1; // FigText text_object; setCurrentNode(e.x, e.y); /* keystroke handling for scrolling, currently scrolling is broken */ /* this does not work because the Graphics instance is only used once */ Graphics g = getGraphics(); /*if (key==Event.LEFT) { g.translate(16,0); ret = true; } else if (key==Event.RIGHT) { g.translate(-16,0); ret = true; } else if (key==Event.UP) { g.translate(0,-16); ret = true; } else if (key==Event.DOWN) { g.translate(0,16); ret = true; } */ if (!ret && _currentNode != null) ret = _currentNode.keyDown(e, key); if (!ret ) ret = _selections.keyDown(e, key); /* needs-more-work: should use key bindings table */ if (!ret && (key == 127 || key == 8)) { _editor.executeAction(new ActionDispose(this),e); ret = true; } /* needs-more-work: should allow nudging and tabbing */ if (!ret) ret = super.keyDown(e,key); return ret; } // public void updateGraphics() { repairDamage(); } /** Returns a collection of all DiagramElements in the layer * currently being edited. Not used very much now, but could be * useful for many Actions. Now used only by ArcPerspective. */ public Enumeration diagramElements() { return _currentView.elements(); } /** Reply the net-level model being edited by this editor. */ public NetList net() { return _net; } public Font getFont(){ return (Font)getGraphicAttribute("font"); } /** Return the LayerComposite that holds the diagram being edited. */ public LayerComposite view() { return _currentView; } /** Add a DiagramElement to the diagram being edited. */ public void add(DiagramElement de) { view().add(de); System.out.println(" calling from editor : element added"); this.isSaved(false); if (de instanceof Perspective) _net.addNode((NetNode)de.owner()); } /** Remove a DiagramElement from the diagram being edited. */ public void remove(DiagramElement de) { view().remove(de); this.isSaved(false); if (de instanceof Perspective) _net.removeNode((NetNode)de.owner()); } /** Reply the top DiagramElement in the current layer that contains * the given point. This is used in determining what the user * clicked on, among other uses. */ public final DiagramElement pick(Point p) { return pick(p.x, p.y); } public DiagramElement pick(int x, int y) { return view().pick(x, y); } public DiagramElement pick(int x){return view().pick(x);} /** Currently does nothing. This is wrong because the current mode * is lost if you drag out of a window accidently. */ public boolean mouseExit(Event e, int x, int y) { _editor.setCursor(Frame.DEFAULT_CURSOR); return true;; } /** When the mouse enters the window, set the Editor's mode to the * global next mode. This is the way that free standing Palette's * interact with Editors. */ public boolean mouseEnter(Event e, int x, int y) { _curMode.setCursorType(_editor); return true; } /** Try to delegate all events to the current mode, the NetNode * under the mouse, or some other object. If no object that I know * about wants to handle it, delegate to super. */ public boolean mouseUp(Event e, int x, int y) { // setCurrentNode(e.x, e.y); boolean ret = _curMode.mouseUp(e, x, y); // if (!ret && _currentNode != null) ret = _currentNode.mouseUp(e, x, y); if (!ret) ret = super.mouseUp(e, x, y); return ret; } /** Try to delegate all events to the current mode, the NetNode * under the mouse, or some other object. If no object that I know * about wants to handle it, delegate to super. */ public boolean mouseDown(Event e, int x, int y) { boolean ret; ret=_curMode.mouseDown(e,x,y); // setCurrentNode(e.x, e.y); // boolean ret = _curMode.mouseDown(e, x, y); // if (!ret && _currentNode != null) ret = _currentNode.mouseDown(e, x, y); if (!ret) ret = super.mouseDown(e, x, y); return ret; } /** Try to delegate all events to the current mode, the NetNode * under the mouse, or some other object. If no object that I know * about wants to handle it, delegate to super. */ public boolean mouseMove(Event e,int x,int y) { /* mouse moves are too frequent and usually not interesting */ boolean ret = _curMode.mouseMove(e,x,y); if (!ret) ret = super.mouseMove(e,x,y); return ret; } /** Try to delegate all events to the current mode, the NetNode * under the mouse, or some other object. If no object that I know * about wants to handle it, delegate to super. */ public boolean mouseDrag(Event e,int x,int y) { // setCurrentNode(e.x, e.y); if(x<0||y<0) return true; // System.out.println(x+"-"+y); boolean ret = _curMode.mouseDrag(e,x,y); // if (!ret && _currentNode != null) ret = _currentNode.mouseDrag(e,x,y); if (!ret) ret = super.mouseDrag(e,x,y); return ret; } /** find the NetNode under the mouse and store it for later use */ protected void setCurrentNode(int x, int y) { _currentNode = nodeUnderMouse(x,y); } /** find the NetNode represented by the Perspective under the mouse, * if any */ protected NetNode nodeUnderMouse(int mx, int my) { DiagramElement de = pick(mx, my); if (de instanceof Perspective) return ((Perspective)de).parentNode; return null; } /** process change notifications from objects that the Editor * observes. An editor observes its Layer's, which intern observe * their DiagramElement's which inturn observe their net-level * models. When something changes state the notification is * propagated up and eventually cause a damaged region and a * redraw.
* Needs-more-work: This is too complex, indirect, and * slow. But the concept is simple. */ public void update(Observable o, Object arg) { if (arg instanceof DiagramElement) { ((DiagramElement) arg).damagedIn(this); if (arg instanceof Perspective) ((Perspective)arg).damagedIn(this); } if (arg instanceof Vector) { String s = (String) ((Vector)arg).elementAt(0); Object obj = ((Vector)arg).elementAt(1); if (s.equals("remove")) { System.out.println("editor got 'remove'"); DiagramElement de; if (obj instanceof DiagramElement) de = (DiagramElement) obj; else de = view().perspectiveFor(obj); if (de != null) remove(de); else System.out.println("de is null"); //de.removeFrom(this); deselectItem(de); } else if (s.equals("refresh")) {damageAll();} } }/* end update */ }/* end class Editor */