]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/participant/PanZoomRotateHandler.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / participant / PanZoomRotateHandler.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.g2d.participant;\r
13 \r
14 import static org.simantics.g2d.canvas.Hints.KEY_CANVAS_TRANSFORM;\r
15 \r
16 import java.awt.Shape;\r
17 import java.awt.geom.AffineTransform;\r
18 import java.awt.geom.Point2D;\r
19 import java.awt.geom.Rectangle2D;\r
20 import java.util.Set;\r
21 \r
22 import org.simantics.g2d.canvas.Hints;\r
23 import org.simantics.g2d.canvas.ICanvasContext;\r
24 import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;\r
25 import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;\r
26 import org.simantics.g2d.canvas.impl.DependencyReflection.Reference;\r
27 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;\r
28 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;\r
29 import org.simantics.g2d.diagram.DiagramHints;\r
30 import org.simantics.g2d.diagram.DiagramUtils;\r
31 import org.simantics.g2d.diagram.IDiagram;\r
32 import org.simantics.g2d.diagram.participant.Selection;\r
33 import org.simantics.g2d.element.ElementUtils;\r
34 import org.simantics.g2d.element.IElement;\r
35 import org.simantics.g2d.scenegraph.SceneGraphConstants;\r
36 import org.simantics.g2d.utils.GeometryUtils;\r
37 import org.simantics.scenegraph.INode;\r
38 import org.simantics.scenegraph.g2d.G2DParentNode;\r
39 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;\r
40 import org.simantics.scenegraph.g2d.events.command.Command;\r
41 import org.simantics.scenegraph.g2d.events.command.CommandEvent;\r
42 import org.simantics.scenegraph.g2d.events.command.Commands;\r
43 import org.simantics.scenegraph.g2d.nodes.NavigationNode;\r
44 import org.simantics.scenegraph.g2d.nodes.TransformNode;\r
45 import org.simantics.scenegraph.utils.NodeUtil;\r
46 import org.simantics.utils.datastructures.hints.HintListenerAdapter;\r
47 import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
48 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;\r
49 import org.simantics.utils.datastructures.hints.IHintListener;\r
50 import org.simantics.utils.datastructures.hints.IHintObservable;\r
51 import org.simantics.utils.page.MarginUtils;\r
52 import org.simantics.utils.page.MarginUtils.Margins;\r
53 import org.simantics.utils.page.PageDesc;\r
54 import org.simantics.utils.threads.ThreadUtils;\r
55 \r
56 /**\r
57  * This participant handles pan, zoom, zoom to fit and rotate commands.\r
58  * \r
59  * Hints:\r
60  *  KEY_TRANSLATE_AMOUNT\r
61  *  KEY_ZOOM_AMOUNT\r
62  *  KEY_ROTATE_AMOUNT\r
63  *  KEY_ZOOM_TO_FIT_MARGINS\r
64  *  KEY_ZOOM_OUT_LIMIT\r
65  *  KEY_ZOOM_IN_LIMIT\r
66  * \r
67  * @author Toni Kalajainen\r
68  * @author Tuukka Lehtonen\r
69  */\r
70 public class PanZoomRotateHandler extends AbstractCanvasParticipant {\r
71 \r
72     /**\r
73      * Express whether or not the view should attempt to keep the current zoom\r
74      * level when the canvas parenting control is resized. If the viewport is\r
75      * set to be adapted to the resized control, the view transform will be\r
76      * adjusted to accommodate for this. Otherwise the view transform will be\r
77      * left alone when the control is resized.\r
78      * \r
79      * If hint is not specified, the default value is <code>true</code>.\r
80      * \r
81      * See {@link NavigationNode} for the zoom level keep implementation.\r
82      */\r
83     public final static Key KEY_ADAPT_VIEWPORT_TO_RESIZED_CONTROL = new KeyOf(Boolean.class, "ADAPT_VIEWPORT_TO_RESIZED_CONTROL");\r
84 \r
85     /**\r
86      * Limit for zooming in expressed as a percentage (100% == 1:1 == identity\r
87      * view transform). If null, there is no limit. Used with an\r
88      * ICanvasContext's hint context.\r
89      */\r
90     public final static Key KEY_ZOOM_OUT_LIMIT = new KeyOf(Double.class, "ZOOM_OUT_LIMIT");\r
91 \r
92     /**\r
93      * Limit for zooming in expressed as a percentage (100% == 1:1 == identity\r
94      * view transform). If null there is no limit. Used with an\r
95      * ICanvasContext's hint context.\r
96      */\r
97     public final static Key KEY_ZOOM_IN_LIMIT = new KeyOf(Double.class, "ZOOM_IN_LIMIT");\r
98 \r
99     public final static Key KEY_DISABLE_ZOOM = new KeyOf(Boolean.class, "DISABLE_ZOOM");\r
100 \r
101     public final static Key KEY_DISABLE_PAN = new KeyOf(Boolean.class, "DISABLE_PAN");\r
102 \r
103 \r
104     @Dependency CanvasGrab grab;\r
105     @Dependency TransformUtil util;\r
106     @Dependency KeyUtil keys;\r
107     @Reference  Selection selection;\r
108     @Reference  CanvasBoundsParticipant bounds;\r
109 \r
110     // Capture center point\r
111     Point2D centerPointControl;\r
112     Point2D centerPointCanvas;\r
113     Point2D controlSize;\r
114 \r
115     final Boolean navigationEnabled;\r
116 \r
117     protected NavigationNode node = null;\r
118     protected G2DParentNode oldRoot = null;\r
119 \r
120     public PanZoomRotateHandler() {\r
121         this(true);\r
122     }\r
123 \r
124     public PanZoomRotateHandler(boolean navigationEnabled) {\r
125         this.navigationEnabled = navigationEnabled;\r
126     }\r
127 \r
128     NavigationNode.TransformListener transformListener = new NavigationNode.TransformListener() {\r
129         @Override\r
130         public void transformChanged(final AffineTransform transform) {\r
131             ThreadUtils.asyncExec(PanZoomRotateHandler.this.getContext().getThreadAccess(), new Runnable() {\r
132                 @Override\r
133                 public void run() {\r
134                     if (isRemoved())\r
135                         return;\r
136                     //System.out.println("PanZoomRotateHandler: set canvas transform: " + transform);\r
137                     setHint(KEY_CANVAS_TRANSFORM, transform);\r
138                 }\r
139             });\r
140         }\r
141     };\r
142 \r
143     IHintListener hintListener = new HintListenerAdapter() {\r
144         @Override\r
145         public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {\r
146             if (node != null) {\r
147                 if (key == Hints.KEY_DISABLE_PAINTING) {\r
148                     boolean visible = !Boolean.TRUE.equals(newValue);\r
149                     if (visible != node.isVisible())\r
150                         node.setVisible(Boolean.valueOf(visible));\r
151                 } else if (key == KEY_ADAPT_VIEWPORT_TO_RESIZED_CONTROL) {\r
152                     boolean noKeepZoom = Boolean.FALSE.equals(newValue);\r
153                     if (noKeepZoom == node.getAdaptViewportToResizedControl())\r
154                         node.setAdaptViewportToResizedControl(Boolean.valueOf(!noKeepZoom));\r
155                 } else if (key == KEY_ZOOM_OUT_LIMIT) {\r
156                     node.setZoomOutLimit((Double) newValue);\r
157                 } else if (key == KEY_ZOOM_IN_LIMIT) {\r
158                     node.setZoomInLimit((Double) newValue);\r
159                 } else if (key == KEY_DISABLE_ZOOM) {\r
160                     node.setZoomEnabled(!Boolean.TRUE.equals(getHint(KEY_DISABLE_ZOOM)));\r
161                 }\r
162             }\r
163         }\r
164     };\r
165 \r
166     @Override\r
167     public void addedToContext(ICanvasContext ctx) {\r
168         super.addedToContext(ctx);\r
169         ctx.getDefaultHintContext().addKeyHintListener(Hints.KEY_DISABLE_PAINTING, hintListener);\r
170         ctx.getDefaultHintContext().addKeyHintListener(KEY_ADAPT_VIEWPORT_TO_RESIZED_CONTROL, hintListener);\r
171         ctx.getDefaultHintContext().addKeyHintListener(KEY_ZOOM_OUT_LIMIT, hintListener);\r
172         ctx.getDefaultHintContext().addKeyHintListener(KEY_ZOOM_IN_LIMIT, hintListener);\r
173         ctx.getDefaultHintContext().addKeyHintListener(KEY_DISABLE_ZOOM, hintListener);\r
174         ctx.getDefaultHintContext().addKeyHintListener(KEY_DISABLE_PAN, hintListener);\r
175     }\r
176 \r
177     @Override\r
178     public void removedFromContext(ICanvasContext ctx) {\r
179         ctx.getDefaultHintContext().removeKeyHintListener(KEY_ZOOM_IN_LIMIT, hintListener);\r
180         ctx.getDefaultHintContext().removeKeyHintListener(KEY_ZOOM_OUT_LIMIT, hintListener);\r
181         ctx.getDefaultHintContext().removeKeyHintListener(KEY_ADAPT_VIEWPORT_TO_RESIZED_CONTROL, hintListener);\r
182         ctx.getDefaultHintContext().removeKeyHintListener(Hints.KEY_DISABLE_PAINTING, hintListener);\r
183         ctx.getDefaultHintContext().removeKeyHintListener(KEY_DISABLE_ZOOM, hintListener);\r
184         ctx.getDefaultHintContext().removeKeyHintListener(KEY_DISABLE_PAN, hintListener);\r
185         super.removedFromContext(ctx);\r
186     }\r
187 \r
188     @SGInit\r
189     public void initSG(G2DParentNode parent) {\r
190         // Replace old NAVIGATION_NODE with a new one\r
191         INode oldnav = NodeUtil.getRootNode(parent).getNode(SceneGraphConstants.NAVIGATION_NODE_NAME);\r
192         if(oldnav != null) {\r
193             node = oldnav.appendParent(SceneGraphConstants.NAVIGATION_NODE_NAME, NavigationNode.class);\r
194             // FIXME : oldnav seems to be the same node as parent (most of the cases).\r
195             // Deleting it will cause plenty of code to fail, since they refer to the node directly.\r
196             // The bug was not shown, since deleting() a Node did not actually wipe its structures (until now).             \r
197             // oldnav.delete();\r
198         } else {\r
199             node = parent.addNode(SceneGraphConstants.NAVIGATION_NODE_NAME, NavigationNode.class);\r
200         }\r
201         node.setLookupId(SceneGraphConstants.NAVIGATION_NODE_NAME);\r
202         node.setZIndex(0);\r
203         node.setTransformListener(transformListener);\r
204         node.setNavigationEnabled(navigationEnabled);\r
205         node.setZoomEnabled(!Boolean.TRUE.equals(getHint(KEY_DISABLE_ZOOM)));\r
206         node.setAdaptViewportToResizedControl(!Boolean.FALSE.equals(getHint(KEY_ADAPT_VIEWPORT_TO_RESIZED_CONTROL)));\r
207         Double z = getHint(KEY_ZOOM_AMOUNT);\r
208         if(z != null) {\r
209             util.setTransform(AffineTransform.getScaleInstance(z, z));\r
210             node.setTransform(AffineTransform.getScaleInstance(z, z));\r
211         }\r
212         boolean visible = !Boolean.TRUE.equals(getHint(Hints.KEY_DISABLE_PAINTING));\r
213         node.setVisible(visible);\r
214         oldRoot = getContext().getCanvasNode();\r
215         getContext().setCanvasNode(node);\r
216     }\r
217 \r
218     public void update() {\r
219         if (bounds != null) {\r
220             Rectangle2D vp = bounds.getControlBounds();\r
221             controlSize = new Point2D.Double(vp.getMaxX(), vp.getMaxY());\r
222             centerPointControl = new Point2D.Double(vp.getCenterX(), vp.getCenterY());\r
223             centerPointCanvas = util.controlToCanvas(centerPointControl, null);\r
224         }\r
225     }\r
226 \r
227     public TransformNode getNode() {\r
228         return node;\r
229     }\r
230 \r
231     /**\r
232      * Ensures that the navigation node handled by this participant contains the\r
233      * specified transform and that {@link Hints#KEY_CANVAS_TRANSFORM} will\r
234      * contain the same value.\r
235      * \r
236      * @param transform\r
237      */\r
238     public void setTransform(AffineTransform transform) {\r
239         getNode().setTransform(transform);\r
240         transformListener.transformChanged(transform);\r
241     }\r
242 \r
243     @SGCleanup\r
244     public void cleanupSG() {\r
245         node.remove();\r
246         node = null;\r
247         getContext().setCanvasNode(oldRoot);\r
248     }\r
249 \r
250 \r
251     /** Arrow key translate */\r
252     public final static Key KEY_TRANSLATE_AMOUNT = new KeyOf(Integer.class);\r
253     public final static Key KEY_ZOOM_AMOUNT = new KeyOf(Double.class);\r
254     public final static Key KEY_ROTATE_AMOUNT = new KeyOf(Double.class);\r
255 \r
256     /** Amount of arrow key translate */\r
257     public final static int DEFAULT_KEYBOARD_TRANSLATE_AMOUNT = 30;\r
258     public final static double DEFAULT_KEYBOARD_ZOOM_AMOUNT = 1.2;\r
259     public final static double DEFAULT_KEYBOARD_ROTATE_AMOUNT = 0.1;\r
260     public final static Margins DEFAULT_ZOOM_TO_FIT_MARGINS = RulerPainter.RULER_MARINGS2;\r
261     public final static Margins DEFAULT_ZOOM_TO_FIT_MARGINS_NO_RULER = MarginUtils.MARGINS2;\r
262 \r
263     public final static int ROTATE_GRAB_ID = -666;\r
264 \r
265     @EventHandler(priority = 0)\r
266     public boolean handleEvent(CommandEvent e) {\r
267         assertDependencies();\r
268         update();\r
269         Command c = e.command;\r
270         boolean panDisabled = Boolean.TRUE.equals(getHint(KEY_DISABLE_PAN)) ? true : false;\r
271         boolean zoomDisabled = Boolean.TRUE.equals(getHint(KEY_DISABLE_ZOOM)) ? true : false;\r
272 \r
273         // Arrow key panning\r
274         if (Commands.PAN_LEFT.equals(c) && !panDisabled) {\r
275             util.translateWithControlCoordinates(\r
276                     new Point2D.Double(\r
277                             getTranslateAmount(), 0));\r
278             return true;\r
279         }\r
280         if (Commands.PAN_RIGHT.equals(c) && !panDisabled) {\r
281             util.translateWithControlCoordinates(\r
282                     new Point2D.Double(\r
283                             -getTranslateAmount(), 0));\r
284             return true;\r
285         }\r
286         if (Commands.PAN_UP.equals(c) && !panDisabled) {\r
287             util.translateWithControlCoordinates(\r
288                     new Point2D.Double(\r
289                             0, getTranslateAmount()));\r
290             return true;\r
291         }\r
292         if (Commands.PAN_DOWN.equals(c) && !panDisabled) {\r
293             util.translateWithControlCoordinates(\r
294                     new Point2D.Double(0, -getTranslateAmount()));\r
295             return true;\r
296         }\r
297         if (Commands.ZOOM_IN.equals(c) && !zoomDisabled) {\r
298             if (centerPointControl == null) return false;\r
299             double scaleFactor = getZoomAmount();\r
300             scaleFactor = limitScaleFactor(scaleFactor);\r
301             util.zoomAroundControlPoint(scaleFactor, centerPointControl);\r
302         }\r
303         if (Commands.ZOOM_OUT.equals(c) && !zoomDisabled) {\r
304             if (centerPointControl == null) return false;\r
305             double scaleFactor = 1 / getZoomAmount();\r
306             scaleFactor = limitScaleFactor(scaleFactor);\r
307             util.zoomAroundControlPoint(scaleFactor, centerPointControl);\r
308         }\r
309 \r
310         if (Commands.ROTATE_CANVAS_CCW.equals(c)) {\r
311             if (centerPointCanvas == null) return false;\r
312             util.rotate(centerPointCanvas, -getRotateAmount());\r
313             setDirty();\r
314             return true;\r
315         }\r
316         if (Commands.ROTATE_CANVAS_CW.equals(c)) {\r
317             if (centerPointCanvas == null) return false;\r
318             util.rotate(centerPointCanvas, getRotateAmount());\r
319             setDirty();\r
320             return true;\r
321         }\r
322         if (Commands.ROTATE_CANVAS_CCW_GRAB.equals(c)) {\r
323             if (centerPointCanvas == null) return false;\r
324             util.rotate(centerPointCanvas, -getRotateAmount());\r
325             grab.grabCanvas(ROTATE_GRAB_ID, centerPointCanvas);\r
326             grab.grabCanvas(ROTATE_GRAB_ID - 1, centerPointCanvas);\r
327             setDirty();\r
328             return true;\r
329         }\r
330         if (Commands.ROTATE_CANVAS_CW_GRAB.equals(c)) {\r
331             if (centerPointCanvas == null) return false;\r
332             util.rotate(centerPointCanvas, getRotateAmount());\r
333             grab.grabCanvas(ROTATE_GRAB_ID, centerPointCanvas);\r
334             grab.grabCanvas(ROTATE_GRAB_ID - 1, centerPointCanvas);\r
335             setDirty();\r
336             return true;\r
337         }\r
338         if (Commands.ROTATE_CANVAS_CCW_RELEASE.equals(c)) {\r
339             if (centerPointCanvas == null) return false;\r
340             grab.releaseCanvas(ROTATE_GRAB_ID);\r
341             grab.releaseCanvas(ROTATE_GRAB_ID - 1);\r
342             setDirty();\r
343             return true;\r
344         }\r
345         if (Commands.ROTATE_CANVAS_CW_RELEASE.equals(c)) {\r
346             if (centerPointCanvas == null) return false;\r
347             grab.releaseCanvas(ROTATE_GRAB_ID);\r
348             grab.releaseCanvas(ROTATE_GRAB_ID - 1);\r
349             setDirty();\r
350             return true;\r
351         }\r
352         if (Commands.ENABLE_PAINTING.equals(c)) {\r
353             Boolean t = getHint(Hints.KEY_DISABLE_PAINTING);\r
354             removeHint(Hints.KEY_DISABLE_PAINTING);\r
355             boolean processed = Boolean.TRUE.equals(t);\r
356             if (processed)\r
357                 setDirty();\r
358             return processed;\r
359         }\r
360         if (Commands.ZOOM_TO_FIT.equals(c) && !zoomDisabled) {\r
361             boolean result = zoomToFit();\r
362             if (!result)\r
363                 result = zoomToPage();\r
364             return result;\r
365         }\r
366         if (Commands.ZOOM_TO_SELECTION.equals(c) && !zoomDisabled && selection != null) {\r
367             if (controlSize==null) return false;\r
368             IDiagram d = getHint(DiagramHints.KEY_DIAGRAM);\r
369             if (d==null) return false;\r
370 \r
371             Set<IElement> selections = selection.getAllSelections();\r
372             Shape bounds = ElementUtils.getElementBoundsOnDiagram(selections);\r
373             if (bounds == null) return false;\r
374             Rectangle2D diagramRect = bounds.getBounds2D();\r
375             if (diagramRect.getWidth() <= 0 && diagramRect.getHeight() <= 0)\r
376                 return false;\r
377 \r
378             // HACK: prevents straight connections from being unzoomable.\r
379             if (diagramRect.getWidth() <= 0)\r
380                 org.simantics.scenegraph.utils.GeometryUtils.expandRectangle(diagramRect, 0, 0, 1, 1);\r
381             if (diagramRect.getHeight() <= 0)\r
382                 org.simantics.scenegraph.utils.GeometryUtils.expandRectangle(diagramRect, 1, 1, 0, 0);\r
383 \r
384             // Show area\r
385             Rectangle2D controlArea = new Rectangle2D.Double(0, 0, controlSize.getX(), controlSize.getY());\r
386             util.fitArea(controlArea, diagramRect, getZoomToFitMargins(getHintStack()));\r
387             return true;\r
388         }\r
389         if (Commands.ZOOM_TO_PAGE.equals(c) && !zoomDisabled) {\r
390             return zoomToPage();\r
391         }\r
392 \r
393         return false;\r
394     }\r
395 \r
396     private boolean zoomToFit() {\r
397         if (controlSize==null) return false;\r
398         IDiagram d = getHint(DiagramHints.KEY_DIAGRAM);\r
399         if (d==null) return false;\r
400 \r
401         Rectangle2D diagramRect = DiagramUtils.getContentRect(d);\r
402         if (diagramRect==null) return false;\r
403         if (diagramRect.isEmpty())\r
404             return false;\r
405 \r
406         // Show area\r
407         Rectangle2D controlArea = new Rectangle2D.Double(0, 0, controlSize.getX(), controlSize.getY());\r
408         //System.out.println("zoomToFit(" + controlArea + ", " + diagramRect + ")");\r
409         util.fitArea(controlArea, diagramRect, getZoomToFitMargins(getHintStack()));\r
410 \r
411         return true;\r
412     }\r
413 \r
414     private boolean zoomToPage() {\r
415         if (controlSize==null) return false;\r
416         PageDesc desc = getHint(Hints.KEY_PAGE_DESC);\r
417         if (desc == null)\r
418             return false;\r
419         if (desc.isInfinite())\r
420             return false;\r
421 \r
422         // Show page\r
423         Rectangle2D diagramRect = new Rectangle2D.Double();\r
424         desc.getPageRectangle(diagramRect);\r
425         if (diagramRect.isEmpty())\r
426             return false;\r
427 \r
428         Rectangle2D controlArea = new Rectangle2D.Double(0, 0, controlSize.getX(), controlSize.getY());\r
429         //System.out.println("zoomToPage(" + controlArea + ", " + diagramRect + ")");\r
430         util.fitArea(controlArea, diagramRect, getZoomToFitMargins(getHintStack()));\r
431         return true;\r
432     }\r
433 \r
434     public double getTranslateAmount()\r
435     {\r
436         Integer h = getHint(KEY_TRANSLATE_AMOUNT);\r
437         if (h==null) return DEFAULT_KEYBOARD_TRANSLATE_AMOUNT;\r
438         return h;\r
439     }\r
440 \r
441     public double getZoomAmount()\r
442     {\r
443         Integer h = getHint(KEY_TRANSLATE_AMOUNT);\r
444         if (h==null) return DEFAULT_KEYBOARD_ZOOM_AMOUNT;\r
445         return h;\r
446     }\r
447 \r
448     public double getRotateAmount()\r
449     {\r
450         Integer h = getHint(KEY_ROTATE_AMOUNT);\r
451         if (h==null) return DEFAULT_KEYBOARD_ROTATE_AMOUNT;\r
452         return h;\r
453     }\r
454 \r
455     public double limitScaleFactor(double scaleFactor) {\r
456         Double inLimit = getHint(PanZoomRotateHandler.KEY_ZOOM_IN_LIMIT);\r
457         Double outLimit = getHint(PanZoomRotateHandler.KEY_ZOOM_OUT_LIMIT);\r
458 \r
459         if (inLimit == null && scaleFactor < 1)\r
460             return scaleFactor;\r
461         if (outLimit == null && scaleFactor > 1)\r
462             return scaleFactor;\r
463 \r
464         AffineTransform view = util.getTransform();\r
465         double currentScale = GeometryUtils.getScale(view) * 100.0;\r
466         double newScale = currentScale * scaleFactor;\r
467 \r
468         if (inLimit != null && newScale > currentScale && newScale > inLimit) {\r
469             if (currentScale < inLimit)\r
470                 scaleFactor = inLimit / currentScale;\r
471             else\r
472                 scaleFactor = 1.0;\r
473         } else if (outLimit != null && newScale < currentScale && newScale < outLimit) {\r
474             if (currentScale > outLimit)\r
475                 scaleFactor = outLimit / currentScale;\r
476             else\r
477                 scaleFactor = 1.0;\r
478         }\r
479         return scaleFactor;\r
480     }\r
481 \r
482     public static Margins getZoomToFitMargins(IHintObservable hints) {\r
483         Margins h = hints.getHint(DiagramHints.KEY_MARGINS);\r
484         if (h == null) {\r
485             Boolean b = hints.getHint(RulerPainter.KEY_RULER_ENABLED);\r
486             boolean rulerEnabled = b == null || Boolean.TRUE.equals(b);\r
487             if (rulerEnabled) {\r
488                 return PanZoomRotateHandler.DEFAULT_ZOOM_TO_FIT_MARGINS;\r
489             } else {\r
490                 return PanZoomRotateHandler.DEFAULT_ZOOM_TO_FIT_MARGINS_NO_RULER;\r
491             }\r
492         }\r
493         return h;\r
494     }\r
495     \r
496 }