/******************************************************************************* * Copyright (c) 2015 MINRES Technologies GmbH and others. * 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 *******************************************************************************/ package com.minres.scviewer.database.swt.internal; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.ScrollBar; import org.eclipse.wb.swt.SWTResourceManager; import com.google.common.collect.Lists; import com.minres.scviewer.database.ITx; import com.minres.scviewer.database.IWaveform; import com.minres.scviewer.database.RelationType; import com.minres.scviewer.database.swt.Constants; import com.minres.scviewer.database.ui.IWaveformViewer; import com.minres.scviewer.database.ui.TrackEntry; import com.minres.scviewer.database.ui.WaveformColors; public class WaveformCanvas extends Canvas { Color[] colors = new Color[WaveformColors.values().length]; private int trackHeight = 50; private long scaleFactor = 1000000L; // 1ns String unit="ns"; private int level = 12; private long maxTime; protected Point origin; /* original size */ protected int rulerHeight=40; protected List painterList; ITx currentSelection; private List selectionListeners; private RulerPainter rulerPainter; private TrackAreaPainter trackAreaPainter; private ArrowPainter arrowPainter; private List cursorPainters; HashMap wave2painterMap; /** * Constructor for ScrollableCanvas. * * @param parent * the parent of this control.super(parent, style | SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE | SWT.V_SCROLL | SWT.H_SCROLL); * @param style * the style of this control. */ public WaveformCanvas(final Composite parent, int style) { super(parent, style | SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.V_SCROLL | SWT.H_SCROLL); addControlListener(new ControlAdapter() { /* resize listener. */ public void controlResized(ControlEvent event) { syncScrollBars(); } }); addPaintListener(new PaintListener() { /* paint listener. */ public void paintControl(final PaintEvent event) { paint(event.gc); } }); painterList = new LinkedList(); origin = new Point(0, 0); selectionListeners = new LinkedList<>(); cursorPainters= new ArrayList<>(); wave2painterMap=new HashMap<>(); initScrollBars(); initColors(null); // order is important: it is bottom to top trackAreaPainter=new TrackAreaPainter(this); painterList.add(trackAreaPainter); rulerPainter=new RulerPainter(this); painterList.add(rulerPainter); arrowPainter=new ArrowPainter(this, IWaveformViewer.NEXT_PREV_IN_STREAM); painterList.add(arrowPainter); CursorPainter cp = new CursorPainter(this, scaleFactor * 10, cursorPainters.size()-1); painterList.add(cp); cursorPainters.add(cp); CursorPainter marker = new CursorPainter(this, scaleFactor * 100, cursorPainters.size()-1); painterList.add(marker); cursorPainters.add(marker); wave2painterMap=new HashMap<>(); // fall back initialization colors[WaveformColors.LINE.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_RED); colors[WaveformColors.LINE_HIGHLITE.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_CYAN); colors[WaveformColors.TRACK_BG_EVEN.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_BLACK); colors[WaveformColors.TRACK_BG_ODD.ordinal()] = SWTResourceManager.getColor(40, 40, 40); colors[WaveformColors.TRACK_BG_HIGHLITE.ordinal()] = SWTResourceManager.getColor(40, 40, 80); colors[WaveformColors.TX_BG.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_GREEN); colors[WaveformColors.TX_BG_HIGHLITE.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_DARK_GREEN); colors[WaveformColors.TX_BORDER.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_RED); colors[WaveformColors.SIGNAL0.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_GREEN); colors[WaveformColors.SIGNAL1.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_GREEN); colors[WaveformColors.SIGNALZ.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_DARK_YELLOW); colors[WaveformColors.SIGNALX.ordinal()] = SWTResourceManager.getColor(255, 51, 51); colors[WaveformColors.SIGNALU.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_YELLOW); colors[WaveformColors.SIGNAL_TEXT.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_WHITE); colors[WaveformColors.SIGNAL_REAL.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_YELLOW); colors[WaveformColors.SIGNAL_NAN.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_RED); colors[WaveformColors.CURSOR.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_RED); colors[WaveformColors.CURSOR_DRAG.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_GRAY); colors[WaveformColors.CURSOR_TEXT.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_WHITE); colors[WaveformColors.MARKER.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_DARK_GRAY); colors[WaveformColors.MARKER_TEXT.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_WHITE); colors[WaveformColors.REL_ARROW.ordinal()] = SWTResourceManager.getColor(SWT.COLOR_MAGENTA); colors[WaveformColors.REL_ARROW_HIGHLITE.ordinal()] = SWTResourceManager.getColor(255, 128, 255); } public void addCursoPainter(CursorPainter cursorPainter){ painterList.add(cursorPainter); cursorPainters.add(cursorPainter); } public void initColors(HashMap colourMap) { Display d = getDisplay(); if (colourMap != null) { for (WaveformColors c : WaveformColors.values()) { if (colourMap.containsKey(c)) colors[c.ordinal()] = new Color(d, colourMap.get(c)); } redraw(); } } public void setHighliteRelation(RelationType relationType){ if(arrowPainter!=null){ boolean redraw = arrowPainter.getHighlightType()!=relationType; arrowPainter.setHighlightType(relationType); if(redraw) redraw(); } } public Point getOrigin() { return origin; } public int getWidth() { return getClientArea().width; } public void setOrigin(Point origin) { setOrigin(origin.x, origin.y); } public void setOrigin(int x, int y) { checkWidget(); ScrollBar hBar = getHorizontalBar(); hBar.setSelection(-x); x = -hBar.getSelection(); ScrollBar vBar = getVerticalBar(); vBar.setSelection(-y); y = -vBar.getSelection(); origin.x = x; origin.y = y; syncScrollBars(); } public long getMaxTime() { return maxTime; } public void setMaxTime(long maxTime) { this.maxTime = maxTime; syncScrollBars(); } public int getTrackHeight() { return trackHeight; } public void setTrackHeight(int trackHeight) { this.trackHeight = trackHeight; syncScrollBars(); } public int getZoomLevel() { return level; } public int getMaxZoomLevel(){ return Constants.unitMultiplier.length*Constants.unitString.length-1; } public void setZoomLevel(int level) { long tc=cursorPainters.get(0).getTime(); // cursor time setZoomLevel(level, tc); } public void setZoomLevel(int level, long centerTime) { long oldScaleFactor=scaleFactor; if(level<0) level = 0; if(level0) { origin.x=(int) -originX; // new cursor time offset relative to left border }else { origin.x=0; } syncScrollBars(); arrowPainter.setTx(tx); redraw(); } } public long getScaleFactor() { return scaleFactor; } public long getScaleFactorPow10() { int scale = level/Constants.unitMultiplier.length; double res = Math.pow(1000, scale); return (long) res; } public String getUnitStr(){ return Constants.unitString[level/Constants.unitMultiplier.length]; } public int getUnitMultiplier(){ return Constants.unitMultiplier[level%Constants.unitMultiplier.length]; } public long getTimeForOffset(int xOffset){ return (xOffset-origin.x) * scaleFactor; } public void addPainter(IPainter painter) { painterList.add(painter); redraw(); } public void removePainter(IPainter painter) { painterList.remove(painter); redraw(); } public void clearAllWaveformPainter() { clearAllWaveformPainter(true); } void clearAllWaveformPainter(boolean update) { trackAreaPainter.getTrackVerticalOffset().clear(); wave2painterMap.clear(); if(update) syncScrollBars(); } public void addWaveformPainter(IWaveformPainter painter) { addWaveformPainter(painter, true); } void addWaveformPainter(IWaveformPainter painter, boolean update) { trackAreaPainter.addTrackPainter(painter); wave2painterMap.put(painter.getTrackEntry().waveform, painter); if(update) syncScrollBars(); } public List getCursorPainters() { return cursorPainters; } /** * Dispose the garbage here */ public void dispose() { for (WaveformColors c : WaveformColors.values()) colors[c.ordinal()].dispose(); super.dispose(); } /* Initialize the scrollbar and register listeners. */ private void initScrollBars() { ScrollBar horizontal = getHorizontalBar(); horizontal.setEnabled(false); horizontal.setVisible(true); horizontal.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { if (painterList.size() == 0) return; setOrigin(-((ScrollBar) event.widget).getSelection(), origin.y); } }); ScrollBar vertical = getVerticalBar(); vertical.setEnabled(false); vertical.setVisible(true); vertical.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { if (painterList.size() == 0) return; setOrigin(origin.x, -((ScrollBar) event.widget).getSelection()); } }); } /** * Synchronize the scrollbar with the image. If the transform is out of * range, it will correct it. This function considers only following factors * : transform, image size, client area. */ public void syncScrollBars() { if (painterList.size() == 0) { redraw(); return; } int height = trackAreaPainter.getHeight(); // incl. Ruler long width = maxTime / scaleFactor; Rectangle clientArea=getClientArea(); ScrollBar horizontal = getHorizontalBar(); horizontal.setIncrement((int) (getClientArea().width / 100)); horizontal.setPageIncrement(getClientArea().width); int clientWidthw = clientArea.width; if (width > clientWidthw) { /* image is wider than client area */ horizontal.setMinimum(0); horizontal.setMaximum((int)width); horizontal.setEnabled(true); if (((int) -origin.x) > horizontal.getMaximum() - clientWidthw) { origin.x = -horizontal.getMaximum() + clientWidthw; } } else { /* image is narrower than client area */ horizontal.setEnabled(false); } horizontal.setThumb(clientWidthw); horizontal.setSelection(-origin.x); ScrollBar vertical = getVerticalBar(); vertical.setIncrement((int) (getClientArea().height / 100)); vertical.setPageIncrement((int) (getClientArea().height)); int clientHeighth = clientArea.height; if (height > clientHeighth) { /* image is higher than client area */ vertical.setMinimum(0); vertical.setMaximum(height); vertical.setEnabled(true); if (((int) -origin.y) > vertical.getMaximum() - clientHeighth) { origin.y = -vertical.getMaximum() + clientHeighth; } } else { /* image is less higher than client area */ vertical.setMaximum((int) (clientHeighth)); vertical.setEnabled(false); } vertical.setThumb(clientHeighth); vertical.setSelection(-origin.y); redraw(); fireSelectionEvent(); } /* Paint function */ private void paint(GC gc) { Rectangle clientRect = getClientArea(); /* Canvas' painting area */ Projection p = new Projection(gc); p.setTranslation(origin); if (painterList.size() > 0 ) { for (IPainter painter : painterList) painter.paintArea(p, clientRect); } else { gc.fillRectangle(clientRect); initScrollBars(); } } public List getElementsAt(Point point) { LinkedList result=new LinkedList<>(); for (IPainter p : Lists.reverse(painterList)) { if (p instanceof TrackAreaPainter) { int y = point.y - origin.y; int x = point.x - origin.x; Entry entry = trackAreaPainter.getTrackVerticalOffset().floorEntry(y); if (entry != null) { if (entry.getValue() instanceof StreamPainter) { ITx tx = ((StreamPainter) entry.getValue()).getClicked(new Point(x, y - entry.getKey())); if(tx!=null) result.add(tx); } result.add(entry.getValue().getTrackEntry()); } } else if (p instanceof CursorPainter) { if (Math.abs(point.x - origin.x - ((CursorPainter) p).getTime()/scaleFactor) < 2) { result.add(p); } } } return result; } public List getEntriesAtPosition(IWaveform iWaveform, int i) { LinkedList result=new LinkedList<>(); int x = i - origin.x; for(IPainter p: wave2painterMap.values()){ if (p instanceof StreamPainter && ((StreamPainter)p).getStream()==iWaveform) { result.add(((StreamPainter) p).getClicked(new Point(x, trackHeight/2))); } } return result; } public void setSelected(ITx currentSelection) { this.currentSelection = currentSelection; if (currentSelection != null) reveal(currentSelection); arrowPainter.setTx(currentSelection); redraw(); } public void reveal(ITx tx) { int lower = (int) (tx.getBeginTime() / scaleFactor); int higher = (int) (tx.getEndTime() / scaleFactor); Point size = getSize(); size.x -= getVerticalBar().getSize().x + 2; size.y -= getHorizontalBar().getSize().y; if (lower < -origin.x) { setOrigin(-lower, origin.y); } else if (higher > (size.x - origin.x)) { setOrigin(size.x - higher, origin.y); } for (IWaveformPainter painter : wave2painterMap.values()) { if (painter instanceof StreamPainter && ((StreamPainter) painter).getStream() == tx.getStream()) { int top = painter.getVerticalOffset() + trackHeight * tx.getConcurrencyIndex(); int bottom = top + trackHeight; if (top < -origin.y) { setOrigin(origin.x, -(top-trackHeight)); } else if (bottom > (size.y - origin.y)) { setOrigin(origin.x, size.y - bottom); } } } } public void reveal(IWaveform waveform) { for (IWaveformPainter painter : wave2painterMap.values()) { TrackEntry te = painter.getTrackEntry(); if(te.waveform == waveform) { Point size = getSize(); //size.x -= getVerticalBar().getSize().x + 2; size.y -=+rulerHeight; ScrollBar sb = getHorizontalBar(); if((sb.getStyle()&SWT.SCROLLBAR_OVERLAY)!=0 && sb.isVisible()) // TODO: check on other platform than MacOSX size.y-= getHorizontalBar().getSize().y; int top = te.vOffset; int bottom = top + trackHeight; if (top < -origin.y) { setOrigin(origin.x, -(top-trackHeight)); } else if (bottom > (size.y - origin.y)) { setOrigin(origin.x, size.y - bottom); } } } } public void reveal(long time) { int scaledTime = (int) (time / scaleFactor); Point size = getSize(); size.x -= getVerticalBar().getSize().x + 2; size.y -= getHorizontalBar().getSize().y; if (scaledTime < -origin.x) { setOrigin(-scaledTime+10, origin.y); } else if (scaledTime > (size.x - origin.x)) { setOrigin(size.x - scaledTime-30, origin.y); } } public int getRulerHeight() { return rulerHeight; } public void setRulerHeight(int rulerHeight) { this.rulerHeight = rulerHeight; } public void addSelectionListener(SelectionAdapter selectionAdapter) { selectionListeners.add(selectionAdapter); } public void removeSelectionListener(SelectionAdapter selectionAdapter) { selectionListeners.remove(selectionAdapter); } /** * */ protected void fireSelectionEvent() { Event e = new Event(); e.widget = this; e.detail=SWT.SELECTED; e.type=SWT.Selection; SelectionEvent ev = new SelectionEvent(e); ev.x = origin.x; ev.y = origin.y; for (SelectionAdapter a : selectionListeners) { a.widgetSelected(ev); } } long getMaxVisibleTime() { return (getClientArea().width+origin.x)*scaleFactor; } long getMinVisibleTime() { return origin.x * scaleFactor; } }