/*******************************************************************************
* 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.Simantics;
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.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) {
Simantics.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(getContext(), 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;
}
}