2015-11-06 19:29:36 +01:00
|
|
|
/*******************************************************************************
|
2021-01-09 14:26:49 +01:00
|
|
|
* Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
|
2015-11-06 19:29:36 +01:00
|
|
|
* 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:
|
|
|
|
* MINRES Technologies GmbH - initial API and implementation
|
|
|
|
*******************************************************************************/
|
2020-07-15 21:42:10 +02:00
|
|
|
package com.minres.scviewer.database.ui.swt.internal;
|
2015-11-06 19:29:36 +01:00
|
|
|
|
2021-01-14 23:14:22 +01:00
|
|
|
import java.util.Arrays;
|
2015-11-06 19:29:36 +01:00
|
|
|
import java.util.Collection;
|
2015-11-13 23:44:44 +01:00
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
2021-01-14 23:14:22 +01:00
|
|
|
import java.util.Optional;
|
2015-11-06 19:29:36 +01:00
|
|
|
|
2015-11-13 23:44:44 +01:00
|
|
|
import org.eclipse.swt.SWT;
|
|
|
|
import org.eclipse.swt.graphics.Color;
|
|
|
|
import org.eclipse.swt.graphics.Path;
|
|
|
|
import org.eclipse.swt.graphics.Point;
|
2015-11-06 19:29:36 +01:00
|
|
|
import org.eclipse.swt.graphics.Rectangle;
|
2015-11-13 23:44:44 +01:00
|
|
|
import org.eclipse.swt.widgets.Display;
|
2015-11-06 19:29:36 +01:00
|
|
|
|
2021-01-14 23:14:22 +01:00
|
|
|
import com.minres.scviewer.database.IEvent;
|
|
|
|
import com.minres.scviewer.database.IHierNode;
|
|
|
|
import com.minres.scviewer.database.IWaveform;
|
2015-11-15 22:15:37 +01:00
|
|
|
import com.minres.scviewer.database.RelationType;
|
2020-11-28 14:47:43 +01:00
|
|
|
import com.minres.scviewer.database.tx.ITx;
|
2021-01-14 23:14:22 +01:00
|
|
|
import com.minres.scviewer.database.tx.ITxEvent;
|
|
|
|
import com.minres.scviewer.database.tx.ITxGenerator;
|
2020-11-28 14:47:43 +01:00
|
|
|
import com.minres.scviewer.database.tx.ITxRelation;
|
2015-11-13 23:44:44 +01:00
|
|
|
import com.minres.scviewer.database.ui.WaveformColors;
|
2015-11-06 19:29:36 +01:00
|
|
|
|
|
|
|
public class ArrowPainter implements IPainter {
|
|
|
|
|
2020-11-29 12:42:51 +01:00
|
|
|
private static final float X_CTRL_OFFSET = 50;
|
2015-11-15 22:15:37 +01:00
|
|
|
|
2020-11-28 19:41:00 +01:00
|
|
|
private int yCtrlOffset = 30;
|
2015-11-13 23:44:44 +01:00
|
|
|
|
|
|
|
private WaveformCanvas waveCanvas;
|
|
|
|
|
2015-11-06 19:29:36 +01:00
|
|
|
private ITx tx;
|
|
|
|
|
2015-11-15 22:15:37 +01:00
|
|
|
private List<LinkEntry> iRect;
|
2015-11-13 23:44:44 +01:00
|
|
|
|
2015-11-15 22:15:37 +01:00
|
|
|
private List<LinkEntry> oRect;
|
2015-11-06 19:29:36 +01:00
|
|
|
|
2015-11-13 23:44:44 +01:00
|
|
|
private Rectangle txRectangle;
|
2015-11-15 22:15:37 +01:00
|
|
|
|
|
|
|
private RelationType highlightType;
|
|
|
|
|
2019-03-17 20:03:22 +01:00
|
|
|
private long selectionOffset;
|
|
|
|
|
2015-11-13 23:44:44 +01:00
|
|
|
long scaleFactor;
|
2015-11-15 22:15:37 +01:00
|
|
|
|
2020-10-17 13:54:34 +02:00
|
|
|
boolean deferUpdate;
|
2015-11-06 19:29:36 +01:00
|
|
|
|
2015-11-15 22:15:37 +01:00
|
|
|
public ArrowPainter(WaveformCanvas waveCanvas, RelationType relationType) {
|
2015-11-06 19:29:36 +01:00
|
|
|
this.waveCanvas = waveCanvas;
|
2015-11-15 22:15:37 +01:00
|
|
|
highlightType=relationType;
|
2015-11-13 23:44:44 +01:00
|
|
|
setTx(null);
|
|
|
|
}
|
2015-11-06 19:29:36 +01:00
|
|
|
|
2015-11-15 22:15:37 +01:00
|
|
|
public RelationType getHighlightType() {
|
|
|
|
return highlightType;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setHighlightType(RelationType highlightType) {
|
|
|
|
this.highlightType = highlightType;
|
|
|
|
}
|
|
|
|
|
2015-11-13 23:44:44 +01:00
|
|
|
public ITx getTx() {
|
|
|
|
return tx;
|
2015-11-06 19:29:36 +01:00
|
|
|
}
|
2015-11-13 23:44:44 +01:00
|
|
|
|
|
|
|
public void setTx(ITx newTx) {
|
|
|
|
this.tx = newTx;
|
2015-11-15 22:15:37 +01:00
|
|
|
iRect = new LinkedList<>();
|
|
|
|
oRect = new LinkedList<>();
|
|
|
|
scaleFactor = waveCanvas.getScaleFactor();
|
|
|
|
if (tx != null) {
|
2015-11-13 23:44:44 +01:00
|
|
|
calculateGeometries();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-14 23:14:22 +01:00
|
|
|
private int getConcurrencyIndex(ITx tx) {
|
|
|
|
IEvent[] eventList = tx.getStream().getEvents().floorEntry(tx.getBeginTime()).getValue();
|
|
|
|
Optional<Integer> res = Arrays.stream(eventList).map(e -> ((ITxEvent)e).getRowIndex()).findFirst();
|
|
|
|
return res.isPresent()? res.get():0;
|
|
|
|
}
|
|
|
|
|
2015-11-13 23:44:44 +01:00
|
|
|
protected void calculateGeometries() {
|
2020-10-17 13:54:34 +02:00
|
|
|
deferUpdate = false;
|
|
|
|
iRect.clear();
|
|
|
|
oRect.clear();
|
2020-11-28 14:08:34 +01:00
|
|
|
IWaveformPainter painter = waveCanvas.wave2painterMap.get(tx.getStream());
|
2015-11-15 22:15:37 +01:00
|
|
|
if (painter == null) { // stream has been added but painter not yet
|
|
|
|
// created
|
2020-10-17 13:54:34 +02:00
|
|
|
deferUpdate = true;
|
2015-11-13 23:44:44 +01:00
|
|
|
return;
|
|
|
|
}
|
2021-01-14 23:14:22 +01:00
|
|
|
int laneHeight = painter.getHeight() / tx.getStream().getRowCount();
|
2020-06-20 17:58:26 +02:00
|
|
|
txRectangle = new Rectangle((int) (tx.getBeginTime() / scaleFactor),
|
2021-01-14 23:14:22 +01:00
|
|
|
waveCanvas.rulerHeight + painter.getVerticalOffset() + laneHeight * getConcurrencyIndex(tx),
|
2015-11-15 22:15:37 +01:00
|
|
|
(int) ((tx.getEndTime() - tx.getBeginTime()) / scaleFactor), laneHeight);
|
2015-11-13 23:44:44 +01:00
|
|
|
deriveGeom(tx.getIncomingRelations(), iRect, false);
|
|
|
|
deriveGeom(tx.getOutgoingRelations(), oRect, true);
|
|
|
|
}
|
|
|
|
|
2015-11-15 22:15:37 +01:00
|
|
|
protected void deriveGeom(Collection<ITxRelation> relations, List<LinkEntry> res, boolean useTarget) {
|
|
|
|
for (ITxRelation iTxRelation : relations) {
|
|
|
|
ITx otherTx = useTarget ? iTxRelation.getTarget() : iTxRelation.getSource();
|
2021-01-14 23:14:22 +01:00
|
|
|
Rectangle bb = createLinkEntry(otherTx, otherTx.getStream());
|
|
|
|
if(bb!=null){
|
2015-11-15 22:15:37 +01:00
|
|
|
res.add(new LinkEntry(bb, iTxRelation.getRelationType()));
|
2021-01-14 23:14:22 +01:00
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
for(IHierNode gen:otherTx.getStream().getChildNodes()) {
|
|
|
|
if(gen instanceof ITxGenerator) {
|
|
|
|
bb = createLinkEntry(otherTx, (IWaveform) gen);
|
|
|
|
if(bb!=null){
|
|
|
|
res.add(new LinkEntry(bb, iTxRelation.getRelationType()));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-11-13 23:44:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-14 23:14:22 +01:00
|
|
|
private Rectangle createLinkEntry(ITx otherTx, IWaveform iWaveform) {
|
|
|
|
if (waveCanvas.wave2painterMap.containsKey(iWaveform)) {
|
|
|
|
IWaveformPainter painter = waveCanvas.wave2painterMap.get(otherTx.getStream());
|
|
|
|
if(painter==null) {
|
|
|
|
for(IHierNode gen:otherTx.getStream().getChildNodes()) {
|
|
|
|
if(gen instanceof ITxGenerator) {
|
|
|
|
painter = waveCanvas.wave2painterMap.get(gen);
|
|
|
|
if(painter!=null)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-14 23:36:07 +01:00
|
|
|
if(painter!=null) {
|
|
|
|
int height = waveCanvas.styleProvider.getTrackHeight();
|
|
|
|
return new Rectangle(
|
|
|
|
(int) (otherTx.getBeginTime() / scaleFactor),
|
|
|
|
waveCanvas.rulerHeight + painter.getVerticalOffset() + height * getConcurrencyIndex(otherTx),
|
|
|
|
(int) ((otherTx.getEndTime() - otherTx.getBeginTime()) / scaleFactor),
|
|
|
|
height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
2021-01-14 23:14:22 +01:00
|
|
|
}
|
|
|
|
|
2015-11-06 19:29:36 +01:00
|
|
|
@Override
|
2020-06-20 17:58:26 +02:00
|
|
|
public void paintArea(Projection proj, Rectangle clientRect) {
|
2020-11-28 19:41:00 +01:00
|
|
|
yCtrlOffset = waveCanvas.styleProvider.getTrackHeight()/2;
|
|
|
|
Color fgColor = waveCanvas.styleProvider.getColor(WaveformColors.REL_ARROW);
|
|
|
|
Color highliteColor = waveCanvas.styleProvider.getColor(WaveformColors.REL_ARROW_HIGHLITE);
|
2015-11-15 22:15:37 +01:00
|
|
|
|
2020-10-17 13:54:34 +02:00
|
|
|
if(tx==null) return;
|
|
|
|
if (!deferUpdate) {
|
2015-11-15 22:15:37 +01:00
|
|
|
scaleFactor = waveCanvas.getScaleFactor();
|
2015-11-13 23:44:44 +01:00
|
|
|
calculateGeometries();
|
|
|
|
}
|
2020-10-17 13:54:34 +02:00
|
|
|
if(deferUpdate) return;
|
2020-06-20 17:58:26 +02:00
|
|
|
int correctionValue = (int)(selectionOffset);
|
2019-03-17 20:03:22 +01:00
|
|
|
Rectangle correctedTargetRectangle = new Rectangle(txRectangle.x+correctionValue, txRectangle.y, txRectangle.width, txRectangle.height);
|
2015-11-15 22:15:37 +01:00
|
|
|
for (LinkEntry entry : iRect) {
|
2019-03-17 20:03:22 +01:00
|
|
|
Rectangle correctedRectangle = new Rectangle(entry.rectangle.x+correctionValue, entry.rectangle.y, entry.rectangle.width, entry.rectangle.height);
|
2020-10-17 12:44:33 +02:00
|
|
|
drawArrow(proj, highlightType.equals(entry.relationType) ? highliteColor : fgColor,
|
2019-03-17 20:03:22 +01:00
|
|
|
correctedRectangle, correctedTargetRectangle);
|
2015-11-13 23:44:44 +01:00
|
|
|
}
|
2015-11-15 22:15:37 +01:00
|
|
|
for (LinkEntry entry : oRect) {
|
2019-03-17 20:03:22 +01:00
|
|
|
Rectangle correctedRectangle = new Rectangle(entry.rectangle.x+correctionValue, entry.rectangle.y, entry.rectangle.width, entry.rectangle.height);
|
2020-10-17 12:44:33 +02:00
|
|
|
drawArrow(proj, highlightType.equals(entry.relationType) ? highliteColor : fgColor, correctedTargetRectangle,
|
2019-03-17 20:03:22 +01:00
|
|
|
correctedRectangle);
|
2015-11-13 23:44:44 +01:00
|
|
|
}
|
2015-11-06 19:29:36 +01:00
|
|
|
}
|
|
|
|
|
2020-10-17 12:44:33 +02:00
|
|
|
protected void drawArrow(Projection proj, Color fgColor, Rectangle srcRectangle, Rectangle tgtRectangle) {
|
2020-06-20 17:58:26 +02:00
|
|
|
Point point1 = proj.project(new Point(srcRectangle.x, srcRectangle.y + srcRectangle.height / 2));
|
|
|
|
Point point2 = proj.project(new Point(tgtRectangle.x, tgtRectangle.y + tgtRectangle.height / 2));
|
2015-11-06 19:29:36 +01:00
|
|
|
|
2015-11-15 22:15:37 +01:00
|
|
|
if (point2.x > point1.x + srcRectangle.width)
|
|
|
|
point1.x += srcRectangle.width;
|
|
|
|
if (point1.x > point2.x + tgtRectangle.width)
|
|
|
|
point2.x += tgtRectangle.width;
|
|
|
|
|
|
|
|
Path path = new Path(Display.getCurrent());
|
|
|
|
path.moveTo(point1.x, point1.y);
|
|
|
|
if (point1.y == point2.y) {
|
|
|
|
Point center = new Point((point1.x + point2.x) / 2, point1.y - yCtrlOffset);
|
2020-11-29 12:42:51 +01:00
|
|
|
path.cubicTo(point1.x + X_CTRL_OFFSET, point1.y, center.x - X_CTRL_OFFSET, center.y, center.x, center.y);
|
|
|
|
path.cubicTo(center.x + X_CTRL_OFFSET, center.y, point2.x - X_CTRL_OFFSET, point2.y, point2.x, point2.y);
|
2015-11-13 23:44:44 +01:00
|
|
|
} else
|
2020-11-29 12:42:51 +01:00
|
|
|
path.cubicTo(point1.x + X_CTRL_OFFSET, point1.y, point2.x - X_CTRL_OFFSET, point2.y, point2.x, point2.y);
|
2020-10-17 12:44:33 +02:00
|
|
|
|
2020-06-20 17:58:26 +02:00
|
|
|
proj.setAntialias(SWT.ON);
|
|
|
|
proj.setForeground(fgColor);
|
|
|
|
proj.getGC().drawPath(path);
|
2015-11-13 23:44:44 +01:00
|
|
|
path.dispose();
|
2020-10-17 12:44:33 +02:00
|
|
|
// now draw the arrow head
|
|
|
|
proj.getGC().drawLine(point2.x - 8, point2.y - 5, point2.x, point2.y);
|
|
|
|
proj.getGC().drawLine(point2.x - 8, point2.y + 5, point2.x, point2.y);
|
|
|
|
|
2015-11-13 23:44:44 +01:00
|
|
|
}
|
2015-11-15 22:15:37 +01:00
|
|
|
|
|
|
|
class LinkEntry {
|
2020-11-29 12:42:51 +01:00
|
|
|
public final Rectangle rectangle;
|
|
|
|
public final RelationType relationType;
|
2015-11-15 22:15:37 +01:00
|
|
|
|
|
|
|
public LinkEntry(Rectangle rectangle, RelationType relationType) {
|
|
|
|
super();
|
|
|
|
this.rectangle = rectangle;
|
|
|
|
this.relationType = relationType;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-06 19:29:36 +01:00
|
|
|
}
|