package org.xmind.ui.internal.views; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import org.eclipse.draw2d.ColorConstants; import org.eclipse.draw2d.Cursors; import org.eclipse.draw2d.Figure; import org.eclipse.draw2d.FigureCanvas; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.LayoutListener; import org.eclipse.draw2d.RangeModel; import org.eclipse.draw2d.RectangleFigure; import org.eclipse.draw2d.Viewport; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Insets; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.ui.part.Page; import org.xmind.gef.GEF; import org.xmind.gef.IGraphicalViewer; import org.xmind.gef.IInputChangedListener; import org.xmind.gef.IViewer; import org.xmind.gef.IZoomListener; import org.xmind.gef.ZoomManager; import org.xmind.gef.ZoomObject; import org.xmind.gef.draw2d.geometry.PrecisionDimension; import org.xmind.gef.draw2d.geometry.PrecisionPoint; import org.xmind.gef.draw2d.graphics.ScaledGraphics; import org.xmind.gef.ui.editor.IGraphicalEditorPage; import org.xmind.ui.resources.ColorUtils; public class SheetOverviewPage extends Page implements ISelectionChangedListener, IInputChangedListener, PropertyChangeListener, IZoomListener, Listener { private class ContentsFigure extends Figure { public ContentsFigure() { setBackgroundColor(ColorUtils.getColor("#f3f4f9")); //$NON-NLS-1$ setOpaque(true); } @Override protected void paintFigure(Graphics graphics) { super.paintFigure(graphics); if (sourceContents == null || zoomScale <= 0) return; graphics.setAntialias(SWT.ON); graphics.pushState(); try { Point offset = getBounds().getLocation(); graphics.translate(offset); Graphics g = graphics; ScaledGraphics sg = null; if (ScaledGraphics.SCALED_GRAPHICS_ENABLED) { sg = new ScaledGraphics(graphics); sg.scale(zoomScale); g = sg; } else { g.scale(zoomScale); } try { paintDelegate(g, sourceContents); } finally { if (sg != null) { sg.dispose(); } } } finally { graphics.popState(); } Rectangle area = getClientArea(); graphics.setLineWidth(1); graphics.setLineStyle(SWT.LINE_SOLID); graphics.setForegroundColor(ColorConstants.darkGray); graphics.drawRectangle(area.x, area.y, area.width - 1, area.height - 1); } private void paintDelegate(Graphics graphics, IFigure figure) { Point loc = figure.getBounds().getLocation(); graphics.translate(-loc.x, -loc.y); try { figure.paint(graphics); } finally { graphics.translate(loc.x, loc.y); } } } private class ContentsLayoutListener extends LayoutListener.Stub { @Override public void postLayout(IFigure container) { update(); } } private IGraphicalEditorPage sourcePage; private IGraphicalViewer sourceViewer; private RangeModel sourceHorizontalRangeModel; private RangeModel sourceVerticalRangeModel; private ZoomManager sourceZoomManager; private IFigure sourceContents; // private IFigure sourceBackground; private FigureCanvas canvas; private IFigure contents; private IFigure feedback; private boolean updating = false; private ContentsLayoutListener contentsListener; private Point moveStart = null; private Point sourceStart = null; private double zoomScale = 1.0d; public SheetOverviewPage(IGraphicalEditorPage sourcePage) { this.sourcePage = sourcePage; } @Override public void createControl(Composite parent) { canvas = new FigureCanvas(parent); canvas.addListener(SWT.Resize, this); canvas.addListener(SWT.MouseDown, this); canvas.addListener(SWT.MouseMove, this); canvas.addListener(SWT.MouseUp, this); canvas.addListener(SWT.MouseWheel, this); contents = new ContentsFigure(); contents.setCursor(Cursors.HAND); canvas.setContents(contents); feedback = createFeedback(); contents.add(feedback); sourceViewer = sourcePage.getViewer(); sourceZoomManager = sourceViewer.getZoomManager(); sourceViewer.addInputChangedListener(this); sourceViewer.addSelectionChangedListener(this); sourceZoomManager.addZoomListener(this); hookViewport(); hookContents(); update(); } public void dispose() { unhookContents(); unhookViewport(); sourceZoomManager.removeZoomListener(this); sourceViewer.removeSelectionChangedListener(this); sourceViewer.removeInputChangedListener(this); super.dispose(); } @Override public Control getControl() { return canvas; } @Override public void setFocus() { canvas.setFocus(); } public void selectionChanged(SelectionChangedEvent event) { update(); } public void inputChanged(IViewer viewer, Object newInput, Object oldInput) { unhookContents(); unhookViewport(); hookViewport(); hookContents(); update(); } public void propertyChange(PropertyChangeEvent evt) { update(); } public void scaleChanged(ZoomObject source, double oldValue, double newValue) { update(); } public void handleEvent(Event event) { if (event.type == SWT.MouseDown) { moveStarted(event.x, event.y); } else if (event.type == SWT.MouseMove) { if (moveStart != null) { feedbackMoved(event.x, event.y); } } else if (event.type == SWT.MouseUp) { moveEnded(event.x, event.y); } else if (event.type == SWT.MouseWheel) { changeZoom(event.count); } else if (event.type == SWT.Resize) { update(); } } private void moveStarted(int x, int y) { moveStart = new Point(x, y); sourceStart = new Point(sourceViewer.getScrollPosition()); } private void update() { if (updating) return; updating = true; Display.getCurrent().asyncExec(new Runnable() { public void run() { doUpdate(); updating = false; } }); } private void doUpdate() { Insets margins; Rectangle feedbackBounds; Rectangle sourceBounds = sourceContents.getBounds(); Dimension source = sourceBounds.getSize(); Rectangle area = contents.getParent().getClientArea(); if (area.width == 0 || area.height == 0 || source.width == 0 || source.height == 0) { zoomScale = -1; margins = IFigure.NO_INSETS; feedbackBounds = null; } else { double wScale = source.width * 1.0d / area.width; double hScale = source.height * 1.0d / area.height; if (wScale > hScale) { zoomScale = 1 / wScale; int m = (int) ((area.height - source.height / wScale) / 2); margins = new Insets(m, 0, m, 0); } else { zoomScale = 1 / hScale; int m = (int) ((area.width - source.width / hScale) / 2); margins = new Insets(0, m, 0, m); } Viewport sourceViewport = sourceViewer.getCanvas().getViewport(); PrecisionPoint loc = new PrecisionPoint( sourceViewport.getViewLocation()); Dimension size = sourceViewport.getSize(); double sourceScale = sourceZoomManager.getScale(); feedbackBounds = new Rectangle( loc.scale(1 / sourceScale) .translate(new PrecisionPoint( sourceBounds.getLocation()).negate()) .scale(zoomScale).translate(margins.left, margins.top) .toDraw2DPoint(), size.scale(zoomScale / sourceScale)); } contents.setBounds(area.getShrinked(margins)); contents.repaint(); if (feedbackBounds == null) { feedback.setBounds(new Rectangle(1, 1, 0, 0)); feedback.setVisible(false); } else { feedback.setBounds(feedbackBounds); feedback.setVisible(true); } } private void hookViewport() { Viewport sourceViewport = sourceViewer.getCanvas().getViewport(); sourceHorizontalRangeModel = sourceViewport.getHorizontalRangeModel(); sourceHorizontalRangeModel.addPropertyChangeListener(this); sourceVerticalRangeModel = sourceViewport.getVerticalRangeModel(); sourceVerticalRangeModel.addPropertyChangeListener(this); } private void unhookViewport() { if (sourceHorizontalRangeModel != null) { sourceHorizontalRangeModel.removePropertyChangeListener(this); sourceHorizontalRangeModel = null; } if (sourceVerticalRangeModel != null) { sourceVerticalRangeModel.removePropertyChangeListener(this); sourceVerticalRangeModel = null; } } private void hookContents() { if (contentsListener == null) contentsListener = new ContentsLayoutListener(); sourceContents = sourceViewer.getLayer(GEF.LAYER_CONTENTS); sourceContents.addLayoutListener(contentsListener); // sourceBackground = sourceViewer.getLayer(GEF.LAYER_BACKGROUND); // sourceBackground.addLayoutListener(contentsListener); } private void unhookContents() { if (contentsListener != null) { if (sourceContents != null) { sourceContents.removeLayoutListener(contentsListener); } // if (sourceBackground != null) { // sourceBackground.removeLayoutListener(contentsListener); // } } } private IFigure createFeedback() { RectangleFigure figure = new RectangleFigure(); figure.setForegroundColor(ColorConstants.red); figure.setLineWidth(2); figure.setFill(false); figure.setOutline(true); return figure; } private void moveEnded(int x, int y) { if (moveStart != null) { if (moveStart.x == x && moveStart.y == y) { directMove(x, y); } } moveStart = null; sourceStart = null; } private void directMove(int x, int y) { Point start = feedback.getBounds().getCenter(); Dimension offset = new PrecisionDimension(x - start.x, y - start.y) .scale(sourceZoomManager.getScale() / zoomScale) .toDraw2DDimension(); sourceViewer.scrollDelta(offset); } private void feedbackMoved(int x, int y) { int dx = x - moveStart.x; int dy = y - moveStart.y; Dimension offset = new PrecisionDimension(dx, dy) .scale(sourceZoomManager.getScale() / zoomScale) .toDraw2DDimension(); sourceViewer.scrollTo(sourceStart.getTranslated(offset)); } private void changeZoom(int value) { if (value > 0) { sourceZoomManager.zoomIn(); } else if (value < 0) { sourceZoomManager.zoomOut(); } } }