/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.modeling.ui.diagramEditor; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.function.Consumer; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.ResourceManager; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IWorkbenchPart; import org.simantics.databoard.util.ObjectUtils; import org.simantics.db.ReadGraph; import org.simantics.db.common.procedure.adapter.ProcedureAdapter; import org.simantics.db.common.request.BinaryRead; import org.simantics.db.exception.DatabaseException; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency; import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant; import org.simantics.g2d.diagram.participant.pointertool.PointerInteractor; import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil; import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil.TerminalInfo; import org.simantics.g2d.participant.TransformUtil; import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler; import org.simantics.scenegraph.g2d.events.KeyEvent; import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent; import org.simantics.scenegraph.g2d.events.MouseEvent; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent; import org.simantics.ui.SimanticsUI; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; import org.simantics.utils.ui.ErrorLogger; import org.simantics.utils.ui.SWTUtils; import org.simantics.utils.ui.workbench.WorkbenchUtils; /** * Shows information about the terminal under the mouse in the Workbench status * line. * * @author Tuukka Lehtonen */ public class TerminalInformer extends AbstractDiagramParticipant { public static interface TerminalNamingStrategy { String getName(ReadGraph graph, TerminalInfo info) throws DatabaseException; } /** * If this hint is not set or set to false, this participant * will show terminal information in the status bar. */ public static final Key TERMINAL_INFO_DISABLED = new KeyOf(Boolean.class); @Dependency protected PointerInteractor pointerInteractor; Control control; transient Display display; IStatusLineManager statusLine; ImageDescriptor currentImage; @Dependency TransformUtil util; Point2D p1 = new Point2D.Double(); Point2D p2 = new Point2D.Double(); Rectangle2D rect = new Rectangle2D.Double(); String viewingMessage; final TerminalNamingStrategy terminalNamingStrategy; /** * @param parentControl * @param resourceManager deprecated argument, use null * @param statusLine * @param terminalNamingStrategy */ public TerminalInformer(Control parentControl, ResourceManager resourceManager, IStatusLineManager statusLine, TerminalNamingStrategy terminalNamingStrategy) { this.control = parentControl; this.statusLine = statusLine; this.terminalNamingStrategy = terminalNamingStrategy; } @Override public void removedFromContext(ICanvasContext ctx) { super.removedFromContext(ctx); final IStatusLineManager statusLine = this.statusLine; if (display != null && statusLine != null) { SWTUtils.asyncExec(display, new Runnable() { @Override public void run() { if (statusLine != null) { statusLine.setMessage(null, null); statusLine.setErrorMessage(null, null); } if (currentImage != null) { JFaceResources.getResources().destroyImage(currentImage); } } }); } } public boolean infoDisabled() { return Boolean.TRUE.equals(getHint(TERMINAL_INFO_DISABLED)); } private boolean infoEvent(MouseMovedEvent me) { return (me.stateMask & MouseEvent.CTRL_MASK) != 0; } private boolean infoEvent(KeyEvent ke) { return ke.keyCode == java.awt.event.KeyEvent.VK_CONTROL && (ke instanceof KeyPressedEvent); } @EventHandler(priority = 0) public boolean handleKeys(KeyEvent ke) { if (infoDisabled() || !infoEvent(ke)) { setMessage(null); return false; } pickRect(rect); return false; } @EventHandler(priority = 0) public boolean handleMove(MouseMovedEvent me) { double pickDist = pointerInteractor.getPickDistance(); p1.setLocation(me.controlPosition.getX() - pickDist, me.controlPosition.getY() - pickDist); p2.setLocation(me.controlPosition.getX() + pickDist, me.controlPosition.getY() + pickDist); util.controlToCanvas(p1, p1); util.controlToCanvas(p2, p2); rect.setFrameFromDiagonal(p1, p2); if (infoDisabled() || !infoEvent(me)) { setMessage(null); return false; } pickRect(rect); return false; } private void createMessage(TerminalInfo terminal, final Consumer callback) { SimanticsUI.getSession().asyncRequest(new TerminalInfoMessage(terminal, terminalNamingStrategy), new ProcedureAdapter() { @Override public void exception(Throwable t) { ErrorLogger.defaultLogError(t); } @Override public void execute(String result) { callback.accept(result); } }); } private void pickRect(Rectangle2D rect) { TerminalInfo terminal = TerminalUtil.pickTerminal(diagram, rect); if (terminal != null) { createMessage(terminal, message -> setMessage(null, message)); } else { setMessage(null); } } private void setMessage(String message) { setMessage(null, message); } private void setMessage(final ImageDescriptor imageDescriptor, String _message) { // Empty message equals no message. if (_message != null && _message.isEmpty()) _message = null; final String message = _message; if (viewingMessage == null && message == null) return; if (ObjectUtils.objectEquals(viewingMessage, message)) return; this.viewingMessage = message; SWTUtils.asyncExec(control, new Runnable() { @Override public void run() { if (control.isDisposed()) return; display = control.getDisplay(); Image img = null; ResourceManager rm = JFaceResources.getResources(); if (imageDescriptor != null) { if (imageDescriptor.equals(TerminalInformer.this.currentImage)) { img = (Image) rm.find(imageDescriptor); } else { img = (Image) rm.createImage(imageDescriptor); currentImage = imageDescriptor; } } else { if (currentImage != null) { rm.destroyImage(currentImage); currentImage = null; } } IStatusLineManager statusLine = getStatusLine(); if (statusLine != null) { statusLine.setMessage(img, message); statusLine.setErrorMessage(null); } } }); } static class TerminalInfoMessage extends BinaryRead { public TerminalInfoMessage(TerminalInfo info, TerminalNamingStrategy strategy) { super(info, strategy); } @Override public String perform(ReadGraph graph) throws DatabaseException { return parameter2.getName(graph, parameter); } } protected IStatusLineManager getStatusLine() { IWorkbenchPart activePart = WorkbenchUtils.getActiveWorkbenchPart(); if (activePart != null) { TerminalInformer.this.statusLine = statusLine = WorkbenchUtils.getStatusLine(activePart); return statusLine; } return null; } }