/* ****************************************************************************** * Copyright (c) 2006-2012 XMind Ltd. and others. * * This file is a part of XMind 3. XMind releases 3 and * above are dual-licensed under the Eclipse Public License (EPL), * which is available at http://www.eclipse.org/legal/epl-v10.html * and the GNU Lesser General Public License (LGPL), * which is available at http://www.gnu.org/licenses/lgpl.html * See http://www.xmind.net/license.html for details. * * Contributors: * XMind Ltd. - initial API and implementation *******************************************************************************/ package org.xmind.gef.ui.editor; import org.eclipse.draw2d.FreeformFigure; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.SWTGraphics; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.jface.util.Util; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CTabFolder; import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.xmind.gef.IGraphicalViewer; import org.xmind.gef.draw2d.IOriginBased; import org.xmind.gef.draw2d.geometry.Geometry; import org.xmind.gef.draw2d.graphics.ScaledGraphics; import org.xmind.gef.part.IGraphicalPart; import org.xmind.gef.part.IPart; public class GraphicalEditorPagePopupPreviewHelper { private static final int SHELL_MAX_WIDTH = 200; private static final int SHELL_MAX_HEIGHT = 200; private static final int CONTENTS_MAX_WIDTH = 1000; private static final int CONTENTS_MAX_HEIGHT = 1000; private static final int SPACING = 5; private final IGraphicalEditor editor; private CTabFolder tabFolder; private int index = -1; private IFigure paintingContents = null; private IFigure boundsContents = null; private Rectangle bounds = null; private Shell popup = null; private int borderWidth = 0; public GraphicalEditorPagePopupPreviewHelper(IGraphicalEditor editor, CTabFolder tabFolder) { this.editor = editor; this.tabFolder = tabFolder; hookTabFolder(); } public int getBorderWidth() { return borderWidth; } public void setBorderWidth(int borderWidth) { this.borderWidth = borderWidth; } protected int getAppliedBorderWidth() { if (Util.isMac()) return getBorderWidth() + 1; return getBorderWidth(); } private void hookTabFolder() { Listener listener = new Listener() { public void handleEvent(Event event) { switch (event.type) { case SWT.MouseHover: showPopup(event); break; case SWT.MouseMove: checkPopup(event); break; case SWT.MouseExit: case SWT.MouseEnter: case SWT.MouseDown: case SWT.MouseUp: case SWT.MouseDoubleClick: case SWT.MouseWheel: case SWT.Dispose: case SWT.Deactivate: case SWT.FocusOut: hidePopup(); break; } } }; tabFolder.addListener(SWT.MouseHover, listener); tabFolder.addListener(SWT.MouseMove, listener); tabFolder.addListener(SWT.MouseEnter, listener); tabFolder.addListener(SWT.MouseExit, listener); tabFolder.addListener(SWT.MouseDown, listener); tabFolder.addListener(SWT.MouseDoubleClick, listener); tabFolder.addListener(SWT.MouseUp, listener); tabFolder.addListener(SWT.MouseWheel, listener); tabFolder.addListener(SWT.Dispose, listener); tabFolder.addListener(SWT.FocusOut, listener); tabFolder.getShell().addListener(SWT.Deactivate, listener); } private void showPopup(Event e) { if (popup != null) return; CTabItem item = tabFolder.getItem(new Point(e.x, e.y)); if (item != null) { int index = tabFolder.indexOf(item); if (index >= 0) { IGraphicalEditorPage page = editor.getPage(index); if (page != null && !page.isDisposed()) { IGraphicalViewer viewer = page.getViewer(); if (viewer != null && !viewer.getControl().isDisposed()) { createPopup(item, viewer); this.index = index; } } } } } private void createPopup(CTabItem item, IGraphicalViewer viewer) { this.paintingContents = getPaintingContents(viewer); this.boundsContents = getBoundsContents(paintingContents, viewer); if (paintingContents != null) { this.bounds = calcContentsBounds(boundsContents, viewer); createPopup(item); if (popup != null && !popup.isDisposed()) popup.setVisible(true); } } protected IFigure getPaintingContents(IGraphicalViewer viewer) { IFigure contents = viewer.getCanvas().getViewport().getContents(); if (contents != null) return contents; return viewer.getCanvas().getLightweightSystem().getRootFigure(); } protected IFigure getBoundsContents(IFigure paintingContents, IGraphicalViewer viewer) { IPart contentsPart = viewer.getRootPart().getContents(); if (contentsPart instanceof IGraphicalPart && contentsPart.getStatus().isActive()) { return ((IGraphicalPart) contentsPart).getFigure(); } return paintingContents; } protected Rectangle calcContentsBounds(IFigure contents, IGraphicalViewer viewer) { Rectangle bounds = getBaseBounds(contents); int width = Math.min(bounds.width, CONTENTS_MAX_WIDTH); int height = Math.min(bounds.height, CONTENTS_MAX_HEIGHT); org.eclipse.draw2d.geometry.Point origin = getOrigin(contents, viewer); if (origin != null) { int x = origin.x - width / 2; int y = origin.y - height / 2; bounds.x = Math.max(bounds.x, Math.min(bounds.x + bounds.width - width, x)); bounds.y = Math.max(bounds.y, Math.min(bounds.y + bounds.height - height, y)); } else { bounds.x = -width / 2; bounds.y = -height / 2; } bounds.width = width; bounds.height = height; return bounds; } protected Rectangle getBaseBounds(IFigure contents) { if (contents instanceof FreeformFigure) return ((FreeformFigure) contents).getFreeformExtent().getCopy(); return contents.getBounds().getCopy(); } protected org.eclipse.draw2d.geometry.Point getOrigin(IFigure contents, IGraphicalViewer viewer) { if (contents instanceof IOriginBased) { return ((IOriginBased) contents).getOrigin(); } return null; } private void createPopup(CTabItem item) { this.popup = new Shell(editor.getSite().getShell(), getPopupShellStyle()); hookPopup(popup); configurePopup(popup, item); } protected int getPopupShellStyle() { int style = SWT.NO_FOCUS | SWT.ON_TOP | SWT.NO_BACKGROUND; if (Util.isMac()) { style |= SWT.NO_TRIM; } else { style |= SWT.RESIZE; } return style; } private void hookPopup(Shell popup) { Listener listener = new Listener() { public void handleEvent(Event event) { switch (event.type) { case SWT.Paint: paintPopup(event); break; case SWT.MouseEnter: case SWT.MouseExit: case SWT.MouseMove: case SWT.MouseDown: case SWT.MouseUp: case SWT.MouseWheel: case SWT.MouseHover: case SWT.Activate: case SWT.FocusIn: case SWT.KeyDown: case SWT.KeyUp: hidePopup(); break; } } }; popup.addListener(SWT.Paint, listener); popup.addListener(SWT.MouseEnter, listener); popup.addListener(SWT.MouseExit, listener); popup.addListener(SWT.MouseMove, listener); popup.addListener(SWT.MouseDown, listener); popup.addListener(SWT.MouseUp, listener); popup.addListener(SWT.MouseWheel, listener); popup.addListener(SWT.MouseHover, listener); popup.addListener(SWT.Activate, listener); popup.addListener(SWT.FocusIn, listener); popup.addListener(SWT.KeyDown, listener); popup.addListener(SWT.KeyUp, listener); } protected void configurePopup(Shell popup, CTabItem item) { int doubleBorderWidth = getAppliedBorderWidth() * 2; int maxWidth = SHELL_MAX_WIDTH - doubleBorderWidth; int maxHeight = SHELL_MAX_HEIGHT - doubleBorderWidth; Dimension size = Geometry.getScaledConstrainedSize(bounds.width, bounds.height, maxWidth, maxHeight); size.expand(doubleBorderWidth, doubleBorderWidth); org.eclipse.swt.graphics.Rectangle trim = popup.computeTrim(0, 0, size.width, size.height); int width = trim.width; int height = trim.height; popup.setSize(width, height); org.eclipse.swt.graphics.Rectangle itemBounds = item.getBounds(); Point leftTop = tabFolder.toDisplay(itemBounds.x, itemBounds.y); org.eclipse.swt.graphics.Rectangle displayArea = Display.getCurrent() .getClientArea(); int x = leftTop.x + itemBounds.width / 2 - width / 2; if (x + width > displayArea.x + displayArea.width) { x = displayArea.x + displayArea.width - width; } if (x < displayArea.x) { x = displayArea.x; } int y; if (tabFolder.getTabPosition() == SWT.BOTTOM) { y = leftTop.y - SPACING - height; if (y < displayArea.y) { y = displayArea.y; } } else { y = leftTop.y + itemBounds.height + SPACING; if (y + height > displayArea.y + displayArea.height) { y = displayArea.y + displayArea.height - height; } } popup.setLocation(x, y); } protected void paintPopup(Event e) { org.eclipse.swt.graphics.Rectangle clientArea = popup.getClientArea(); int border = getAppliedBorderWidth(); int x = clientArea.x + border; int y = clientArea.y + border; double w = clientArea.width - border - border; double h = clientArea.height - border - border; GC gc = e.gc; SWTGraphics swtGraphics = new SWTGraphics(gc); swtGraphics.pushState(); double horizontalScale = w / bounds.width; double verticalScale = h / bounds.height; double scale = Math.max(horizontalScale, verticalScale); swtGraphics.translate(x, y); Graphics g = swtGraphics; ScaledGraphics sg = null; if (ScaledGraphics.SCALED_GRAPHICS_ENABLED) { sg = new ScaledGraphics(swtGraphics); sg.scale(scale); g = sg; } else { g.scale(scale); } g.translate(-bounds.x, -bounds.y); try { paintFigure(paintingContents, g); } finally { if (sg != null) { sg.dispose(); } swtGraphics.popState(); swtGraphics.dispose(); } if (border > 0) { gc.setForeground(e.display.getSystemColor(SWT.COLOR_GRAY)); gc.setAlpha(0xff); gc.setLineWidth(border); gc.setLineStyle(SWT.LINE_SOLID); gc.setClipping(clientArea.x, clientArea.y, clientArea.width, clientArea.height); gc.drawRectangle(clientArea.x + border / 2, clientArea.y + border / 2, clientArea.width - border, clientArea.height - border); } } protected void paintFigure(IFigure figure, Graphics graphics) { figure.paint(graphics); } private void hidePopup() { if (popup != null) { popup.dispose(); popup = null; } paintingContents = null; boundsContents = null; bounds = null; index = -1; } private void checkPopup(Event e) { CTabItem item = tabFolder.getItem(new Point(e.x, e.y)); if (item == null) { hidePopup(); return; } int index = tabFolder.indexOf(item); if (index < 0) { hidePopup(); return; } else if (index != this.index) { boolean showing = popup != null; hidePopup(); if (showing) showPopup(e); return; } IGraphicalEditorPage page = editor.getPage(index); if (page == null || page.isDisposed()) { hidePopup(); return; } IGraphicalViewer viewer = page.getViewer(); if (viewer == null || viewer.getControl().isDisposed()) { hidePopup(); return; } } }