View Javadoc

1   /*
2    * Copyright (c) 2005, The K-Wf Grid Consortium
3    * Fraunhofer Institute for Computer Architecture and Software Technology
4    * See http://www.kwfgrid.eu and http://www.first.fraunhofer.de for more details.
5    */
6   package net.kwfgrid.gwui;
7   
8   import org.glassbox.gui.*;
9   import org.glassbox.graphview.GraphGroup;
10  import org.glassbox.Theme;
11  import org.glassbox.SwingFactory;
12  import org.glassbox.SwingThread;
13  
14  import de.fzi.wim.guibase.graphview.lens.*;
15  import de.fzi.wim.guibase.graphview.view.*;
16  import de.fzi.wim.guibase.graphview.graph.*;
17  
18  import javax.swing.*;
19  import javax.swing.event.ChangeListener;
20  import javax.swing.event.ChangeEvent;
21  import java.awt.*;
22  import java.awt.event.*;
23  import java.awt.event.ActionEvent;
24  import java.awt.geom.Point2D;
25  import java.beans.PropertyChangeListener;
26  import java.beans.PropertyChangeEvent;
27  import java.util.*;
28  
29  import org.apache.log4j.Logger;
30  
31  /***
32     A member to manage the lenses of a <code>JGraphPane</code>. Must be member of a <code>GraphGroup</code>.
33     Offers zooming and background dragging.  
34   */
35  public class LensManager extends AbstractGroup implements VisibleMember, ChangeListener, ZoomGroup {
36      public static final String IDENTIFIER = "net.kwfgrid.gwui.LensManager";
37  
38      private static final String BUTTON_THEME_PREFIX = "kwfgrid.Toolbar.button";
39      private static final String PANEL_THEME_PREFIX = "kwfgrid.Toolbar";
40      private static final String SLIDER_THEME_PREFIX = "kwfgrid.Toolbar.button";
41      private static final String ZOOM_IN_ICON_KEY = "kwfgrid.LensManager.zoom-in.icon";
42      private static final String ZOOM_OUT_ICON_KEY = "kwfgrid.LensManager.zoom-out.icon";
43      private static final String ZOOM_FULL_ICON_KEY = "kwfgrid.LensManager.zoom-full.icon";
44      private static final Icon ZOOM_IN_ICON = Theme.getIcon(ZOOM_IN_ICON_KEY);
45      private static final Icon ZOOM_OUT_ICON = Theme.getIcon(ZOOM_OUT_ICON_KEY);
46      private static final Icon ZOOM_FULL_ICON = Theme.getIcon(ZOOM_FULL_ICON_KEY);
47      private static final String LAYOUT_GAP_KEY = "kwfgrid.Toolbar.layout-gap.size";
48      private static final Dimension LAYOUT_GAP = Theme.getSize(LAYOUT_GAP_KEY);
49      private static final String GRAPH_BORDER_KEY = "kwfgrid.LensManager.graph-border.size";
50      private static final Dimension GRAPH_BORDER = Theme.getSize(GRAPH_BORDER_KEY);
51      private static final String DRAG_CURSOR_KEY = "kwfgrid.LensManager.drag.cursor";
52      private static final Cursor DRAG_CURSOR = Theme.getCursor(DRAG_CURSOR_KEY);
53  
54      private static Logger logger = Logger.getLogger(LensManager.class);
55  
56      private Manipulator _dragger = new AbstractManipulator() {
57  	    private Point _grabpoint = null;
58  	    private Cursor _cursor = null;
59  
60  	    public String getName() {
61  		return "net.kwfgrid.gwui.LensManager._dragger";
62  	    }
63  	    
64  	    public boolean isDragging() {
65  		return _grabpoint!=null;
66  	    }
67  
68  	    public void mousePressed(MouseEvent e) {
69  		Point point=e.getPoint();
70  		if (m_graphPane.getNodeAtPoint(point)==null && m_graphPane.getNearestEdge(point)==null) {
71  		    _grabpoint=point;
72  		    _cursor = m_graphPane.getCursor();
73  		    m_graphPane.setCursor(DRAG_CURSOR);
74  		    e.consume();
75  		}
76  	    }
77  
78  	    public void mouseReleased(MouseEvent e) {
79  		if (isDragging()) {
80  		    performDrag(e.getPoint());
81  		    m_graphPane.setCursor(_cursor);
82  		    _cursor = null;
83  		    _grabpoint = null;
84  		    e.consume();
85  		}
86  	    }
87  
88  	    public void mouseDragged(MouseEvent e) {
89  		if (isDragging()) {
90  		    performDrag(e.getPoint());
91  		    e.consume();
92  		}
93  	    }
94  
95  	    protected void performDrag(Point position) {
96  		_p3.setLocation( _grabpoint.x-position.x, _grabpoint.y-position.y);
97  		_zoomlens.undoLens(_p3);
98  
99  		_p3.setLocation(_translatelens.getTranslateX() - _p3.getX(),
100 				_translatelens.getTranslateY() - _p3.getY());
101 		
102 		correctTranslate(_p3);
103 
104 		_translatelens.setTranslate(_p3.getX(), _p3.getY());
105 
106 		_grabpoint = position;
107 	    }
108 	};
109 
110     private class ZoomFullAction extends AbstractAction {
111 	public ZoomFullAction(Icon icon) {
112 	    super(null, icon);
113 	}
114 
115 	public void actionPerformed(ActionEvent e) {
116 	    focusWholeGraph();
117 	}
118     }
119 
120     private class ZoomInAction extends AbstractAction {
121 	public ZoomInAction(Icon icon) {
122 	    super(null, icon);
123 	}
124 
125 	public void actionPerformed(ActionEvent e) {
126 	    setZoomFactor(_zoomlens.getZoomFactor()+0.05d);
127 	}
128     }
129 
130     private class ZoomOutAction extends AbstractAction {
131 	public ZoomOutAction(Icon icon) {
132 	    super(null, icon);
133 	}
134 
135 	public void actionPerformed(ActionEvent e) {
136 	    setZoomFactor(_zoomlens.getZoomFactor()-0.05d);
137 	}
138     }
139 
140     private JGraphPane _graphpane;
141     private JPanel _view;
142     private JSlider _slider;
143     private JButton _zoominbutton;
144     private JButton _zoomoutbutton;
145     private JButton _zoomfullbutton;
146     private ZoomLens _zoomlens;
147     private TranslateLens _translatelens;
148     private LensSet _lensset;
149     private Rectangle _r1;
150     private Rectangle _r2;
151     private Point2D.Double _p1, _p2, _p3;
152     private Group _group;
153     private boolean _internalsliderchange;
154 
155     public LensManager() {
156 	super();
157 	_r1 = new Rectangle();
158 	_r2 = new Rectangle();
159 	_p1 = new Point2D.Double();
160 	_p2 = new Point2D.Double();
161 	_p3 = new Point2D.Double();
162 	_view = null;
163 	_group = null;
164 	_zoomlens = new ZoomLens();
165 	_translatelens = new TranslateLens();
166 	_lensset = new LensSet();
167 	_lensset.addLens(_translatelens);
168 	_lensset.addLens(_zoomlens);
169 	_internalsliderchange = false;
170 	setProperty(ZOOM_FACTOR_KEY, new Double(1d));
171     }
172 
173     public LensManager(JGraphPane graphpane) {
174 	this();
175 	setGraphPane(graphpane);
176     }
177     
178     public void setGraphPane(JGraphPane graphpane) {
179 	_graphpane = graphpane;
180 	_graphpane.setLens(_lensset);
181 	_graphpane.addManipulator(_dragger);	
182     }
183 
184     ///
185     /// Protected API
186     /// ....................................................................................................
187 
188     protected void calculateGraphBoundingBoxOnScreen(Rectangle target) {
189 	Point2D b = new Point2D.Double();
190 	b.setLocation(GRAPH_BORDER.width, GRAPH_BORDER.height);
191 
192 	logger.debug("GraphBorder: "+b);
193 
194 	_zoomlens.applyLens(b);
195 
196 	logger.debug("GraphBorder after zoom: "+b);
197 
198 	double bw = b.getX();
199 	double bh = b.getY();
200 
201 	if (_graphpane!=null && _graphpane.getGraph()!=null) {
202 	    boolean first = true;
203 	    Rectangle box = new Rectangle();
204 
205 	    Iterator nodes = _graphpane.getGraph().getNodes().iterator(); while (nodes.hasNext()) {
206 		Node node = (Node)nodes.next();
207 		_graphpane.getNodeScreenBounds(node, box);
208 
209 		logger.debug("Adding node screenbounds for node "+node+": "+box);
210 
211 		if (first) {
212 		    first = false;
213 		    target.setBounds(box.x, box.y, box.width, box.height);
214 		} else {
215 		    target.add(box);
216 		}
217 	    }
218 	    
219 	    Iterator edges = _graphpane.getGraph().getEdges().iterator(); while (edges.hasNext()) {
220 		Edge edge = (Edge)edges.next();
221 		_graphpane.getEdgeScreenBounds(edge, box);
222 
223 		logger.debug("Adding edge screenbounds for edge "+edge+": "+box);
224 
225 		target.add(box);
226 	    }
227 	} else {
228 	    target.setSize(0, 0);
229 	    target.setLocation(0, 0);
230 	}
231 
232 	logger.debug("Graph bounding box on screen without border: "+target);
233 
234 	target.grow((int)bw, (int)bh);
235 
236 	logger.debug("Calculated graph bounding box on screen: "+target);
237     }
238 
239     protected void calculateGraphBoundingBox(Rectangle target) {
240 	if (_graphpane!=null && _graphpane.getGraph()!=null) {
241 	    boolean first = true;
242 	    Rectangle nodebox = new Rectangle();
243 
244 	    nodebox.setSize(100, 100);  // fixme!
245 
246 	    Iterator nodes = _graphpane.getGraph().getNodes().iterator(); while (nodes.hasNext()) {
247 		Node node = (Node)nodes.next();
248 		nodebox.setLocation((int)node.getX() - nodebox.width / 2, (int)node.getY() - nodebox.height / 2);
249 
250 		logger.debug("Adding bounds for node "+node+": "+node.getX()+","+node.getY());
251 
252 		if (first) {
253 		    first = false;
254 		    target.setBounds(nodebox);
255 		} else {		    
256 		    target.add(nodebox);
257 		}
258 	    }
259 	} else {
260 	    target.setSize(0, 0);
261 	    target.setLocation(0, 0);
262 	}
263 	
264 	target.grow(GRAPH_BORDER.width, GRAPH_BORDER.height);
265 
266 	logger.debug("Calculated graph bounding box: "+target);
267     }
268 
269     /***
270        Correct a calculated translation so that the graph is always in the view.
271        <code>translate</code> is assumed before _zoomlens.applyLens() and is returned after _zoomlens.undoLens() so that
272        it can be directly set to _translatelens.
273        Uses _r1, _p1, _p2.
274      */
275     protected void correctTranslate(Point2D translate) {
276 	_zoomlens.applyLens(translate);
277 
278 	calculateGraphBoundingBox(_r1);
279 	_p1.setLocation(_r1.getX(), _r1.getY());
280 	_p2.setLocation(_r1.getWidth(), _r1.getHeight());
281 	_zoomlens.applyLens(_p1);
282 	_zoomlens.applyLens(_p2);
283 
284 	if (_p2.getX() <= _graphpane.getWidth()) {
285 	    translate.setLocation(- _p1.getX() - _graphpane.getWidth() / 2 + (_graphpane.getWidth() - _p2.getX()) / 2, 
286 				  translate.getY());
287 	} else {
288 	    if (translate.getX() > - _p1.getX()  - _graphpane.getWidth() / 2) {
289 		translate.setLocation(-_p1.getX()  - _graphpane.getWidth() / 2,
290 				      translate.getY());
291 	    } else if (translate.getX()  < - _p1.getX() - _p2.getX() + _graphpane.getWidth() / 2) {
292 		translate.setLocation(- _p1.getX() - _p2.getX() + _graphpane.getWidth() / 2,
293 				      translate.getY());
294 	    }
295 	}
296 
297 	if (_p2.getY() <= _graphpane.getHeight()) {
298 	    translate.setLocation(translate.getX(),
299 				  - _p1.getY() - _graphpane.getHeight() / 2 + (_graphpane.getHeight() - _p2.getY()) / 2);
300 	} else {
301 	    if (translate.getY()  > -_p1.getY()  - _graphpane.getHeight() / 2) {
302 		translate.setLocation(translate.getX(),
303 				      -_p1.getY()  - _graphpane.getHeight() / 2);
304 	    } else if (translate.getY()  < _graphpane.getHeight() / 2 - _p1.getY()  - _p2.getY()) {
305 		translate.setLocation(translate.getX(),
306 				      _graphpane.getHeight() / 2 - _p1.getY()  - _p2.getY());
307 	    }
308 	}
309 
310 	_zoomlens.undoLens(translate);
311     }
312 
313     protected void updateState() {
314 	if (getGroup()!=null && _view!=null) {
315 	    Object modalmember = getGroup().getProperty(VisibleGroup.MODAL_MEMBER_KEY);
316 	    Object status = getGroup().getProperty(WorkflowGroup.WORKFLOW_STATUS_KEY);
317 	    Object appstatus = getGroup().getProperty(Group.APPLICATION_STATUS_KEY);
318 	    
319 	    if (status==null) status = WorkflowGroup.STATUS_UNDEFINED;
320 	    
321 	    if (Group.EXITING.equals(appstatus) ||
322 		WorkflowGroup.STATUS_UNDEFINED.equals(status)) {
323 		_view.setVisible(false);
324 	    } else {
325 		_view.setVisible(true);
326 		if (modalmember!=null) {
327 		    _slider.setEnabled(false);
328 		    _slider.setToolTipText(null);
329 		} else {
330 		    _slider.setEnabled(true);
331 		    _slider.setToolTipText("Adjust Zoom Factor");
332 		}
333 	    }
334 	}	
335     }
336 
337     ///
338     /// Public API
339     /// ....................................................................................................
340 
341     /***
342        Focus 0,0.
343      */
344     public void focusNull() {
345 	_translatelens.setTranslate(0d, 0d);
346     }
347 
348     /***
349        Focus the specified node by setting the translate lens to the according values.
350        Not implemented yet.
351      */
352     public void focusNode(Node node) {
353 	
354     }
355 
356     /***
357        Change zoom factor.
358      */
359     public void setZoomFactor(double zoomfactor) {
360 	if (zoomfactor > 1d) zoomfactor = 1d;
361 	else if (zoomfactor < 0d) zoomfactor = 0d;
362 	_slider.setValue((int)(zoomfactor * 80d));
363     }
364 
365     /***
366        Fit the whole graph into the visible area.
367      */
368     public void focusWholeGraph() {
369 	logger.debug("focusWholeGraph()");
370 
371 	calculateGraphBoundingBox(_r1);
372 
373 	_r2.setLocation(0, 0);
374 	_r2.setSize(_graphpane.getWidth(), _graphpane.getHeight());
375 	
376 	logger.debug("Viewport is "+_r2);
377 
378 	double zf = Math.min(.6d, Math.min((double)_r2.getHeight() / (double)_r1.getHeight(), 
379 					   (double)_r2.getWidth() / (double)_r1.getWidth()));
380 
381 	logger.debug("Calculated zoom-factor: "+zf);
382 
383 	setProperty(ZOOM_FACTOR_KEY, new Double(zf));
384 	_zoomlens.setZoomFactor(zf);
385 
386 	Point2D smallgbb_location = new Point2D.Double();
387 	smallgbb_location.setLocation(_r1.x, _r1.y);
388 	_zoomlens.applyLens(smallgbb_location);
389 	Point2D smallgbb_size = new Point2D.Double();
390 	smallgbb_size.setLocation(_r1.width, _r1.height);
391 	_zoomlens.applyLens(smallgbb_size);
392 	double scrwidth = (double)_r2.width;
393 	double scrheight = (double)_r2.height;
394 
395 	Point2D d = new Point2D.Double();
396 	d.setLocation(- smallgbb_location.getX() - scrwidth / 2 + (scrwidth - smallgbb_size.getX()) / 2,
397 		      - smallgbb_location.getY() - scrheight / 2 + (scrheight - smallgbb_size.getY()) /2);
398 	_zoomlens.undoLens(d);
399 
400 	logger.debug("Calculated translate: "+d);
401 
402 	_translatelens.setTranslate(d.getX(), d.getY());
403 	
404 	logger.debug("Setting slider value to "+((int)(zf * 80d)));
405 
406 	_internalsliderchange = true;
407 	setZoomFactor(zf);
408 	_internalsliderchange = false;	
409 
410 	logger.debug("focusWholeGraph() exited");
411     }
412 
413     /***
414        Focus the graph center by setting the translate lens to the according values.
415        Not implemented yet.
416      */
417     public void focusGraphCenter() {
418 	
419     }
420     
421     ///
422     /// Implementation of ChangeListener
423     /// ....................................................................................................
424 
425     public void stateChanged(ChangeEvent e) {
426 	if (!_internalsliderchange) {
427 	    double zf = ((double)_slider.getValue())/80d;
428 	    setProperty(ZOOM_FACTOR_KEY, new Double(zf));
429 	    _zoomlens.setZoomFactor(zf);
430 
431 	    _p3.setLocation(_translatelens.getTranslateX(),
432 			    _translatelens.getTranslateY());
433 	    
434 	    correctTranslate(_p3);
435 
436 	    _translatelens.setTranslate(_p3.getX(), _p3.getY());
437 	}
438     }
439     
440     ///
441     /// Implemention of VisibleMember
442     /// ....................................................................................................
443 
444     public Group getGroup() {
445 	return _group;
446     }
447 
448     public void setGroup(Group group) {
449 	_group = group;
450     }
451 
452     public String getIdentifier() {
453 	return IDENTIFIER;
454     }
455 
456     public void groupPropertyChanged(String name, Object oldvalue, Object newvalue) {
457 	logger.debug("groupPropertyChanged("+name+", "+oldvalue+", "+newvalue+")");
458 
459 	if (name.equals(Group.APPLICATION_STATUS_KEY)) {
460 	    updateState();
461 	} else if (name.equals(VisibleGroup.MODAL_MEMBER_KEY)) {
462 	    updateState();
463 	} else if (name.equals(WorkflowGroup.WORKFLOW_STATUS_KEY)) {
464 	    updateState();
465 	} 
466 
467 	logger.debug("groupPropertyChanged.exit");
468     }
469 
470     public JComponent getView() {
471 	if (_view==null) {
472 	    _slider = SwingFactory.createSlider(SLIDER_THEME_PREFIX);
473 	    _slider.setOrientation(SwingConstants.HORIZONTAL);
474 	    _slider.setMaximum(80);
475 	    _slider.setMinimum(0);
476 	    _slider.setMinorTickSpacing(1);
477 	    _slider.setPaintLabels(false);
478 	    _slider.setPaintTicks(false);
479 	    _slider.setPaintTrack(true);
480 	    _slider.setSnapToTicks(true);
481 	    _slider.setValue(40);
482 	    _slider.addChangeListener(this);
483 	    
484 	    _zoomfullbutton = SwingFactory.createButton(BUTTON_THEME_PREFIX);
485 	    _zoomfullbutton.setAction(new ZoomFullAction(ZOOM_FULL_ICON));
486 
487 	    _zoominbutton = SwingFactory.createButton(BUTTON_THEME_PREFIX);
488 	    _zoominbutton.setAction(new ZoomInAction(ZOOM_IN_ICON));
489 
490 	    _zoomoutbutton = SwingFactory.createButton(BUTTON_THEME_PREFIX);
491 	    _zoomoutbutton.setAction(new ZoomOutAction(ZOOM_OUT_ICON));
492 
493 	    _view = SwingFactory.createPanel(PANEL_THEME_PREFIX);
494 	    _view.setLayout(new FlowLayout(FlowLayout.LEFT, LAYOUT_GAP.width, LAYOUT_GAP.height));
495 	    _view.add(_zoomoutbutton);
496 	    _view.add(_zoomfullbutton);
497 	    _view.add(_zoominbutton);
498 	    _view.add(_slider);
499 	    _view.setSize(_view.getLayout().preferredLayoutSize(_view));
500 	    _view.validate();
501 	    updateState();
502 	}
503 	return _view;
504     }
505 }