--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.g2d.participant;\r
+\r
+import static org.simantics.g2d.canvas.Hints.KEY_CANVAS_TRANSFORM;\r
+\r
+import java.awt.Shape;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Point2D;\r
+import java.awt.geom.Rectangle2D;\r
+import java.util.Set;\r
+\r
+import org.simantics.g2d.canvas.Hints;\r
+import org.simantics.g2d.canvas.ICanvasContext;\r
+import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;\r
+import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;\r
+import org.simantics.g2d.canvas.impl.DependencyReflection.Reference;\r
+import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;\r
+import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;\r
+import org.simantics.g2d.diagram.DiagramHints;\r
+import org.simantics.g2d.diagram.DiagramUtils;\r
+import org.simantics.g2d.diagram.IDiagram;\r
+import org.simantics.g2d.diagram.participant.Selection;\r
+import org.simantics.g2d.element.ElementUtils;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.g2d.scenegraph.SceneGraphConstants;\r
+import org.simantics.g2d.utils.GeometryUtils;\r
+import org.simantics.scenegraph.INode;\r
+import org.simantics.scenegraph.g2d.G2DParentNode;\r
+import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;\r
+import org.simantics.scenegraph.g2d.events.command.Command;\r
+import org.simantics.scenegraph.g2d.events.command.CommandEvent;\r
+import org.simantics.scenegraph.g2d.events.command.Commands;\r
+import org.simantics.scenegraph.g2d.nodes.NavigationNode;\r
+import org.simantics.scenegraph.g2d.nodes.TransformNode;\r
+import org.simantics.scenegraph.utils.NodeUtil;\r
+import org.simantics.utils.datastructures.hints.HintListenerAdapter;\r
+import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
+import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;\r
+import org.simantics.utils.datastructures.hints.IHintListener;\r
+import org.simantics.utils.datastructures.hints.IHintObservable;\r
+import org.simantics.utils.page.MarginUtils;\r
+import org.simantics.utils.page.MarginUtils.Margins;\r
+import org.simantics.utils.page.PageDesc;\r
+import org.simantics.utils.threads.ThreadUtils;\r
+\r
+/**\r
+ * This participant handles pan, zoom, zoom to fit and rotate commands.\r
+ * \r
+ * Hints:\r
+ * KEY_TRANSLATE_AMOUNT\r
+ * KEY_ZOOM_AMOUNT\r
+ * KEY_ROTATE_AMOUNT\r
+ * KEY_ZOOM_TO_FIT_MARGINS\r
+ * KEY_ZOOM_OUT_LIMIT\r
+ * KEY_ZOOM_IN_LIMIT\r
+ * \r
+ * @author Toni Kalajainen\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class PanZoomRotateHandler extends AbstractCanvasParticipant {\r
+\r
+ /**\r
+ * Express whether or not the view should attempt to keep the current zoom\r
+ * level when the canvas parenting control is resized. If the viewport is\r
+ * set to be adapted to the resized control, the view transform will be\r
+ * adjusted to accommodate for this. Otherwise the view transform will be\r
+ * left alone when the control is resized.\r
+ * \r
+ * If hint is not specified, the default value is <code>true</code>.\r
+ * \r
+ * See {@link NavigationNode} for the zoom level keep implementation.\r
+ */\r
+ public final static Key KEY_ADAPT_VIEWPORT_TO_RESIZED_CONTROL = new KeyOf(Boolean.class, "ADAPT_VIEWPORT_TO_RESIZED_CONTROL");\r
+\r
+ /**\r
+ * Limit for zooming in expressed as a percentage (100% == 1:1 == identity\r
+ * view transform). If null, there is no limit. Used with an\r
+ * ICanvasContext's hint context.\r
+ */\r
+ public final static Key KEY_ZOOM_OUT_LIMIT = new KeyOf(Double.class, "ZOOM_OUT_LIMIT");\r
+\r
+ /**\r
+ * Limit for zooming in expressed as a percentage (100% == 1:1 == identity\r
+ * view transform). If null there is no limit. Used with an\r
+ * ICanvasContext's hint context.\r
+ */\r
+ public final static Key KEY_ZOOM_IN_LIMIT = new KeyOf(Double.class, "ZOOM_IN_LIMIT");\r
+\r
+ public final static Key KEY_DISABLE_ZOOM = new KeyOf(Boolean.class, "DISABLE_ZOOM");\r
+\r
+ public final static Key KEY_DISABLE_PAN = new KeyOf(Boolean.class, "DISABLE_PAN");\r
+\r
+\r
+ @Dependency CanvasGrab grab;\r
+ @Dependency TransformUtil util;\r
+ @Dependency KeyUtil keys;\r
+ @Reference Selection selection;\r
+ @Reference CanvasBoundsParticipant bounds;\r
+\r
+ // Capture center point\r
+ Point2D centerPointControl;\r
+ Point2D centerPointCanvas;\r
+ Point2D controlSize;\r
+\r
+ final Boolean navigationEnabled;\r
+\r
+ protected NavigationNode node = null;\r
+ protected G2DParentNode oldRoot = null;\r
+\r
+ public PanZoomRotateHandler() {\r
+ this(true);\r
+ }\r
+\r
+ public PanZoomRotateHandler(boolean navigationEnabled) {\r
+ this.navigationEnabled = navigationEnabled;\r
+ }\r
+\r
+ NavigationNode.TransformListener transformListener = new NavigationNode.TransformListener() {\r
+ @Override\r
+ public void transformChanged(final AffineTransform transform) {\r
+ ThreadUtils.asyncExec(PanZoomRotateHandler.this.getContext().getThreadAccess(), new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ if (isRemoved())\r
+ return;\r
+ //System.out.println("PanZoomRotateHandler: set canvas transform: " + transform);\r
+ setHint(KEY_CANVAS_TRANSFORM, transform);\r
+ }\r
+ });\r
+ }\r
+ };\r
+\r
+ IHintListener hintListener = new HintListenerAdapter() {\r
+ @Override\r
+ public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {\r
+ if (node != null) {\r
+ if (key == Hints.KEY_DISABLE_PAINTING) {\r
+ boolean visible = !Boolean.TRUE.equals(newValue);\r
+ if (visible != node.isVisible())\r
+ node.setVisible(Boolean.valueOf(visible));\r
+ } else if (key == KEY_ADAPT_VIEWPORT_TO_RESIZED_CONTROL) {\r
+ boolean noKeepZoom = Boolean.FALSE.equals(newValue);\r
+ if (noKeepZoom == node.getAdaptViewportToResizedControl())\r
+ node.setAdaptViewportToResizedControl(Boolean.valueOf(!noKeepZoom));\r
+ } else if (key == KEY_ZOOM_OUT_LIMIT) {\r
+ node.setZoomOutLimit((Double) newValue);\r
+ } else if (key == KEY_ZOOM_IN_LIMIT) {\r
+ node.setZoomInLimit((Double) newValue);\r
+ } else if (key == KEY_DISABLE_ZOOM) {\r
+ node.setZoomEnabled(!Boolean.TRUE.equals(getHint(KEY_DISABLE_ZOOM)));\r
+ }\r
+ }\r
+ }\r
+ };\r
+\r
+ @Override\r
+ public void addedToContext(ICanvasContext ctx) {\r
+ super.addedToContext(ctx);\r
+ ctx.getDefaultHintContext().addKeyHintListener(Hints.KEY_DISABLE_PAINTING, hintListener);\r
+ ctx.getDefaultHintContext().addKeyHintListener(KEY_ADAPT_VIEWPORT_TO_RESIZED_CONTROL, hintListener);\r
+ ctx.getDefaultHintContext().addKeyHintListener(KEY_ZOOM_OUT_LIMIT, hintListener);\r
+ ctx.getDefaultHintContext().addKeyHintListener(KEY_ZOOM_IN_LIMIT, hintListener);\r
+ ctx.getDefaultHintContext().addKeyHintListener(KEY_DISABLE_ZOOM, hintListener);\r
+ ctx.getDefaultHintContext().addKeyHintListener(KEY_DISABLE_PAN, hintListener);\r
+ }\r
+\r
+ @Override\r
+ public void removedFromContext(ICanvasContext ctx) {\r
+ ctx.getDefaultHintContext().removeKeyHintListener(KEY_ZOOM_IN_LIMIT, hintListener);\r
+ ctx.getDefaultHintContext().removeKeyHintListener(KEY_ZOOM_OUT_LIMIT, hintListener);\r
+ ctx.getDefaultHintContext().removeKeyHintListener(KEY_ADAPT_VIEWPORT_TO_RESIZED_CONTROL, hintListener);\r
+ ctx.getDefaultHintContext().removeKeyHintListener(Hints.KEY_DISABLE_PAINTING, hintListener);\r
+ ctx.getDefaultHintContext().removeKeyHintListener(KEY_DISABLE_ZOOM, hintListener);\r
+ ctx.getDefaultHintContext().removeKeyHintListener(KEY_DISABLE_PAN, hintListener);\r
+ super.removedFromContext(ctx);\r
+ }\r
+\r
+ @SGInit\r
+ public void initSG(G2DParentNode parent) {\r
+ // Replace old NAVIGATION_NODE with a new one\r
+ INode oldnav = NodeUtil.getRootNode(parent).getNode(SceneGraphConstants.NAVIGATION_NODE_NAME);\r
+ if(oldnav != null) {\r
+ node = oldnav.appendParent(SceneGraphConstants.NAVIGATION_NODE_NAME, NavigationNode.class);\r
+ // FIXME : oldnav seems to be the same node as parent (most of the cases).\r
+ // Deleting it will cause plenty of code to fail, since they refer to the node directly.\r
+ // The bug was not shown, since deleting() a Node did not actually wipe its structures (until now). \r
+ // oldnav.delete();\r
+ } else {\r
+ node = parent.addNode(SceneGraphConstants.NAVIGATION_NODE_NAME, NavigationNode.class);\r
+ }\r
+ node.setLookupId(SceneGraphConstants.NAVIGATION_NODE_NAME);\r
+ node.setZIndex(0);\r
+ node.setTransformListener(transformListener);\r
+ node.setNavigationEnabled(navigationEnabled);\r
+ node.setZoomEnabled(!Boolean.TRUE.equals(getHint(KEY_DISABLE_ZOOM)));\r
+ node.setAdaptViewportToResizedControl(!Boolean.FALSE.equals(getHint(KEY_ADAPT_VIEWPORT_TO_RESIZED_CONTROL)));\r
+ Double z = getHint(KEY_ZOOM_AMOUNT);\r
+ if(z != null) {\r
+ util.setTransform(AffineTransform.getScaleInstance(z, z));\r
+ node.setTransform(AffineTransform.getScaleInstance(z, z));\r
+ }\r
+ boolean visible = !Boolean.TRUE.equals(getHint(Hints.KEY_DISABLE_PAINTING));\r
+ node.setVisible(visible);\r
+ oldRoot = getContext().getCanvasNode();\r
+ getContext().setCanvasNode(node);\r
+ }\r
+\r
+ public void update() {\r
+ if (bounds != null) {\r
+ Rectangle2D vp = bounds.getControlBounds();\r
+ controlSize = new Point2D.Double(vp.getMaxX(), vp.getMaxY());\r
+ centerPointControl = new Point2D.Double(vp.getCenterX(), vp.getCenterY());\r
+ centerPointCanvas = util.controlToCanvas(centerPointControl, null);\r
+ }\r
+ }\r
+\r
+ public TransformNode getNode() {\r
+ return node;\r
+ }\r
+\r
+ /**\r
+ * Ensures that the navigation node handled by this participant contains the\r
+ * specified transform and that {@link Hints#KEY_CANVAS_TRANSFORM} will\r
+ * contain the same value.\r
+ * \r
+ * @param transform\r
+ */\r
+ public void setTransform(AffineTransform transform) {\r
+ getNode().setTransform(transform);\r
+ transformListener.transformChanged(transform);\r
+ }\r
+\r
+ @SGCleanup\r
+ public void cleanupSG() {\r
+ node.remove();\r
+ node = null;\r
+ getContext().setCanvasNode(oldRoot);\r
+ }\r
+\r
+\r
+ /** Arrow key translate */\r
+ public final static Key KEY_TRANSLATE_AMOUNT = new KeyOf(Integer.class);\r
+ public final static Key KEY_ZOOM_AMOUNT = new KeyOf(Double.class);\r
+ public final static Key KEY_ROTATE_AMOUNT = new KeyOf(Double.class);\r
+\r
+ /** Amount of arrow key translate */\r
+ public final static int DEFAULT_KEYBOARD_TRANSLATE_AMOUNT = 30;\r
+ public final static double DEFAULT_KEYBOARD_ZOOM_AMOUNT = 1.2;\r
+ public final static double DEFAULT_KEYBOARD_ROTATE_AMOUNT = 0.1;\r
+ public final static Margins DEFAULT_ZOOM_TO_FIT_MARGINS = RulerPainter.RULER_MARINGS2;\r
+ public final static Margins DEFAULT_ZOOM_TO_FIT_MARGINS_NO_RULER = MarginUtils.MARGINS2;\r
+\r
+ public final static int ROTATE_GRAB_ID = -666;\r
+\r
+ @EventHandler(priority = 0)\r
+ public boolean handleEvent(CommandEvent e) {\r
+ assertDependencies();\r
+ update();\r
+ Command c = e.command;\r
+ boolean panDisabled = Boolean.TRUE.equals(getHint(KEY_DISABLE_PAN)) ? true : false;\r
+ boolean zoomDisabled = Boolean.TRUE.equals(getHint(KEY_DISABLE_ZOOM)) ? true : false;\r
+\r
+ // Arrow key panning\r
+ if (Commands.PAN_LEFT.equals(c) && !panDisabled) {\r
+ util.translateWithControlCoordinates(\r
+ new Point2D.Double(\r
+ getTranslateAmount(), 0));\r
+ return true;\r
+ }\r
+ if (Commands.PAN_RIGHT.equals(c) && !panDisabled) {\r
+ util.translateWithControlCoordinates(\r
+ new Point2D.Double(\r
+ -getTranslateAmount(), 0));\r
+ return true;\r
+ }\r
+ if (Commands.PAN_UP.equals(c) && !panDisabled) {\r
+ util.translateWithControlCoordinates(\r
+ new Point2D.Double(\r
+ 0, getTranslateAmount()));\r
+ return true;\r
+ }\r
+ if (Commands.PAN_DOWN.equals(c) && !panDisabled) {\r
+ util.translateWithControlCoordinates(\r
+ new Point2D.Double(0, -getTranslateAmount()));\r
+ return true;\r
+ }\r
+ if (Commands.ZOOM_IN.equals(c) && !zoomDisabled) {\r
+ if (centerPointControl == null) return false;\r
+ double scaleFactor = getZoomAmount();\r
+ scaleFactor = limitScaleFactor(scaleFactor);\r
+ util.zoomAroundControlPoint(scaleFactor, centerPointControl);\r
+ }\r
+ if (Commands.ZOOM_OUT.equals(c) && !zoomDisabled) {\r
+ if (centerPointControl == null) return false;\r
+ double scaleFactor = 1 / getZoomAmount();\r
+ scaleFactor = limitScaleFactor(scaleFactor);\r
+ util.zoomAroundControlPoint(scaleFactor, centerPointControl);\r
+ }\r
+\r
+ if (Commands.ROTATE_CANVAS_CCW.equals(c)) {\r
+ if (centerPointCanvas == null) return false;\r
+ util.rotate(centerPointCanvas, -getRotateAmount());\r
+ setDirty();\r
+ return true;\r
+ }\r
+ if (Commands.ROTATE_CANVAS_CW.equals(c)) {\r
+ if (centerPointCanvas == null) return false;\r
+ util.rotate(centerPointCanvas, getRotateAmount());\r
+ setDirty();\r
+ return true;\r
+ }\r
+ if (Commands.ROTATE_CANVAS_CCW_GRAB.equals(c)) {\r
+ if (centerPointCanvas == null) return false;\r
+ util.rotate(centerPointCanvas, -getRotateAmount());\r
+ grab.grabCanvas(ROTATE_GRAB_ID, centerPointCanvas);\r
+ grab.grabCanvas(ROTATE_GRAB_ID - 1, centerPointCanvas);\r
+ setDirty();\r
+ return true;\r
+ }\r
+ if (Commands.ROTATE_CANVAS_CW_GRAB.equals(c)) {\r
+ if (centerPointCanvas == null) return false;\r
+ util.rotate(centerPointCanvas, getRotateAmount());\r
+ grab.grabCanvas(ROTATE_GRAB_ID, centerPointCanvas);\r
+ grab.grabCanvas(ROTATE_GRAB_ID - 1, centerPointCanvas);\r
+ setDirty();\r
+ return true;\r
+ }\r
+ if (Commands.ROTATE_CANVAS_CCW_RELEASE.equals(c)) {\r
+ if (centerPointCanvas == null) return false;\r
+ grab.releaseCanvas(ROTATE_GRAB_ID);\r
+ grab.releaseCanvas(ROTATE_GRAB_ID - 1);\r
+ setDirty();\r
+ return true;\r
+ }\r
+ if (Commands.ROTATE_CANVAS_CW_RELEASE.equals(c)) {\r
+ if (centerPointCanvas == null) return false;\r
+ grab.releaseCanvas(ROTATE_GRAB_ID);\r
+ grab.releaseCanvas(ROTATE_GRAB_ID - 1);\r
+ setDirty();\r
+ return true;\r
+ }\r
+ if (Commands.ENABLE_PAINTING.equals(c)) {\r
+ Boolean t = getHint(Hints.KEY_DISABLE_PAINTING);\r
+ removeHint(Hints.KEY_DISABLE_PAINTING);\r
+ boolean processed = Boolean.TRUE.equals(t);\r
+ if (processed)\r
+ setDirty();\r
+ return processed;\r
+ }\r
+ if (Commands.ZOOM_TO_FIT.equals(c) && !zoomDisabled) {\r
+ boolean result = zoomToFit();\r
+ if (!result)\r
+ result = zoomToPage();\r
+ return result;\r
+ }\r
+ if (Commands.ZOOM_TO_SELECTION.equals(c) && !zoomDisabled && selection != null) {\r
+ if (controlSize==null) return false;\r
+ IDiagram d = getHint(DiagramHints.KEY_DIAGRAM);\r
+ if (d==null) return false;\r
+\r
+ Set<IElement> selections = selection.getAllSelections();\r
+ Shape bounds = ElementUtils.getElementBoundsOnDiagram(selections);\r
+ if (bounds == null) return false;\r
+ Rectangle2D diagramRect = bounds.getBounds2D();\r
+ if (diagramRect.getWidth() <= 0 && diagramRect.getHeight() <= 0)\r
+ return false;\r
+\r
+ // HACK: prevents straight connections from being unzoomable.\r
+ if (diagramRect.getWidth() <= 0)\r
+ org.simantics.scenegraph.utils.GeometryUtils.expandRectangle(diagramRect, 0, 0, 1, 1);\r
+ if (diagramRect.getHeight() <= 0)\r
+ org.simantics.scenegraph.utils.GeometryUtils.expandRectangle(diagramRect, 1, 1, 0, 0);\r
+\r
+ // Show area\r
+ Rectangle2D controlArea = new Rectangle2D.Double(0, 0, controlSize.getX(), controlSize.getY());\r
+ util.fitArea(controlArea, diagramRect, getZoomToFitMargins(getHintStack()));\r
+ return true;\r
+ }\r
+ if (Commands.ZOOM_TO_PAGE.equals(c) && !zoomDisabled) {\r
+ return zoomToPage();\r
+ }\r
+\r
+ return false;\r
+ }\r
+\r
+ private boolean zoomToFit() {\r
+ if (controlSize==null) return false;\r
+ IDiagram d = getHint(DiagramHints.KEY_DIAGRAM);\r
+ if (d==null) return false;\r
+\r
+ Rectangle2D diagramRect = DiagramUtils.getContentRect(d);\r
+ if (diagramRect==null) return false;\r
+ if (diagramRect.isEmpty())\r
+ return false;\r
+\r
+ // Show area\r
+ Rectangle2D controlArea = new Rectangle2D.Double(0, 0, controlSize.getX(), controlSize.getY());\r
+ //System.out.println("zoomToFit(" + controlArea + ", " + diagramRect + ")");\r
+ util.fitArea(controlArea, diagramRect, getZoomToFitMargins(getHintStack()));\r
+\r
+ return true;\r
+ }\r
+\r
+ private boolean zoomToPage() {\r
+ if (controlSize==null) return false;\r
+ PageDesc desc = getHint(Hints.KEY_PAGE_DESC);\r
+ if (desc == null)\r
+ return false;\r
+ if (desc.isInfinite())\r
+ return false;\r
+\r
+ // Show page\r
+ Rectangle2D diagramRect = new Rectangle2D.Double();\r
+ desc.getPageRectangle(diagramRect);\r
+ if (diagramRect.isEmpty())\r
+ return false;\r
+\r
+ Rectangle2D controlArea = new Rectangle2D.Double(0, 0, controlSize.getX(), controlSize.getY());\r
+ //System.out.println("zoomToPage(" + controlArea + ", " + diagramRect + ")");\r
+ util.fitArea(controlArea, diagramRect, getZoomToFitMargins(getHintStack()));\r
+ return true;\r
+ }\r
+\r
+ public double getTranslateAmount()\r
+ {\r
+ Integer h = getHint(KEY_TRANSLATE_AMOUNT);\r
+ if (h==null) return DEFAULT_KEYBOARD_TRANSLATE_AMOUNT;\r
+ return h;\r
+ }\r
+\r
+ public double getZoomAmount()\r
+ {\r
+ Integer h = getHint(KEY_TRANSLATE_AMOUNT);\r
+ if (h==null) return DEFAULT_KEYBOARD_ZOOM_AMOUNT;\r
+ return h;\r
+ }\r
+\r
+ public double getRotateAmount()\r
+ {\r
+ Integer h = getHint(KEY_ROTATE_AMOUNT);\r
+ if (h==null) return DEFAULT_KEYBOARD_ROTATE_AMOUNT;\r
+ return h;\r
+ }\r
+\r
+ public double limitScaleFactor(double scaleFactor) {\r
+ Double inLimit = getHint(PanZoomRotateHandler.KEY_ZOOM_IN_LIMIT);\r
+ Double outLimit = getHint(PanZoomRotateHandler.KEY_ZOOM_OUT_LIMIT);\r
+\r
+ if (inLimit == null && scaleFactor < 1)\r
+ return scaleFactor;\r
+ if (outLimit == null && scaleFactor > 1)\r
+ return scaleFactor;\r
+\r
+ AffineTransform view = util.getTransform();\r
+ double currentScale = GeometryUtils.getScale(view) * 100.0;\r
+ double newScale = currentScale * scaleFactor;\r
+\r
+ if (inLimit != null && newScale > currentScale && newScale > inLimit) {\r
+ if (currentScale < inLimit)\r
+ scaleFactor = inLimit / currentScale;\r
+ else\r
+ scaleFactor = 1.0;\r
+ } else if (outLimit != null && newScale < currentScale && newScale < outLimit) {\r
+ if (currentScale > outLimit)\r
+ scaleFactor = outLimit / currentScale;\r
+ else\r
+ scaleFactor = 1.0;\r
+ }\r
+ return scaleFactor;\r
+ }\r
+\r
+ public static Margins getZoomToFitMargins(IHintObservable hints) {\r
+ Margins h = hints.getHint(DiagramHints.KEY_MARGINS);\r
+ if (h == null) {\r
+ Boolean b = hints.getHint(RulerPainter.KEY_RULER_ENABLED);\r
+ boolean rulerEnabled = b == null || Boolean.TRUE.equals(b);\r
+ if (rulerEnabled) {\r
+ return PanZoomRotateHandler.DEFAULT_ZOOM_TO_FIT_MARGINS;\r
+ } else {\r
+ return PanZoomRotateHandler.DEFAULT_ZOOM_TO_FIT_MARGINS_NO_RULER;\r
+ }\r
+ }\r
+ return h;\r
+ }\r
+ \r
+}
\ No newline at end of file