001: package org.enhydra.jawe.components.graph;
002:
003: import java.awt.Graphics;
004: import java.awt.Point;
005: import java.awt.Rectangle;
006: import java.awt.event.KeyAdapter;
007: import java.awt.event.KeyEvent;
008: import java.awt.event.KeyListener;
009: import java.awt.event.MouseEvent;
010: import java.awt.event.MouseListener;
011: import java.awt.geom.Point2D;
012: import java.awt.geom.Rectangle2D;
013: import java.io.Serializable;
014: import java.util.Map;
015:
016: import javax.swing.Action;
017: import javax.swing.KeyStroke;
018: import javax.swing.SwingUtilities;
019:
020: import org.enhydra.jawe.JaWEManager;
021: import org.enhydra.jawe.base.controller.JaWEActions;
022: import org.enhydra.shark.xpdl.XMLElement;
023: import org.enhydra.shark.xpdl.XMLUtil;
024: import org.enhydra.shark.xpdl.XPDLConstants;
025: import org.enhydra.shark.xpdl.elements.Activity;
026: import org.enhydra.shark.xpdl.elements.ActivitySet;
027: import org.enhydra.shark.xpdl.elements.WorkflowProcess;
028: import org.jgraph.JGraph;
029: import org.jgraph.event.GraphLayoutCacheEvent;
030: import org.jgraph.event.GraphLayoutCacheListener;
031: import org.jgraph.event.GraphModelEvent;
032: import org.jgraph.event.GraphModelListener;
033: import org.jgraph.graph.CellHandle;
034: import org.jgraph.graph.CellView;
035: import org.jgraph.graph.DefaultGraphModel;
036: import org.jgraph.graph.GraphConstants;
037: import org.jgraph.graph.GraphContext;
038: import org.jgraph.graph.GraphLayoutCache;
039: import org.jgraph.plaf.basic.BasicGraphUI;
040:
041: /**
042: * This class and it's inner classes controls mouse actions and clipboard.
043: * It is addapted to get wanted editing cell behaviour, selection behaviour
044: * , to implement cell overlaping, to implement right participant adjustment
045: * after cell (or group of cells) is moved, and to implement proper copying
046: * and pasting/cloning of cells, as well as pasting at wanted location (along
047: * with right participant adjustment).
048: */
049: public class JaWEGraphUI extends BasicGraphUI {
050:
051: public final static int SELECTION = 0;
052: public final static int MULTIPLE_SELECTION = 1;
053: public final static int INSERT_ELEMENT = 2;
054: public final static int INSERT_PARTICIPANT = 3;
055: public final static int INSERT_SPEC_ELEMENT = 4;
056: public final static int INSERT_TRANSITION_START = 5;
057: public final static int INSERT_TRANSITION_POINTS = 6;
058:
059: protected int status;
060: protected boolean aborted = false;
061: protected boolean selectOnRelease = false;
062:
063: /**
064: * Returns graph.
065: */
066: public Graph getGraph() {
067: return (Graph) graph;
068: }
069:
070: public GraphController getGraphController() {
071: return ((GraphMarqueeHandler) marquee).getGraphController();
072: }
073:
074: /**
075: * Paint the background of this graph. Calls paintGrid.
076: */
077: protected void paintBackground(Graphics g) {
078: Rectangle pageBounds = new Rectangle(0, 0, graph.getWidth(),
079: graph.getHeight());
080:
081: if (graph.isGridVisible()) {
082: paintGrid(graph.getGridSize(), g, pageBounds);
083: }
084: }
085:
086: /**
087: * This method is called by EditAction class, as well as by
088: * pressing F2 or clicking a mouse on a cell.
089: */
090: protected boolean startEditing(Object cell, MouseEvent event) {
091: if (cell instanceof WorkflowElement) {
092: XMLElement el = ((WorkflowElement) cell)
093: .getPropertyObject();
094: if (el instanceof Activity) {
095: Activity act = (Activity) el;
096: if (act.getActivityType() == XPDLConstants.ACTIVITY_TYPE_SUBFLOW) {
097: WorkflowProcess wp = XMLUtil.getSubflowProcess(
098: JaWEManager.getInstance().getXPDLHandler(),
099: act);
100: if (wp != null)
101: getGraphController().selectGraphForElement(wp);
102: return true;
103: } else if (act.getActivityType() == XPDLConstants.ACTIVITY_TYPE_BLOCK) {
104: ActivitySet as = XMLUtil.getBlockActivitySet(act);
105: if (as != null)
106: getGraphController().selectGraphForElement(as);
107: return true;
108: }
109: } else if (el instanceof FreeTextExpressionParticipant
110: || el instanceof CommonExpressionParticipant) {
111: return true;
112: }
113:
114: JaWEManager.getInstance().getJaWEController()
115: .getJaWEActions().getAction(
116: JaWEActions.EDIT_PROPERTIES_ACTION)
117: .actionPerformed(null);
118: return true;
119: }
120: return false;
121: }
122:
123: // FIXED by xxp - there was a problem when zooming-out activity
124: public void startEditingAtCell(JGraph pGraph, Object cell) {
125: if (cell != null)
126: startEditing(cell, null);
127: }
128:
129: /**
130: * Creates the listener responsible for updating the selection based on
131: * mouse events.
132: */
133: protected MouseListener createMouseListener() {
134: return new PEMouseHandler();
135: }
136:
137: /**
138: * Handles selection in a way that we expect.
139: */
140: public class PEMouseHandler extends MouseHandler {
141:
142: public void mousePressed(MouseEvent e) {
143: if (!graph.hasFocus())
144: graph.requestFocus();
145: aborted = false;
146: selectOnRelease = false;
147: if (status == SELECTION && graph.isSelectionEnabled()) {
148: // find where was clicked...
149: int s = graph.getTolerance();
150:
151: Rectangle2D r = graph.fromScreen(new Rectangle(e.getX()
152: - s, e.getY() - s, 2 * s, 2 * s));
153: focus = (focus != null && focus.intersects(graph, r)) ? focus
154: : null;
155: Point2D point = graph
156: .fromScreen(new Point(e.getPoint()));
157:
158: // changed from original because of overlapping
159: if (focus == null) {
160: cell = graph.getNextViewAt(focus, point.getX(),
161: point.getY());
162: focus = cell;
163: } else {
164: cell = focus;
165: }
166:
167: // if it's right mouse button show popup menu, otherwise user whish to select something
168: if (SwingUtilities.isRightMouseButton(e)) {
169: // POPUP
170: if (cell != null) {
171: if (!graph.isCellSelected(cell.getCell())) {
172: selectCellForEvent(cell.getCell(), e);
173: }
174: } else {
175: graph.clearSelection();
176: }
177:
178: ((GraphMarqueeHandler) marquee).popupMenu(e
179: .getPoint());
180: } else {
181: // SIMPLE SELECTION
182: if (cell != null) {
183: if (e.getClickCount() == 2) {
184: startEditing(cell.getCell(), e);
185: } else {
186: if (graph.isCellSelected(cell.getCell())
187: && !e.isControlDown()
188: && graph.getSelectionCells().length > 1) {
189: selectOnRelease = true;
190: } else {
191: if (!graph.isCellSelected(cell
192: .getCell())
193: || e.isControlDown()) {
194: selectCellForEvent(cell.getCell(),
195: e);
196: }
197: }
198:
199: if (handle != null) {
200: handle.mousePressed(e);
201: }
202: }
203: } else {
204: // MULTIPLE SELECTION
205: marquee.mousePressed(e);
206: status = MULTIPLE_SELECTION;
207: }
208: }
209: }
210:
211: if (SwingUtilities.isRightMouseButton(e)) {
212: if (status == INSERT_TRANSITION_POINTS) {
213: status = INSERT_TRANSITION_START;
214: ((GraphMarqueeHandler) marquee).reset();
215: } else {
216: ((GraphMarqueeHandler) marquee).setSelectionMode();
217: status = SELECTION;
218: }
219: } else {
220: if (graph.isEditable()) {
221: if (status == INSERT_PARTICIPANT) {
222: // maybe latter we can add inserting participants where user choose, not at end...
223: ((GraphMarqueeHandler) marquee)
224: .insertParticipant();
225: } else if (status == INSERT_SPEC_ELEMENT) {
226: // this is reserved for special buttons... like block or process. Maybe we should move them somewhere.
227: ((GraphMarqueeHandler) marquee)
228: .insertSpecialElement();
229: } else if (status == INSERT_ELEMENT) {
230: ((GraphMarqueeHandler) marquee)
231: .insertElement((Point) getGraph()
232: .fromScreen(e.getPoint()));
233: } else if (status == INSERT_TRANSITION_START) {
234: GraphPortViewInterface gpvi = (GraphPortViewInterface) graph
235: .getPortViewAt(e.getX(), e.getY());
236: if (gpvi != null) {
237: if (((GraphMarqueeHandler) marquee)
238: .insertTransitionFirstPort(gpvi)) {
239: status = INSERT_TRANSITION_POINTS;
240: }
241: }
242: } else if (status == INSERT_TRANSITION_POINTS) {
243: GraphPortViewInterface gpvi = (GraphPortViewInterface) graph
244: .getPortViewAt(e.getX(), e.getY());
245: if (gpvi == null) {
246: ((GraphMarqueeHandler) marquee).addPoint(e
247: .getPoint());
248: ((GraphMarqueeHandler) marquee)
249: .drawTransition(e);
250: } else {
251: if (((GraphMarqueeHandler) marquee)
252: .insertTransitionSecondPort(gpvi)) {
253: status = INSERT_TRANSITION_START;
254: ((GraphMarqueeHandler) marquee).reset();
255: }
256: }
257: }
258: } else {
259: // maybe display info... external package so can't be edited...
260: }
261: }
262:
263: e.consume();
264: }
265:
266: public void mouseMoved(MouseEvent e) {
267: if (status == INSERT_TRANSITION_START
268: || status == INSERT_TRANSITION_POINTS) {
269: ((GraphMarqueeHandler) marquee).drawTransition(e);
270: }
271:
272: e.consume();
273: }
274:
275: public void mouseDragged(MouseEvent e) {
276: if (status == SELECTION && !aborted) {
277: // added - if one of selected cell is Participant there must be no dragging
278: Object[] sc = graph.getSelectionCells();
279: if (sc != null) {
280: for (int i = 0; i < sc.length; i++) {
281: if (sc[i] instanceof GraphParticipantInterface) {
282: e.consume();
283: return;
284: }
285: }
286: }
287:
288: selectOnRelease = false;
289: autoscroll(graph, e.getPoint());
290: if (handle != null) {
291: handle.mouseDragged(e);
292: }
293: } else if (status == MULTIPLE_SELECTION && !aborted) {
294: // drag rectangle for multiply selection
295: marquee.mouseDragged(e);
296: }
297:
298: e.consume();
299: }
300:
301: public void mouseReleased(MouseEvent e) {
302: if (status == SELECTION) {
303: if (handle != null && !aborted) {
304: handle.mouseReleased(e);
305: }
306:
307: if (selectOnRelease) {
308: // graph.clearSelection();
309: selectCellForEvent(cell.getCell(), e);
310: }
311: } else if (status == MULTIPLE_SELECTION
312: && graph.isSelectionEnabled() && !aborted) {
313: marquee.mouseReleased(e);
314: status = SELECTION;
315: }
316:
317: e.consume();
318: }
319: }
320:
321: /**
322: * Constructs the "root handle" for <code>context</code>.
323: *
324: * @param context
325: * reference to the context of the current selection.
326: */
327: public CellHandle createHandle(GraphContext context) {
328: if (context != null && !context.isEmpty() && graph.isEnabled())
329: return new PERootHandle(context);
330: return null;
331: }
332:
333: /**
334: * Manages selection movement. It is adapted to suport proper
335: * undo in coordination with WorkflowManager class.
336: */
337: public class PERootHandle extends RootHandle {
338: /**
339: * Creates a root handle which contains handles for the given
340: * cells. The root handle and all its childs point to the
341: * specified JGraph instance. The root handle is responsible
342: * for dragging the selection.
343: */
344: public PERootHandle(GraphContext ctx) {
345: super (ctx);
346: }
347:
348: protected Point2D getInitialLocation(Object[] cells) {
349: try {
350: return super .getInitialLocation(cells);
351: } catch (Throwable thr) {
352: return null;
353: }
354: }
355:
356: public void mouseReleased(MouseEvent event) {
357: if (event != null && !event.isConsumed()) {
358: if (activeHandle != null) {
359: activeHandle.mouseReleased(event);
360: activeHandle = null;
361: } else if (isMoving && !event.getPoint().equals(start)) {
362: if (cachedBounds != null) {
363: int dx = event.getX() - (int) start.getX();//HM, JGraph3.4.1
364: int dy = event.getY() - (int) start.getY();//HM, JGraph3.4.1
365: Point2D tmp = graph
366: .fromScreen(new Point(dx, dy));//HM, JGraph3.4.1
367: GraphLayoutCache.translateViews(views, tmp
368: .getX(), tmp.getY());//HM, JGraph3.4.1
369: }
370:
371: // Harald Meister: snap activities to grid if grid is enabled
372: if (GraphUtilities.getGraphController()
373: .getGraphSettings().shouldShowGrid()
374: && views[0] instanceof GraphActivityViewInterface) {
375: GraphActivityViewInterface view = (GraphActivityViewInterface) views[0];
376: Rectangle2D rect = view.getBounds();//HM, JGraph3.4.1
377: int dx = 0;
378: int dy = 0;
379:
380: int gridsize = GraphUtilities
381: .getGraphController()
382: .getGraphSettings().getGridSize();
383: int deltax = (int) rect.getX() % gridsize;
384: int deltay = (int) rect.getY() % gridsize;
385: int halfgrid = gridsize / 2;
386: if (deltax > halfgrid) {
387: dx += (gridsize - deltax);
388: } else {
389: dx -= deltax;
390: }
391: if (deltay > halfgrid) {
392: dy += (gridsize - deltay);
393: } else {
394: dy -= deltay;
395: }
396: Point2D tmp = graph
397: .fromScreen(new Point(dx, dy));//HM, JGraph3.4.1
398: GraphLayoutCache.translateViews(views, tmp
399: .getX(), tmp.getY());//HM, JGraph3.4.1
400: }
401: // Harald Meister
402:
403: CellView[] all = graphLayoutCache
404: .getAllDescendants(views);
405:
406: if (graph.isMoveable()) { // Move Cells
407: //if (!graphModel.isAttributeStore()) {
408: //Map propertyMap = GraphConstants.createPropertyMap(all,null);
409: Map propertyMap = GraphConstants
410: .createAttributes(all, null);
411: GraphManager gm = getGraph().getGraphManager();
412: gm.moveCellsAndArrangeParticipants(propertyMap);
413: /*} else {
414: Map propertyMap = GraphConstants.createPropertyMap(all,null);
415: WorkflowManager dm=getGraph().getWorkflowManager();
416: dm.moveCellsAndArrangeParticipants(propertyMap);
417: }*/
418: }
419: event.consume();
420: }
421: }
422: start = null;
423: }
424:
425: }
426:
427: /**
428: * Returns a listener that can update the graph when the view changes.
429: */
430: protected GraphLayoutCacheListener createGraphLayoutCacheListener() {
431: return new PEGraphLayoutCacheHandler();
432: }
433:
434: /**
435: * This class observes view changes and is adapted to disallow
436: * deselection of cells after dragging.
437: */
438: public class PEGraphLayoutCacheHandler extends
439: GraphLayoutCacheHandler {
440: /*
441: * (non-Javadoc)
442: *
443: * @see org.jgraph.event.GraphLayoutCacheListener#graphLayoutCacheChanged(org.jgraph.event.GraphLayoutCacheEvent)
444: */
445: public void graphLayoutCacheChanged(GraphLayoutCacheEvent e) {
446: Object[] changed = e.getChange().getChanged();
447: if (changed != null && changed.length > 0) {
448: for (int i = 0; i < changed.length; i++) {
449: graph.updateAutoSize(graphLayoutCache.getMapping(
450: changed[i], false));
451: }
452: }
453: Object[] inserted = e.getChange().getInserted();
454: if (inserted != null
455: && inserted.length > 0
456: && graphLayoutCache.isSelectsLocalInsertedCells()
457: && !(graphLayoutCache.isSelectsAllInsertedCells() && !graphLayoutCache
458: .isPartial()) && graph.isEnabled()) {
459: Object[] roots = DefaultGraphModel.getRoots(graphModel,
460: inserted);
461: if (roots != null && roots.length > 0) {
462: focus = graphLayoutCache
463: .getMapping(roots[0], false);
464: graph.setSelectionCells(roots);
465: }
466: }
467: updateSize();
468: }
469: }
470:
471: /**
472: * Returns a listener that can update the graph when the model changes.
473: */
474: // protected GraphModelListener createGraphModelListener() {
475: // return new PEGraphModelHandler();
476: // }
477: /**
478: * Listens for changes in the graph model and updates the view accordingly.
479: */
480: public class PEGraphModelHandler implements GraphModelListener,
481: Serializable {
482:
483: public void graphChanged(GraphModelEvent e) {
484: Object[] removed = e.getChange().getRemoved();
485: // Remove from selection & focus
486: if (removed != null && removed.length > 0) {
487: // Update Focus If Necessary
488: for (int i = 0; i < removed.length && focus != null; i++) {
489: if (removed[i] == focus.getCell()) {
490: focus = null;
491: break;
492: }
493: }
494: // Remove from selection
495: graph.getSelectionModel().removeSelectionCells(removed);
496: }
497: if (graphLayoutCache != null)
498: graphLayoutCache.graphChanged(e.getChange());
499: // Get arrays
500: Object[] inserted = e.getChange().getInserted();
501: Object[] changed = e.getChange().getChanged();
502: // Insert
503: if (inserted != null && inserted.length > 0) {
504: // Update focus to first inserted cell
505: focus = graphLayoutCache.getMapping(inserted[0], false);
506: for (int i = 0; i < inserted.length; i++)
507: graph.updateAutoSize(graphLayoutCache.getMapping(
508: inserted[i], false));
509: }
510: // Change (update size)
511: if (changed != null && changed.length > 0) {
512: for (int i = 0; i < changed.length; i++)
513: graph.updateAutoSize(graphLayoutCache.getMapping(
514: changed[i], false));
515: }
516: // Select if not partial
517: if (!graphLayoutCache.isPartial()
518: && graphLayoutCache.isSelectsAllInsertedCells()
519: && graph.isEnabled()) {
520: // Object[] roots = JaWEGraphModel.getRoots(graphModel, inserted);
521: // if (roots != null && roots.length > 0) {
522: // focus = graphLayoutCache.getMapping(roots[0], false);
523: // graph.setSelectionCells(roots);
524: // }
525: graph.setSelectionCells(inserted);
526: }
527: updateSize();
528: }
529:
530: } // End of BasicGraphUI.GraphModelHandler
531:
532: public void reset() {
533: status = ((GraphMarqueeHandler) marquee).getStatus();
534: }
535:
536: /**
537: * Creates the listener reponsible for getting key events from the graph.
538: */
539: protected KeyListener createKeyListener() {
540: return new PEKeyHandler();
541: }
542:
543: /**
544: * This is used to get multiple key down events to appropriately generate
545: * events.
546: */
547: public class PEKeyHandler extends KeyAdapter implements
548: Serializable {
549: /** Key code that is being generated for. */
550: protected Action repeatKeyAction;
551:
552: /** Set to true while keyPressed is active. */
553: protected boolean isKeyDown;
554:
555: public void keyPressed(KeyEvent e) {
556: KeyStroke keyStroke = KeyStroke.getKeyStroke(
557: e.getKeyCode(), e.getModifiers());
558: if (keyStroke.getKeyCode() == KeyEvent.VK_ESCAPE) {
559: if (marquee != null)
560: marquee.mouseReleased(null);
561: ((GraphMarqueeHandler) marquee).setSelectionMode();
562: aborted = true;
563: }
564: }
565:
566: public void keyReleased(KeyEvent e) {
567: }
568: }
569: }
|