/******************************************************************************* * Copyright (c) 2014, 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.ui.swt; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.HashMap; import java.util.List; import java.util.Map.Entry; import java.util.TreeMap; import java.util.Vector; import org.eclipse.core.runtime.ListenerList; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CLabel; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.TextLayout; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.ScrollBar; import org.eclipse.wb.swt.SWTResourceManager; import swing2swt.layout.BorderLayout; import com.minres.scviewer.database.ISignal; import com.minres.scviewer.database.ITx; import com.minres.scviewer.database.ITxEvent; import com.minres.scviewer.database.ITxStream; import com.minres.scviewer.database.IWaveform; import com.minres.scviewer.database.IWaveformEvent; import com.minres.scviewer.ui.handler.GotoDirection; public class TxDisplay implements PropertyChangeListener, ISelectionProvider, MouseListener{ private ListenerList listeners = new ListenerList(); private static final String SELECTION="selection"; private ITx currentSelection; private IWaveform currentWaveformSelection; private ScrolledComposite nameListScrolled; private ScrolledComposite valueListScrolled; private Canvas nameList; private Canvas valueList; WaveformCanvas trackList; private Composite top; protected ObservableList> streams; Vector cursorPainters; private Composite trackPane; private Ruler ruler; TreeMap> trackVerticalOffset; HashMap, String> actualValues; // private long maxTime=0; private Font nameFont, nameFontB; public TxDisplay(Composite parent) { trackVerticalOffset = new TreeMap>(); actualValues= new HashMap, String>(); cursorPainters=new Vector(); nameFont = parent.getDisplay().getSystemFont(); nameFontB = SWTResourceManager.getBoldFont(nameFont); streams=new ObservableList>(); streams.addPropertyChangeListener(this); top = new Composite(parent, SWT.NONE); top.setLayout(new FillLayout(SWT.HORIZONTAL)); SashForm topSash = new SashForm(top, SWT.SMOOTH); topSash.setBackground(topSash.getDisplay().getSystemColor( SWT.COLOR_GRAY)); Composite composite = new Composite(topSash, SWT.NONE); composite.setLayout(new FillLayout(SWT.HORIZONTAL)); SashForm leftSash = new SashForm(composite, SWT.SMOOTH); leftSash.setBackground(leftSash.getDisplay().getSystemColor( SWT.COLOR_GRAY)); Composite namePane = createTextPane(leftSash, "Name"); namePane.setBackground(namePane.getDisplay().getSystemColor( SWT.COLOR_WIDGET_BACKGROUND)); nameListScrolled = new ScrolledComposite(namePane, SWT.H_SCROLL | SWT.V_SCROLL); nameListScrolled.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); nameListScrolled.setExpandHorizontal(true); nameListScrolled.setExpandVertical(true); nameListScrolled.setAlwaysShowScrollBars(true); nameListScrolled.addControlListener(new ControlAdapter(){ @Override public void controlResized(ControlEvent e) { nameListScrolled.getVerticalBar().setVisible(false); } }); nameList = new Canvas(nameListScrolled, SWT.NONE){ @Override public Point computeSize(int wHint, int hHint, boolean changed) { Rectangle bounds= super.getClientArea(); return new Point(bounds.width, bounds.height); } }; nameList.addListener(SWT.Paint, new Listener() { @Override public void handleEvent(Event event) { GC gc = event.gc; Rectangle rect = ((Canvas)event.widget).getClientArea(); paintNames(gc, rect); } }); nameList.addMouseListener(this); nameListScrolled.setContent(nameList); Composite valuePane = createTextPane(leftSash, "Value"); valuePane.setBackground(valuePane.getDisplay().getSystemColor( SWT.COLOR_WIDGET_BACKGROUND)); valueListScrolled = new ScrolledComposite(valuePane, SWT.H_SCROLL | SWT.V_SCROLL); valueListScrolled.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); valueListScrolled.setExpandHorizontal(true); valueListScrolled.setExpandVertical(true); valueListScrolled.setAlwaysShowScrollBars(true); valueListScrolled.addControlListener(new ControlAdapter(){ @Override public void controlResized(ControlEvent e) { valueListScrolled.getVerticalBar().setVisible(false); } }); valueList = new Canvas(valueListScrolled, SWT.NONE){ @Override public Point computeSize(int wHint, int hHint, boolean changed) { Rectangle bounds= super.getClientArea(); return new Point(bounds.width, bounds.height); } }; valueList.addListener(SWT.Paint, new Listener() { @Override public void handleEvent(Event event) { GC gc = event.gc; Rectangle rect = ((Canvas)event.widget).getClientArea(); paintValues(gc, rect); } }); valueList.addMouseListener(this); valueListScrolled.setContent(valueList); trackPane = new Composite(topSash, SWT.NONE); trackPane.setLayout(new BorderLayout(0, 0)); ruler = new Ruler(trackPane, SWT.NONE); ruler.setLayoutData(BorderLayout.NORTH); trackList = new WaveformCanvas(trackPane, SWT.NONE); trackList.setLayoutData(BorderLayout.CENTER); trackList.setStreams(streams); trackList.setRuler(ruler); trackList.addPainter(new TrackPainter(trackList)); CursorPainter cp = new CursorPainter(trackList, trackList.getScaleFactor()*10); trackList.addPainter(cp); cursorPainters.add(cp); trackList.setMaxTime(1); trackList.addMouseListener(this); nameListScrolled.getVerticalBar().addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { int y = ((ScrollBar) e.widget).getSelection(); valueListScrolled.setOrigin(valueListScrolled.getOrigin().x, y); trackList.scrollToY(y); } }); valueListScrolled.getVerticalBar().addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { int y = ((ScrollBar) e.widget).getSelection(); nameListScrolled.setOrigin(nameListScrolled.getOrigin().x, y); trackList.scrollToY(y); } }); trackList.getVerticalBar().addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { int y = trackList.getVerticalBar().getSelection(); nameListScrolled.setOrigin(nameListScrolled.getOrigin().x, y); valueListScrolled.setOrigin(valueListScrolled.getOrigin().x, y); } }); topSash.setWeights(new int[] {30, 70}); leftSash.setWeights(new int[] {75, 25}); } private Composite createTextPane(SashForm leftSash, String text) { Composite namePane = new Composite(leftSash, SWT.NONE); GridLayout gl_namePane = new GridLayout(1, false); gl_namePane.verticalSpacing = 0; gl_namePane.marginWidth = 0; gl_namePane.horizontalSpacing = 0; gl_namePane.marginHeight = 0; namePane.setLayout(gl_namePane); CLabel nameLabel = new CLabel(namePane, SWT.NONE); GridData gd_nameLabel = new GridData(SWT.CENTER, SWT.CENTER, true, false, 1, 1); gd_nameLabel.heightHint = Ruler.height-2; nameLabel.setLayoutData(gd_nameLabel); nameLabel.setText(text); Label nameSep = new Label(namePane, SWT.SEPARATOR | SWT.HORIZONTAL); nameSep.setBackground(SWTResourceManager.getColor(SWT.COLOR_DARK_GRAY)); nameSep.setForeground(SWTResourceManager.getColor(SWT.COLOR_BLACK)); GridData gd_nameSep = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1); gd_nameSep.heightHint = 2; nameSep.setLayoutData(gd_nameSep); return namePane; } @Override public void propertyChange(PropertyChangeEvent pce) { if("size".equals(pce.getPropertyName())) { updateTracklist(); } } @SuppressWarnings("unchecked") protected void updateTracklist() { int yoffs=0; int nameMaxWidth=0; IWaveformPainter painter=null; trackVerticalOffset.clear(); actualValues.clear(); trackList.clearAllWavefromPainter(); boolean even=true; TextLayout tl = new TextLayout(trackList.getDisplay()); tl.setFont(nameFontB); for(IWaveform waveform:streams){ int height=trackList.getTrackHeight(); if(waveform instanceof ITxStream){ height*=((ITxStream)waveform).getMaxConcurrency(); painter= new StreamPainter(trackList, even, height, (ITxStream) waveform); actualValues.put(waveform, ""); } else if(waveform instanceof ISignal){ painter= new SignalPainter(trackList, even, height, (ISignal) waveform); actualValues.put(waveform, "---"); } trackList.addWavefromPainter(yoffs, painter); trackVerticalOffset.put(yoffs, waveform); tl.setText(waveform.getFullName()); nameMaxWidth=Math.max(nameMaxWidth, tl.getBounds().width); yoffs+=height; even=!even; } nameList.setSize(nameMaxWidth+15, yoffs); nameListScrolled.setMinSize(nameMaxWidth+15, yoffs); valueList.setSize(calculateValueWidth(), yoffs); valueListScrolled.setMinSize(calculateValueWidth(), yoffs); nameList.redraw(); valueList.redraw(); trackList.redraw(); top.layout(new Control[]{valueList, nameList, trackList}); } private int calculateValueWidth() { TextLayout tl = new TextLayout(trackList.getDisplay()); tl.setFont(nameFontB); int valueMaxWidth=0; for(String v:actualValues.values()){ tl.setText(v); valueMaxWidth=Math.max(valueMaxWidth, tl.getBounds().width); } return valueMaxWidth+15; } @Override public void addSelectionChangedListener(ISelectionChangedListener listener) { listeners.add(listener); } @Override public void removeSelectionChangedListener(ISelectionChangedListener listener) { listeners.remove(listener); } @Override public ISelection getSelection() { if(currentSelection!=null) return new StructuredSelection(currentSelection); else if(currentWaveformSelection!=null) return new StructuredSelection(currentWaveformSelection); else return null; } @SuppressWarnings("unchecked") @Override public void setSelection(ISelection selection) { boolean selectionChanged=false; if(selection instanceof IStructuredSelection){ Object sel =((IStructuredSelection)selection).getFirstElement(); if(sel instanceof ITx && currentSelection!=sel){ currentSelection=(ITx) sel; currentWaveformSelection = currentSelection.getStream(); selectionChanged=true; } else if(sel instanceof IWaveform && currentWaveformSelection!=sel){ currentSelection=null; currentWaveformSelection = (IWaveform) sel; selectionChanged=true; } } else { if(currentSelection!=null || currentWaveformSelection!=null) selectionChanged=true; currentSelection=null; currentWaveformSelection = null; } if(selectionChanged){ trackList.setSelected(currentSelection, currentWaveformSelection); nameList.setData(SELECTION, currentWaveformSelection); valueList.redraw(); nameList.redraw(); Object[] list = listeners.getListeners(); for (int i = 0; i < list.length; i++) { ((ISelectionChangedListener) list[i]).selectionChanged(new SelectionChangedEvent(this, selection)); } } } @SuppressWarnings("unchecked") public void moveSelection(GotoDirection direction) { if(currentWaveformSelection instanceof ITxStream){ ITxStream stream = (ITxStream) currentWaveformSelection; ITx transaction=null; if(direction==GotoDirection.NEXT){ Entry> entry = stream.getEvents().higherEntry(currentSelection.getBeginTime()); if(entry!=null) do { for(ITxEvent evt:entry.getValue()){ if(evt.getType()==ITxEvent.Type.BEGIN){ transaction=evt.getTransaction(); break; } } if(transaction==null) entry=stream.getEvents().higherEntry(entry.getKey()); }while(entry!=null && transaction==null); }else if(direction==GotoDirection.PREV){ Entry> entry = stream.getEvents().lowerEntry(currentSelection.getBeginTime()); if(entry!=null) do { for(ITxEvent evt:entry.getValue()){ if(evt.getType()==ITxEvent.Type.BEGIN) transaction=evt.getTransaction(); break; } if(transaction==null) entry=stream.getEvents().lowerEntry(entry.getKey()); }while(entry!=null && transaction==null); } if(transaction!=null){ setSelection(new StructuredSelection(transaction)); } } } @Override public void mouseDoubleClick(MouseEvent e) { } @Override public void mouseDown(MouseEvent e) { if(e.widget==trackList){ Object o = trackList.getClicked(new Point(e.x, e.y)); if(o !=null) setSelection(new StructuredSelection(o)); }else if(e.widget==valueList || e.widget==nameList){ Entry> entry = trackVerticalOffset.floorEntry(e.y); if(entry!=null) setSelection(new StructuredSelection(entry.getValue())); } } @Override public void mouseUp(MouseEvent e) { } public List> getStreamList(){ return streams; } protected void paintNames(GC gc, Rectangle rect) { if(streams.size()>0){ @SuppressWarnings("unchecked") IWaveform wave = (IWaveform) nameList.getData(SELECTION); Integer firstKey=trackVerticalOffset.floorKey(rect.y); if(firstKey==null) firstKey=trackVerticalOffset.firstKey(); Integer lastKey = trackVerticalOffset.floorKey(rect.y+rect.height); Rectangle subArea = new Rectangle(rect.x, 0, rect.width, trackList.getTrackHeight()); if(lastKey==firstKey){ IWaveform w = trackVerticalOffset.get(firstKey); if(w instanceof ITxStream) subArea.height*=((ITxStream)w).getMaxConcurrency(); drawTextFormat(gc, subArea, firstKey, w.getFullName(), wave!=null && w.getId()==wave.getId()); }else{ for(Entry> entry : trackVerticalOffset.subMap(firstKey, true, lastKey, true).entrySet()){ IWaveform w = entry.getValue(); subArea.height=trackList.getTrackHeight(); if(w instanceof ITxStream) subArea.height*=((ITxStream)w).getMaxConcurrency(); drawTextFormat(gc, subArea, entry.getKey(), w.getFullName(), wave!=null && w.getId()==wave.getId()); } } } } @SuppressWarnings("unchecked") protected void paintValues(GC gc, Rectangle rect) { if(streams.size()>0){ IWaveform wave = (IWaveform) nameList.getData(SELECTION); Integer firstKey=trackVerticalOffset.floorKey(rect.y); if(firstKey==null) firstKey=trackVerticalOffset.firstKey(); Integer lastKey = trackVerticalOffset.floorKey(rect.y+rect.height); Rectangle subArea = new Rectangle(rect.x, 0, rect.width, trackList.getTrackHeight()); if(lastKey==firstKey){ IWaveform w = trackVerticalOffset.get(firstKey); if(w instanceof ITxStream) subArea.height*=((ITxStream)w).getMaxConcurrency(); drawTextFormat(gc, subArea, firstKey, actualValues.get(w), wave!=null && w.getId()==wave.getId()); }else{ for(Entry> entry : trackVerticalOffset.subMap(firstKey, true, lastKey, true).entrySet()){ IWaveform w = entry.getValue(); subArea.height=trackList.getTrackHeight(); if(w instanceof ITxStream) subArea.height*=((ITxStream)w).getMaxConcurrency(); drawTextFormat(gc, subArea, entry.getKey(), actualValues.get(w), wave!=null && w.getId()==wave.getId()); } } } } protected void drawTextFormat(GC gc, Rectangle subArea, int yOffset, String value, boolean highlite) { Point size = gc.textExtent(value); if(highlite){ gc.setBackground(SWTResourceManager.getColor(SWT.COLOR_LIST_SELECTION)); gc.setForeground(SWTResourceManager.getColor(SWT.COLOR_LIST_SELECTION_TEXT)); gc.fillRectangle(subArea.x, subArea.y+yOffset, subArea.width, subArea.height); gc.setFont(nameFontB); }else gc.setFont(nameFont); gc.drawText(value, subArea.x+5, subArea.y + yOffset+(trackList.getTrackHeight()-size.y)/2, true); } public long getMaxTime() { return trackList.getMaxTime(); } public void setMaxTime(long maxTime) { this.trackList.setMaxTime(maxTime); } public void setZoomLevel(int scale) { trackList.setZoomLevel(scale); } public int getZoomLevel() { return trackList.getZoomLevel(); } // public long getScaleFactor(){ // return trackList.getScaleFactor(); // } }