// 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 // $Id: Document.java,v 1.2 2000/09/19 21:08:31 dougo Exp $ // Modified by : Kedar Patankar package edu.neu.ccs.demeter.tools.apstudio.graphedit; import java.util.Observer; import java.util.Observable; import java.util.Hashtable; import java.util.Vector; import java.util.Enumeration; 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.MenuItem; import java.awt.Cursor; import java.awt.PopupMenu; import java.awt.PrintGraphics; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.event.KeyListener; import java.awt.event.KeyEvent; import java.awt.event.FocusListener; import java.awt.event.FocusEvent; /** This class provides 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,MouseListener,MouseMotionListener,KeyListener,FocusListener//,ActionListener { /** 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. Sticky variable takes care of continuous mode*/ private Mode _curMode; private static boolean _sticky = false; /** This holds the LayerComposite being displayed in the editor */ private LayerComposite _currentView; /** holds all the objects the user currently has selected */ private SelectionMultiple _selections; /** Each Editor has a RedrawManager that executes in a separate * thread of control to update damaged regions of the display. */ private RedrawManager _redrawer; /** List of net-level nodes in the connected graph model underlying * the diagram being shown in this editor. */ private UGraph _net; private Rectangle _graphBoundingBox; // Summation of all the bounding boxes /** Used to remember what NetNode is under the mouse. Used in event * dispatching to pass some events to the NetNode under the mouse. */ private UVertex _currentNode; private UEdge _currentArc; //This is for popup thing private PopupMenu _popup,_traversalPopup; private MenuItem _reorder,_properties,_delete; private MenuItem _from,_to,_bypass,_onlyThru;//,_toStop; /** refers to an object that defines the editors menus and maps menu * events into Actions */ private EditorMenus menu; // This is the Editor frame in which document is residing. This reference is important as // document is panel not window, so whenever any class requires object of type frame this // is used. private Editor _editor; /** default size of the document*/ private int _defaultWidth=1500,_defaultHeight=2000; //Default size of the vertex(i.e. the minimum size) private int _Vwidth = 45,_Vheight= 35; // Following variables keep trak of the file saving needs private boolean _cdNeedsSaving,_gcdNeedsSaving,_firstTime,_firstTimeCreated; // This number holds the current UID in use. This uid is incremented each time some // new member joins graph private int curId; private int altNo, constNo, interfaceNo; // Name of the document i.e. name of the file in general private String _documentName=null; /** default graphical attributes for newly created objects */ private Hashtable _graphAttrs; ///////////////////////////////////////////////////////////////////////////////////// // Data declaration part is over now comes the methods ///////////////////////////////////////////////////////////////////////////////////// public Rectangle getBoundingBox() { return new Rectangle(_graphBoundingBox.width,_graphBoundingBox.height); } public Dimension getPreferredSize() { int width = _graphBoundingBox.width * 2; int height = _graphBoundingBox.height * 2; width = Math.max(width, _defaultWidth); height = Math.max(height, _defaultHeight); return new Dimension(width,height); } /** 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; } public void set_pkg(Package p){_net.set_pkg(p);} public void set_preamble(JavaCode j){_net.set_preamble(j);} public Package get_pkg(){ return _net.get_pkg();} public JavaCode get_preamble(){return _net.get_preamble();} public void showDefaultPropertySheet(){_editor.showDefaultPropertySheet(this);} public void showVertexPropertySheet(Perspective p){_editor.showVertexPropertySheet(p);} public void showEdgePropertySheet(ArcPerspective a){_editor.showEdgePropertySheet(a);} /** Construct a new Editor to edit the given NetList */ public Document(Editor ed) { _net = new UGraph(); _net.initialize(); _graphBoundingBox = new Rectangle(0,0); _selections = new SelectionMultiple(); _editor=ed; _redrawer = new RedrawManager(this); _currentView = new LayerComposite(); _currentView.addSublayer(new LayerDiagram()); _currentView.addObserver(this); _graphAttrs = new Hashtable(); setGraphicAttribute("LineColor", Color.black); setGraphicAttribute("FillColor", Color.white); Globals.setFont(new Font("Times Roman",Font.PLAIN,12)); Globals.setFontMetrics(_editor.getToolkit().getFontMetrics(Globals.getFont())); mode(new ModeSelect(this,_editor)); _cdNeedsSaving=false; _gcdNeedsSaving=false; curId=0; altNo = 0; constNo = 0; interfaceNo = 0; _firstTime=false; _firstTimeCreated = false; Dimension size = getPreferredSize(); setSize(size.width,size.height); setBackground(Color.lightGray); addMouseListener(this); addMouseMotionListener(this); addKeyListener(this); addFocusListener(this); // Context sensitive menu /* _popup = new PopupMenu(); _traversalPopup=new PopupMenu("Traversal Directives"); _from = new MenuItem("From"); _from.addActionListener(this); _traversalPopup.add(_from); _to = new MenuItem("To"); _to.addActionListener(this); _traversalPopup.add(_to); _traversalPopup.addSeparator(); _bypass = new MenuItem("Bypassing"); _bypass.addActionListener(this); _traversalPopup.add(_bypass); _onlyThru = new MenuItem("OnlyThrough"); _onlyThru.addActionListener(this); _traversalPopup.add(_onlyThru); add(_traversalPopup); */ } /* public void actionPerformed(ActionEvent event) { String selection = event.getActionCommand(); setSelectionString(selection); } */ public void setSelectionString(String name) { if(_currentNode!=null) { String vname = _currentNode.get_vertexname().get_name().toString(); if(name.equals("From")) addFrom(vname); else if(name.equals("To")) addTo(vname); else if(name.equals("Bypassing")) addBypassV(vname); else if(name.equals("OnlyThrough")) addOnlyThroughV(vname); } else { String ename=null; if(_currentArc instanceof UConstEdge) ename=((UConstEdge)_currentArc).get_edgename().get_name().toString(); UVertex f = net().getNode(_currentArc.get_fromVertex()); String from = f.get_vertexname().get_name().toString(); UVertex t = net().getNode(_currentArc.get_toVertex() ); String to = t.get_vertexname().get_name().toString(); if(name.equals("Bypassing")) addBypassE(from,ename,to); else addOnlyThroughE(from,ename,to); } } private void addFrom(String name){_editor.addFrom(name);} private void addTo(String name){_editor.addTo(name);} private void addBypassV(String name){_editor.addBypassV(name);} private void addOnlyThroughV(String name){_editor.addOnlyThroughV(name);} private void addBypassE(String from,String name,String to){_editor.addBypassE(from,name,to);} private void addOnlyThroughE(String from,String name,String to){_editor.addOnlyThroughE(from,name,to);} private boolean shouldTravPopup(){return _editor.shouldTravPopup();} public UVertex getSelectedVertex(){return _currentNode;} public void showTraversalPopup(int x, int y,UVertex uv) { _currentArc=null; _currentNode=uv; _editor.showTraversalPopup(x,y,uv,_editor.shouldTravPopup()); } public void showTraversalPopup(int x, int y,UEdge ue) { _currentArc=ue; _currentNode=null; if(ue instanceof UConstEdge) { if(((UConstEdge)ue).get_edgename() == null) // filtering out repetition edge { _editor.showTraversalPopup(x,y,ue,false); return; } } _editor.showTraversalPopup(x,y,ue,_editor.shouldTravPopup()); } public void gcdNeedsSaving(boolean b) { if(_gcdNeedsSaving==b) return; _gcdNeedsSaving=b; if(!_gcdNeedsSaving) { _editor.removeFromOutstanding(_documentName); _editor.settitle("- ["+_documentName+"]"); } else { _editor.addInOutstanding(_documentName); _editor.settitle("- ["+_documentName+" *]"); } } public boolean gcdNeedsSaving(){return _gcdNeedsSaving;} public void cdNeedsSaving(boolean b) { gcdNeedsSaving(b); if(_cdNeedsSaving==b) return; _cdNeedsSaving=b; } public boolean cdNeedsSaving(){return _cdNeedsSaving;} 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 boolean firstTimeCreated(){return _firstTimeCreated;} public void firstTimeCreated(boolean b){_firstTimeCreated=b;} public void setId(int i){curId=i;} public UID nextId(){++curId;return new UID(new Integer(curId));} public String getNextConstvertName() { boolean found; String name=null; for(found = true;found;) { constNo++; name = "Const"+constNo; found = _net.checkThisOut(name); } return name; } public String getNextAltvertName() { boolean found; String name=null; for(found = true;found;) { altNo++; name = "Alt"+altNo; found = _net.checkThisOut(name); } return name; } public String getNextInterfacevertName() { boolean found; String name=null; for(found = true;found;) { interfaceNo++; name = "Const"+interfaceNo; found = _net.checkThisOut(name); } return name; } public void setCursorType(){_curMode.setCursorType();} public void setMessage(String s){_editor.setMessage(s);} public Editor getEditor(){return _editor;} public void menuFornoSelection(){_editor.menuFornoSelection();} public void menuForsingleSelection(){_editor.menuForsingleSelection();} public void menuFormultipleSelection(){_editor.menuFormultipleSelection();} /** 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 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); if( !(g instanceof PrintGraphics)) { _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 = getSize(); _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 Vector selectedDEs() { return _selections.deContents(); } public DiagramElement selectionsingle() { return _selections.firstelement();} // 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 UGraph net() { return _net; } /** 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); _graphBoundingBox.add(de.getBoundingBox()); cdNeedsSaving(true); if (de instanceof Perspective) _net.addNode((UVertex)de.owner()); else if(de instanceof ArcPerspective) _net.addArc((UEdge)de.owner()); } /** Remove a DiagramElement from the diagram being edited. */ public void remove(DiagramElement de) { view().remove(de); cdNeedsSaving(true); } /** 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(UID x){return view().pick(x);} /** find the NetNode under the mouse and store it for later use */ public boolean setCurrentNode(int x, int y) { _currentNode = nodeUnderMouse(x,y); if(_currentNode==null) return false; return true; } /** find the NetNode represented by the Perspective under the mouse, * if any */ private UVertex nodeUnderMouse(int mx, int my) { DiagramElement de = pick(mx, my); if (de instanceof Perspective) return (UVertex) de.owner(); return null; } /** find the NetNode under the mouse and store it for later use */ public boolean setCurrentArc(int x, int y) { _currentArc = arcUnderMouse(x,y); if(_currentArc==null) return false; return true; } /** find the NetNode represented by the Perspective under the mouse, * if any */ private UEdge arcUnderMouse(int mx, int my) { DiagramElement de = pick(mx, my); if (de instanceof ArcPerspective) return (UEdge)((ArcPerspective)de).owner(); 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) { damaged((DiagramElement) arg); 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"); deselectItem(de); } else if (s.equals("refresh")) {damageAll();} } }/* end update */ // ********************************************************* // // ************* User Interface *************************** // // ********************************************************* // /** 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. */ // Focussing required for keyboard handling public void focusGained(FocusEvent event){} public void focusLost(FocusEvent event){}//deselect();} // Keyboard handling public void keyReleased(KeyEvent event) { int keyCode = event.getKeyCode(); // System.out.println("key typed : "+keyCode); switch(keyCode) { case KeyEvent.VK_BACK_SPACE : case KeyEvent.VK_DELETE :_editor.executeAction(new ActionDispose(_editor,this),event); break; case KeyEvent.VK_F5:_editor.executeAction(new ActionRefresh(this),event);break; case KeyEvent.VK_F6:_editor.executeAction(new ActionClearPreview(this),event);break; } } public void keyPressed(KeyEvent event){} public void keyTyped(KeyEvent event){} // Mouse handling public void mouseClicked(MouseEvent event){} public void mouseEntered(MouseEvent event) { _curMode.setCursorType(); } public void mouseExited(MouseEvent event) { setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } public void mousePressed(MouseEvent event) { this.requestFocus(); _curMode.mouseDown(event); } public void mouseReleased(MouseEvent event) { if(event.getModifiers()==4 && event.getClickCount() == 2) return; if(event.getClickCount() == 2) { if(_curMode instanceof ModeSelect) ((ModeSelect)_curMode).handleDoubleClick(event); return; } // if(event.isPopupTrigger()) // This doesn't work on solaris hence using modifiers if(event.getModifiers()==4) { // if(shouldTravPopup()) if(_curMode instanceof ModeSelect) ((ModeSelect)_curMode).doPopupMenu(event); } else _curMode.mouseUp(event); } public void mouseMoved(MouseEvent event){} public void mouseDragged(MouseEvent event) { if(event.getModifiers()==4) return; _curMode.mouseDrag(event); } // ********************************************************* // // ************* User Interface *************************** // // ********************************************************* // public UGraph toExactGraph() { UGraph graph=new UGraph(); graph.set_pkg(net().get_pkg()); graph.set_preamble(net().get_preamble()); Enumeration elm = net().vertexEnum(); while (elm.hasMoreElements()) { UVertex u=(UVertex)elm.nextElement(); Point p=u.get_Perspective().position(); Coordinates c=new Coordinates(new X(new Integer(p.x)),new Y(new Integer(p.y))); u.set_incoming(null); for(Enumeration e= u.get_inArcIdList().elements();e.hasMoreElements();) u.add_incoming((UID)e.nextElement()); u.set_outgoing(null); // for(int i=u.get_outArcIdList().size()-1;i>=0;i--) // { // u.add_outgoing((UID)u.get_outArcIdList().elementAt(i)); // } for(Enumeration e=u.get_outArcIdList().elements();e.hasMoreElements();) u.add_outgoing((UID)e.nextElement()); u.set_position(c); ((UConstOrAltVertex)u).set_parse(new YaParse()); ((UConstOrAltVertex)u).set_keywords(null); graph.add_uvertex(u); } elm = net().edgeEnum(); while (elm.hasMoreElements()) { UEdge u=(UEdge)elm.nextElement(); if(u instanceof UConstEdge) { Cardinality cardinality; String card=((FigConstEdge)u.get_persp().get_figure()).get_cardinality(); if (card.equals("1")) cardinality=new Cardinality(new Lower(new Integer(1)),null); else if (card.equals("0..1")) cardinality=new Cardinality(new Lower(new Integer(0)), new Upper(new String("1"))); else if (card.equals("0..*")) cardinality=new Cardinality(new Lower(new Integer(0)), new Upper(new String("*"))); else cardinality=new Cardinality(new Lower(new Integer(1)), new Upper(new String("*"))); ((UConstEdge)u).set_card(cardinality); BendPoint bp = ((FigConstEdge)u.get_persp().get_figure()).get_bendPoint(); u.set_bendpoint(bp); ((UConstEdge)u).set_keywords(null); } graph.add_uedge(u); } return graph; } }/* end class Document*/