Merge branch 'feature/custom_scrollbar' into develop

This commit is contained in:
Eyck Jentzsch 2022-01-08 19:17:15 +01:00
commit 2d11e39653
50 changed files with 4173 additions and 272 deletions

View File

@ -30,25 +30,43 @@ Legend:
* Mouse Scroll wheel: MScrl * Mouse Scroll wheel: MScrl
* Context any means Name List, Value List or Waveform * Context any means Name List, Value List or Waveform
| Input | Modifier | Context | Action | | Input | Modifier | Context | Action |
|-----------|----------|----------|-----------------------------------| |------------|----------|----------|-----------------------------------|
| LMB klick | | any | select | | LMB click | | any | select |
| LMB klick | Shift | Waveform | move selected marker to position | | LMB click | Shift | Waveform | move selected marker to position |
| LMB klick | Control | Waveform | move cursor to position | | LMB click | Control | Waveform | move cursor to position |
| LMB drag | | Waveform | zoom to range | | LMB drag | | Waveform | zoom to range |
| MMB klick | | Waveform | move selected marker to position | | MMB click | | Waveform | move selected marker to position |
| MScrl | | any | scroll window up/down | | MScrl | | any | scroll window up/down |
| MScrl | Shift | any | scroll window left/right | | MScrl | Shift | any | scroll window left/right |
| Key left | | Waveform | scroll window to the left (slow) | | MScrl | Control | Waveform | zoom in/out |
| Key right | | Waveform | scroll window to the right (slow) | | Key left | | Waveform | scroll window to the left (slow) |
| Key left | Shift | Waveform | scroll window to the left (fast) | | Key right | | Waveform | scroll window to the right (slow) |
| Key right | Shift | Waveform | scroll window to the right (fast) | | Key left | Shift | Waveform | scroll window to the left (fast) |
| Key up | | Waveform | move selection up | | Key right | Shift | Waveform | scroll window to the right (fast) |
| Key down | | Waveform | move selection down | | Key up | | Waveform | move selection up |
| Key up | Control | Waveform | move selected track up | | Key down | | Waveform | move selection down |
| Key down | Control | Waveform | move selected track down | | Key up | Control | Waveform | move selected track up |
| Key + | Control | Waveform | zoom in | | Key down | Control | Waveform | move selected track down |
| Key - | Control | Waveform | zoom out | | Key + | Control | Waveform | zoom in |
| Key Pos1 | | Waveform | jump to selected marker | | Key - | Control | Waveform | zoom out |
| Key End | | Waveform | jump to cursor | | Key Pos1 | | Waveform | jump to selected marker |
| Key Del | | any | delete selected entries | | Key End | | Waveform | jump to cursor |
| Key Del | | any | delete selected entries |
| LMB click | | ZoomBar | increment/decrement 1 page |
| LMB drag | | ZoomBar | drag both markers (pan) |
| LMB drag | Control | ZoomBar | drag one marker (zoom) |
| MMB drag | | ZoomBar | drag one marker (zoom) |
| xMB dclick | | ZoomBar | pan to position |
| MScrl | | ZoomBar | scroll window left/right |
| MScrl | Shift | ZoomBar | scroll window left/right double speed |
| MScrl | Control | ZoomBar | zoom in/out |
| Key left | | ZoomBar | scroll window to the left (slow) |
| Key right | | ZoomBar | scroll window to the right (slow) |
| Key up | | ZoomBar | scroll window to the left (slow) |
| Key down | | ZoomBar | scroll window to the right (slow) |
| Key PgUp | | ZoomBar | scroll window to the left (fast) |
| Key PgDown | | ZoomBar | scroll window to the right (fast) |
| Key Pos1 | | ZoomBar | scroll to begin |
| Key End | | ZoomBar | scroll to end |

View File

@ -13,6 +13,7 @@ package com.minres.scviewer.database.ui.swt;
import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Plugin; import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.Status;
import org.eclipse.wb.swt.SWTResourceManager;
import org.osgi.framework.BundleContext; import org.osgi.framework.BundleContext;
public class DatabaseUiPlugin extends Plugin { public class DatabaseUiPlugin extends Plugin {
@ -24,6 +25,7 @@ public class DatabaseUiPlugin extends Plugin {
@Override @Override
public void stop(BundleContext context) throws Exception { public void stop(BundleContext context) throws Exception {
SWTResourceManager.dispose();
getLog().log(new Status(IStatus.OK, "org.eclipse.e4.core", "Stopping org.eclipse.e4.core bundle...")); getLog().log(new Status(IStatus.OK, "org.eclipse.e4.core", "Stopping org.eclipse.e4.core bundle..."));
} }
} }

View File

@ -35,14 +35,15 @@ public class TrackAreaPainter implements IPainter {
} }
public void paintArea(Projection proj, Rectangle a) { public void paintArea(Projection proj, Rectangle a) {
Rectangle area = proj.unProject(new Rectangle(a.x, a.y+waveCanvas.rulerHeight, a.width, a.height-waveCanvas.rulerHeight)); Rectangle area = proj.unProject(new Rectangle(a.x, a.y+waveCanvas.rulerHeight, a.width, a.height-waveCanvas.rulerHeight));
proj.setBackground(this.waveCanvas.styleProvider.getColor(WaveformColors.TRACK_BG_EVEN)); proj.setBackground(this.waveCanvas.styleProvider.getColor(WaveformColors.TRACK_BG_EVEN));
proj.setFillRule(SWT.FILL_EVEN_ODD); proj.setFillRule(SWT.FILL_EVEN_ODD);
proj.fillRectangle(area); proj.fillRectangle(area);
if(trackVerticalOffset.size()>0){ if(trackVerticalOffset.size()>0){
Integer firstKey=trackVerticalOffset.floorKey(area.y); Integer firstKey=trackVerticalOffset.floorKey(area.y);
if(firstKey==null) firstKey=trackVerticalOffset.firstKey(); if(firstKey==null) firstKey=trackVerticalOffset.firstKey();
Integer lastKey = trackVerticalOffset.floorKey(area.y+area.height); Integer lastKey = trackVerticalOffset.floorKey(area.y+area.height);
//if(lastKey==null) lastKey= trackVerticalOffset.lastKey();
Rectangle subArea = new Rectangle(area.x, 0, area.width, 0); Rectangle subArea = new Rectangle(area.x, 0, area.width, 0);
if(lastKey.equals(firstKey)){ if(lastKey.equals(firstKey)){
subArea.y=firstKey; subArea.y=firstKey;

View File

@ -46,6 +46,7 @@ import com.minres.scviewer.database.ui.IWaveformZoom;
import com.minres.scviewer.database.ui.TrackEntry; import com.minres.scviewer.database.ui.TrackEntry;
import com.minres.scviewer.database.ui.ZoomKind; import com.minres.scviewer.database.ui.ZoomKind;
import com.minres.scviewer.database.ui.swt.Constants; import com.minres.scviewer.database.ui.swt.Constants;
import com.minres.scviewer.database.ui.swt.internal.slider.ZoomBar;
public class WaveformCanvas extends Canvas implements IWaveformZoom{ public class WaveformCanvas extends Canvas implements IWaveformZoom{
@ -53,6 +54,8 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
public static final long ZOOM_FULL = -1; public static final long ZOOM_FULL = -1;
private static final int INITIAL_ZOOM_BAR_MAX = 1000;
private boolean doubleBuffering = true; private boolean doubleBuffering = true;
IWaveformStyleProvider styleProvider; IWaveformStyleProvider styleProvider;
@ -81,22 +84,23 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
private List<CursorPainter> cursorPainters; private List<CursorPainter> cursorPainters;
private ZoomBar horizontal;
private int[] lastHorSelection;
private long sliderScaleFactor = 1;
private ScrollBar vertical;
HashMap<IWaveform, IWaveformPainter> wave2painterMap; HashMap<IWaveform, IWaveformPainter> wave2painterMap;
/**
* Constructor for ScrollableCanvas. public WaveformCanvas(final Composite parent, int style, IWaveformStyleProvider styleProvider, ZoomBar.IProvider scrollbarProvider) {
* super(parent, (style & ~SWT.H_SCROLL) | SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.V_SCROLL );
* @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, IWaveformStyleProvider styleProvider) {
super(parent, style | SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.V_SCROLL | SWT.H_SCROLL);
this.styleProvider=styleProvider; this.styleProvider=styleProvider;
addControlListener(new ControlAdapter() { /* resize listener. */ addControlListener(new ControlAdapter() { /* resize listener. */
@Override @Override
public void controlResized(ControlEvent event) { public void controlResized(ControlEvent event) {
syncScrollBars(); syncSb();
} }
}); });
addPaintListener((final PaintEvent event) -> paint(event.gc)); addPaintListener((final PaintEvent event) -> paint(event.gc));
@ -106,6 +110,8 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
cursorPainters= new ArrayList<>(); cursorPainters= new ArrayList<>();
wave2painterMap=new HashMap<>(); wave2painterMap=new HashMap<>();
horizontal = scrollbarProvider.getScrollBar();
vertical = getVerticalBar();
initScrollBars(); initScrollBars();
// order is important: it is bottom to top // order is important: it is bottom to top
trackAreaPainter=new TrackAreaPainter(this); trackAreaPainter=new TrackAreaPainter(this);
@ -114,10 +120,10 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
painterList.add(arrowPainter); painterList.add(arrowPainter);
rulerPainter=new RulerPainter(this); rulerPainter=new RulerPainter(this);
painterList.add(rulerPainter); painterList.add(rulerPainter);
CursorPainter cp = new CursorPainter(this, scaleFactor * 10, cursorPainters.size()-1); CursorPainter cp = new CursorPainter(this, getScale() * 10, cursorPainters.size()-1);
painterList.add(cp); painterList.add(cp);
cursorPainters.add(cp); cursorPainters.add(cp);
CursorPainter marker = new CursorPainter(this, scaleFactor * 100, cursorPainters.size()-1); CursorPainter marker = new CursorPainter(this, getScale() * 100, cursorPainters.size()-1);
painterList.add(marker); painterList.add(marker);
cursorPainters.add(marker); cursorPainters.add(marker);
wave2painterMap=new HashMap<>(); wave2painterMap=new HashMap<>();
@ -149,15 +155,9 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
public void setOrigin(int x, int y) { public void setOrigin(int x, int y) {
checkWidget(); checkWidget();
ScrollBar hBar = getHorizontalBar();
if(x<=0) hBar.setSelection(-x);
x = -hBar.getSelection();
ScrollBar vBar = getVerticalBar();
if(y<=0) vBar.setSelection(-y);
y = -vBar.getSelection();
origin.x = x; origin.x = x;
origin.y = y; origin.y = y;
syncScrollBars(); syncSb();
} }
@Override @Override
@ -167,7 +167,13 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
public void setMaxTime(long maxTime) { public void setMaxTime(long maxTime) {
this.maxTime = maxTime; this.maxTime = maxTime;
syncScrollBars(); if(maxTime>INITIAL_ZOOM_BAR_MAX) {
long maxBarTime = maxTime;
while(maxBarTime>Integer.MAX_VALUE) maxBarTime/=1000;
horizontal.setMaximum((int) maxBarTime);
}
sliderScaleFactor = maxTime/horizontal.getMaximum();
syncSb();
} }
@Override @Override
@ -206,37 +212,41 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
factor=1; factor=1;
else if(factor>maxFactor) else if(factor>maxFactor)
factor=maxFactor; factor=maxFactor;
if(factor!=scaleFactor || (getMaxVisibleTime()+getMinVisibleTime()/2) != centerTime) { if(factor!=getScale() || (getMaxVisibleTime()+getMinVisibleTime()/2) != centerTime) {
scaleFactor = factor; updateScaleFactor(factor);
scaleMagnitude = 0;
for(int i=Constants.POWERS_OF_TEN.length-1; i>0; i--) {
if(scaleFactor>=Constants.POWERS_OF_TEN[i]) {
scaleMagnitude = i;
break;
}
}
ITx tx = arrowPainter.getTx(); ITx tx = arrowPainter.getTx();
arrowPainter.setTx(null); arrowPainter.setTx(null);
/* /*
* xc = tc/oldScaleFactor * xc = tc/oldScaleFactor
* xoffs = xc+origin.x * xoffs = xc+origin.x
* xcn = tc/newScaleFactor * xcn = tc/newScaleFactor
* t0n = (xcn-xoffs)*scaleFactor * t0n = (xcn-xoffs)*getScale()
*/ */
long xoffs = clientAreaWidth/2; long xoffs = clientAreaWidth/2;
long xcn=centerTime/scaleFactor; // new total x-offset long xcn=centerTime/getScale(); // new total x-offset
long originX=xcn-xoffs; long originX=xcn-xoffs;
if(originX>0) { if(originX>0) {
origin.x=(int) -originX; // new cursor time offset relative to left border origin.x=(int) -originX; // new cursor time offset relative to left border
}else { }else {
origin.x=0; origin.x=0;
} }
syncScrollBars(); syncSb();
arrowPainter.setTx(tx); arrowPainter.setTx(tx);
redraw(); redraw();
} }
} }
private void updateScaleFactor(long factor) {
scaleFactor = factor;
scaleMagnitude = 0;
for(int i=Constants.POWERS_OF_TEN.length-1; i>0; i--) {
if(scaleFactor>=Constants.POWERS_OF_TEN[i]) {
scaleMagnitude = i;
break;
}
}
}
@Override @Override
public String timeToString(long time) { public String timeToString(long time) {
int idx = scaleMagnitude/3; int idx = scaleMagnitude/3;
@ -245,7 +255,7 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
} }
public long getTimeForOffset(int xOffset){ public long getTimeForOffset(int xOffset){
return (xOffset-origin.x) * scaleFactor; return (xOffset-origin.x) * getScale();
} }
public void addPainter(IPainter painter) { public void addPainter(IPainter painter) {
@ -265,7 +275,7 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
void clearAllWaveformPainter(boolean update) { void clearAllWaveformPainter(boolean update) {
trackAreaPainter.trackVerticalOffset.clear(); trackAreaPainter.trackVerticalOffset.clear();
wave2painterMap.clear(); wave2painterMap.clear();
if(update) syncScrollBars(); if(update) syncSb();
} }
public void addWaveformPainter(IWaveformPainter painter) { public void addWaveformPainter(IWaveformPainter painter) {
@ -275,7 +285,7 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
void addWaveformPainter(IWaveformPainter painter, boolean update) { void addWaveformPainter(IWaveformPainter painter, boolean update) {
trackAreaPainter.addTrackPainter(painter); trackAreaPainter.addTrackPainter(painter);
wave2painterMap.put(painter.getTrackEntry().waveform, painter); wave2painterMap.put(painter.getTrackEntry().waveform, painter);
if(update) syncScrollBars(); if(update) syncSb();
} }
public List<CursorPainter> getCursorPainters() { public List<CursorPainter> getCursorPainters() {
@ -284,26 +294,41 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
/* Initialize the scrollbar and register listeners. */ /* Initialize the scrollbar and register listeners. */
private void initScrollBars() { private void initScrollBars() {
ScrollBar horizontal = getHorizontalBar();
horizontal.setEnabled(false); horizontal.setEnabled(false);
horizontal.setVisible(true); horizontal.setVisible(true);
horizontal.addSelectionListener(new SelectionAdapter() { horizontal.addSelectionListener(new SelectionAdapter() {
@Override @Override
public void widgetSelected(SelectionEvent event) { public void widgetSelected(SelectionEvent event) {
if (painterList.isEmpty()) if (!painterList.isEmpty()) {
return; int[] sel = horizontal.getSelection();
setOrigin(-((ScrollBar) event.widget).getSelection(), origin.y); long lowerTime = sel[0]*sliderScaleFactor;
long upperTime = sel[1]*sliderScaleFactor;
if(sel[1]-sel[0] != lastHorSelection[1]-lastHorSelection[0]) {
long time_diff = upperTime-lowerTime;
long factor = time_diff/getClientArea().width;
setScalingFactor(factor, lowerTime+time_diff/2);
} else {
origin.x = -(int) (lowerTime/getScale());
WaveformCanvas.this.getDisplay().asyncExec(() -> {redraw();});
}
lastHorSelection=sel;
}
} }
}); });
ScrollBar vertical = getVerticalBar(); horizontal.setMinimum(0);
horizontal.setMaximum(INITIAL_ZOOM_BAR_MAX);
lastHorSelection = horizontal.getSelection();
vertical.setEnabled(false); vertical.setEnabled(false);
vertical.setVisible(true); vertical.setVisible(true);
vertical.addSelectionListener(new SelectionAdapter() { vertical.addSelectionListener(new SelectionAdapter() {
@Override @Override
public void widgetSelected(SelectionEvent event) { public void widgetSelected(SelectionEvent event) {
if (painterList.isEmpty()) if (!painterList.isEmpty()) {
return; origin.y=-vertical.getSelection();
setOrigin(origin.x, -((ScrollBar) event.widget).getSelection()); fireSelectionEvent();
WaveformCanvas.this.getDisplay().asyncExec(() -> {redraw();});
}
} }
}); });
} }
@ -313,50 +338,48 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
* range, it will correct it. This function considers only following factors * range, it will correct it. This function considers only following factors
* :<b> transform, image size, client area</b>. * :<b> transform, image size, client area</b>.
*/ */
public void syncScrollBars() { public void syncSb() {
if (painterList.isEmpty()) { if (!painterList.isEmpty()) {
redraw(); syncHSb();
return; syncVSb();
fireSelectionEvent();
} }
int height = trackAreaPainter.getHeight(); // incl. Ruler redraw();
long width = maxTime / scaleFactor; }
Rectangle clientArea=getClientArea();
ScrollBar horizontal = getHorizontalBar();
horizontal.setIncrement(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 (-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(); private void syncVSb() {
vertical.setIncrement(getClientArea().height / 100); Rectangle clientArea=getClientArea();
vertical.setPageIncrement(getClientArea().height); int height = trackAreaPainter.getHeight(); // incl. Ruler
int clientHeighth = clientArea.height; int clientHeight = clientArea.height;
if (height > clientHeighth) { /* image is higher than client area */ vertical.setIncrement(clientHeight / 100);
vertical.setPageIncrement(clientHeight/2);
if (height > clientHeight) { /* image is higher than client area */
vertical.setMinimum(0); vertical.setMinimum(0);
vertical.setMaximum(height); vertical.setMaximum(height);
vertical.setEnabled(true); vertical.setEnabled(true);
if ( -origin.y > vertical.getMaximum() - clientHeighth) { if ( -origin.y > vertical.getMaximum() - clientHeight) {
origin.y = -vertical.getMaximum() + clientHeighth; origin.y = -vertical.getMaximum() + clientHeight;
} }
} else { /* image is less higher than client area */ } else { /* image is less higher than client area */
vertical.setMaximum(clientHeighth); vertical.setMaximum(clientHeight);
vertical.setEnabled(false); vertical.setEnabled(false);
} }
vertical.setThumb(clientHeighth); vertical.setThumb(clientHeight);
vertical.setSelection(-origin.y); vertical.setSelection(-origin.y);
redraw(); }
fireSelectionEvent();
private void syncHSb() {
horizontal.setEnabled(wave2painterMap.size()>0);
Rectangle clientArea=getClientArea();
int clientWidth = clientArea.width;
if(sliderScaleFactor>0) {
int lower = (int) ( ( -origin.x * getScale()) / sliderScaleFactor);
int upper = (int) (((clientWidth-origin.x) * getScale()) / sliderScaleFactor);
lastHorSelection = new int[] {Math.max(lower,0), Math.min(upper, horizontal.getMaximum())};
horizontal.setSelection(lastHorSelection);
}
long width = maxTime / getScale();
horizontal.setButtonsEnabled(width > clientWidth);
} }
/* Paint function */ /* Paint function */
@ -406,7 +429,7 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
result.add(entry.getValue().getTrackEntry()); result.add(entry.getValue().getTrackEntry());
} }
} else if (p instanceof CursorPainter) { } else if (p instanceof CursorPainter) {
if (Math.abs(point.x - origin.x - ((CursorPainter) p).getTime()/scaleFactor) < 2) { if (Math.abs(point.x - origin.x - ((CursorPainter) p).getTime()/getScale()) < 2) {
result.add(p); result.add(p);
} }
} }
@ -434,11 +457,11 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
} }
public void reveal(ITx tx) { public void reveal(ITx tx) {
int lower = (int) (tx.getBeginTime() / scaleFactor); int lower = (int) (tx.getBeginTime() / getScale());
int higher = (int) (tx.getEndTime() / scaleFactor); int higher = (int) (tx.getEndTime() / getScale());
Point size = getSize(); Point size = getSize();
size.x -= getVerticalBar().getSize().x + 2; size.x -= vertical.getSize().x + 2;
size.y -= getHorizontalBar().getSize().y; size.y -= horizontal.getSize().y;
if (lower < -origin.x) { if (lower < -origin.x) {
setOrigin(-lower, origin.y); setOrigin(-lower, origin.y);
} else if (higher > (size.x - origin.x)) { } else if (higher > (size.x - origin.x)) {
@ -467,9 +490,8 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
if(te.waveform == waveform) { if(te.waveform == waveform) {
Point size = getSize(); Point size = getSize();
size.y -=+rulerHeight; size.y -=+rulerHeight;
ScrollBar sb = getHorizontalBar(); if((horizontal.getStyle()&SWT.SCROLLBAR_OVERLAY)!=0 && horizontal.isVisible())
if((sb.getStyle()&SWT.SCROLLBAR_OVERLAY)!=0 && sb.isVisible()) size.y-= horizontal.getSize().y;
size.y-= getHorizontalBar().getSize().y;
int top = te.vOffset; int top = te.vOffset;
int bottom = top + styleProvider.getTrackHeight(); int bottom = top + styleProvider.getTrackHeight();
if (top < -origin.y) { if (top < -origin.y) {
@ -482,10 +504,10 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
} }
public void reveal(long time) { public void reveal(long time) {
int scaledTime = (int) (time / scaleFactor); int scaledTime = (int) (time / getScale());
Point size = getSize(); Point size = getSize();
size.x -= getVerticalBar().getSize().x + 2; size.x -= vertical.getSize().x + 2;
size.y -= getHorizontalBar().getSize().y; size.y -= horizontal.getSize().y;
if (scaledTime < -origin.x) { if (scaledTime < -origin.x) {
setOrigin(-scaledTime+10, origin.y); setOrigin(-scaledTime+10, origin.y);
} else if (scaledTime > (size.x - origin.x)) { } else if (scaledTime > (size.x - origin.x)) {
@ -495,7 +517,7 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
@Override @Override
public void centerAt(long time) { public void centerAt(long time) {
int scaledTime = (int) (time / scaleFactor); int scaledTime = (int) (time / getScale());
int newX = -scaledTime+getWidth()/2; int newX = -scaledTime+getWidth()/2;
setOrigin(newX>0?0:newX, origin.y); setOrigin(newX>0?0:newX, origin.y);
} }
@ -535,12 +557,12 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
@Override @Override
public long getMaxVisibleTime() { public long getMaxVisibleTime() {
return (getClientArea().width-origin.x)*scaleFactor; return (getClientArea().width-origin.x)*getScale();
} }
@Override @Override
public long getMinVisibleTime() { public long getMinVisibleTime() {
return -origin.x * scaleFactor; return -origin.x * getScale();
} }
@Override @Override
@ -548,7 +570,7 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{
long duration = getMaxVisibleTime()-getMinVisibleTime(); long duration = getMaxVisibleTime()-getMinVisibleTime();
if(time>0) { if(time>0) {
if((time+duration)<getMaxTime()) { if((time+duration)<getMaxTime()) {
int scaledTime = (int) (time / scaleFactor); int scaledTime = (int) (time / getScale());
setOrigin(-scaledTime, origin.y); setOrigin(-scaledTime, origin.y);
} }
} else { } else {

View File

@ -91,6 +91,7 @@ import com.minres.scviewer.database.ui.IWaveformStyleProvider;
import com.minres.scviewer.database.ui.IWaveformView; import com.minres.scviewer.database.ui.IWaveformView;
import com.minres.scviewer.database.ui.IWaveformZoom; import com.minres.scviewer.database.ui.IWaveformZoom;
import com.minres.scviewer.database.ui.TrackEntry; import com.minres.scviewer.database.ui.TrackEntry;
import com.minres.scviewer.database.ui.swt.internal.slider.ZoomBar;
public class WaveformView implements IWaveformView { public class WaveformView implements IWaveformView {
@ -112,6 +113,10 @@ public class WaveformView implements IWaveformView {
private final Canvas valueList; private final Canvas valueList;
private final Control nameFill;
private final Control valueFill;
final WaveformCanvas waveformCanvas; final WaveformCanvas waveformCanvas;
final ToolTipHandler toolTipHandler; final ToolTipHandler toolTipHandler;
@ -204,7 +209,7 @@ public class WaveformView implements IWaveformView {
long endTime = waveformCanvas.getTimeForOffset(end.x); long endTime = waveformCanvas.getTimeForOffset(end.x);
if(startTime<endTime) { if(startTime<endTime) {
waveformCanvas.setVisibleRange(startTime, endTime); waveformCanvas.setVisibleRange(startTime, endTime);
} else { } else if(start.x!=end.x){
long targetTimeRange = startTime-endTime; long targetTimeRange = startTime-endTime;
long currentTimeRange = waveformCanvas.getMaxVisibleTime() - waveformCanvas.getMinVisibleTime(); long currentTimeRange = waveformCanvas.getMaxVisibleTime() - waveformCanvas.getMinVisibleTime();
long factor = currentTimeRange/targetTimeRange *waveformCanvas.getScale(); long factor = currentTimeRange/targetTimeRange *waveformCanvas.getScale();
@ -266,10 +271,23 @@ public class WaveformView implements IWaveformView {
switch (e.type) { switch (e.type) {
case SWT.MouseWheel: case SWT.MouseWheel:
if((e.stateMask & SWT.CTRL) != 0) { if((e.stateMask & SWT.CTRL) != 0) {
if(e.count<0) if(e.count<0) // up scroll
waveformCanvas.setScale(waveformCanvas.getScale()*11/10); waveformCanvas.setScale(waveformCanvas.getScale()*11/10);
else else // down scroll
waveformCanvas.setScale(waveformCanvas.getScale()*10/11); waveformCanvas.setScale(waveformCanvas.getScale()*10/11);
e.doit=false;
} else if((e.stateMask & SWT.SHIFT) != 0) {
long upper = waveformCanvas.getMaxVisibleTime();
long lower = waveformCanvas.getMinVisibleTime();
long duration = upper-lower;
if(e.count<0) { // up scroll
long newLower = Math.min(waveformCanvas.getMaxTime()-duration, lower+duration/10);
waveformCanvas.setMinVisibleTime(newLower);
} else {// down scroll
long newLower = Math.max(0, lower-duration/10);
waveformCanvas.setMinVisibleTime(newLower);
}
e.doit=false;
} }
break; break;
case SWT.MouseDown: case SWT.MouseDown:
@ -330,7 +348,25 @@ public class WaveformView implements IWaveformView {
rightSash.setBackground(SWTResourceManager.getColor(SWT.COLOR_GRAY)); rightSash.setBackground(SWTResourceManager.getColor(SWT.COLOR_GRAY));
Composite valuePane = new Composite(rightSash, SWT.NONE); Composite valuePane = new Composite(rightSash, SWT.NONE);
waveformCanvas = new WaveformCanvas(rightSash, SWT.NONE, styleProvider);
Composite waveformPane = new Composite(rightSash, SWT.NONE);
GridLayout gl_waveformPane = new GridLayout(1, false);
gl_waveformPane.verticalSpacing = 0;
gl_waveformPane.marginWidth = 0;
gl_waveformPane.marginHeight = 0;
waveformPane.setLayout(gl_waveformPane);
waveformCanvas = new WaveformCanvas(waveformPane, SWT.NONE | SWT.V_SCROLL /*| SWT.H_SCROLL*/, styleProvider, new ZoomBar.IProvider() {
@Override
public ZoomBar getScrollBar() {
ZoomBar timeSliderPane = new ZoomBar(waveformPane, SWT.NONE);
GridData gd_timeSliderPane = new GridData(SWT.FILL, SWT.BOTTOM, false, false, 1, 1);
timeSliderPane.setLayoutData(gd_timeSliderPane);
return timeSliderPane;
}
});
waveformCanvas.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
// create the name pane // create the name pane
createTextPane(namePane, "Name"); createTextPane(namePane, "Name");
@ -347,7 +383,10 @@ public class WaveformView implements IWaveformView {
@Override @Override
public void controlResized(ControlEvent e) { public void controlResized(ControlEvent e) {
nameListScrolled.getVerticalBar().setVisible(false); nameListScrolled.getVerticalBar().setVisible(false);
if(nameListScrolled.getSize().y == nameList.getSize().y) {
((GridData)nameFill.getLayoutData()).heightHint=18;
namePane.layout();
}
} }
}); });
nameList = new Canvas(nameListScrolled, SWT.NONE) { nameList = new Canvas(nameListScrolled, SWT.NONE) {
@ -366,6 +405,7 @@ public class WaveformView implements IWaveformView {
}); });
nameList.addMouseListener(nameValueMouseListener); nameList.addMouseListener(nameValueMouseListener);
nameListScrolled.setContent(nameList); nameListScrolled.setContent(nameList);
nameFill = createFill(namePane);
createTextPane(valuePane, "Value"); createTextPane(valuePane, "Value");
@ -379,7 +419,10 @@ public class WaveformView implements IWaveformView {
@Override @Override
public void controlResized(ControlEvent e) { public void controlResized(ControlEvent e) {
valueListScrolled.getVerticalBar().setVisible(false); valueListScrolled.getVerticalBar().setVisible(false);
if(valueListScrolled.getSize().y == valueList.getSize().y) {
((GridData)valueFill.getLayoutData()).heightHint=18;
valuePane.layout();
}
} }
}); });
valueList = new Canvas(valueListScrolled, SWT.NONE) { valueList = new Canvas(valueListScrolled, SWT.NONE) {
@ -398,6 +441,7 @@ public class WaveformView implements IWaveformView {
}); });
valueList.addMouseListener(nameValueMouseListener); valueList.addMouseListener(nameValueMouseListener);
valueListScrolled.setContent(valueList); valueListScrolled.setContent(valueList);
valueFill = createFill(valuePane);
waveformCanvas.setMaxTime(1); waveformCanvas.setMaxTime(1);
waveformCanvas.addPaintListener(waveformMouseListener); waveformCanvas.addPaintListener(waveformMouseListener);
@ -457,6 +501,15 @@ public class WaveformView implements IWaveformView {
}); });
} }
private Control createFill(Composite pane) {
Label cLabel = new Label(pane, SWT.NONE);
cLabel.setBackground(SWTResourceManager.getColor(SWT.COLOR_WIDGET_BACKGROUND));
GridData gd_cLabel = new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1);
gd_cLabel.heightHint = 0;
cLabel.setLayoutData(gd_cLabel);
return cLabel;
}
private void createTextPane(Composite namePane, String text) { private void createTextPane(Composite namePane, String text) {
GridLayout glNamePane = new GridLayout(1, false); GridLayout glNamePane = new GridLayout(1, false);
glNamePane.verticalSpacing = 0; glNamePane.verticalSpacing = 0;
@ -522,7 +575,7 @@ public class WaveformView implements IWaveformView {
tracksVerticalHeight += streamEntry.height; tracksVerticalHeight += streamEntry.height;
even = !even; even = !even;
} }
waveformCanvas.syncScrollBars(); waveformCanvas.syncSb();
nameList.setSize(nameMaxWidth + 15, tracksVerticalHeight); nameList.setSize(nameMaxWidth + 15, tracksVerticalHeight);
nameListScrolled.setMinSize(nameMaxWidth + 15, tracksVerticalHeight); nameListScrolled.setMinSize(nameMaxWidth + 15, tracksVerticalHeight);
nameList.redraw(); nameList.redraw();

View File

@ -0,0 +1,35 @@
package com.minres.scviewer.database.ui.swt.internal.slider;
import org.eclipse.swt.widgets.Display;
public class ActionTimer implements Runnable {
public static final int INITIAL_DELAY = 300;
public static final int FAST_DELAY = 50;
private final Display display;
private final TimerAction timerAction;
public interface TimerAction extends Runnable {
boolean isEnabled();
}
public ActionTimer( TimerAction timerAction, Display display ) {
this.display = display;
this.timerAction = timerAction;
}
public void activate() {
if( timerAction.isEnabled() ) {
display.timerExec( INITIAL_DELAY, this );
}
}
@Override
public void run() {
if( timerAction.isEnabled() ) {
timerAction.run();
display.timerExec( FAST_DELAY, this );
}
}
}

View File

@ -0,0 +1,192 @@
package com.minres.scviewer.database.ui.swt.internal.slider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
public class ImageButton extends Composite
{
private Image hoverImage;
private Image normalImage;
private Image pressedImage;
private Image disabledImage;
private int width;
private int height;
private boolean hover;
private boolean pressed;
private boolean autoFire;
private ActionTimer actionTimer;
private ActionTimer.TimerAction timerAction;
public ImageButton(Composite parent, int style) {
super(parent, style);
timerAction = new ActionTimer.TimerAction() {
@Override
public void run() {
notifyListeners();
}
@Override
public boolean isEnabled() {
return pressed;
}
};
actionTimer = new ActionTimer(timerAction, this.getDisplay() );
addListener(SWT.Dispose, event -> {
if (hoverImage != null) hoverImage.dispose();
if (normalImage != null) normalImage.dispose();
if (pressedImage != null) pressedImage.dispose();
if (disabledImage != null) disabledImage.dispose();
});
addListener(SWT.Paint, event -> {
paintControl(event);
});
addMouseTrackListener(new MouseTrackAdapter() {
public void mouseEnter(MouseEvent arg0) {
if(isEnabled()) {
hover=true;
redraw();
}
}
public void mouseExit(MouseEvent arg0) {
if(isEnabled()) {
hover=false;
redraw();
}
}
});
addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
if(isEnabled()) {
pressed=true;
notifyListeners();
if(autoFire) actionTimer.activate();
redraw();
}
}
@Override
public void mouseUp(MouseEvent e) {
pressed=false;
redraw();
}
});
}
private void paintControl(Event event) {
GC gc = event.gc;
if (hoverImage != null) {
if(pressed)
gc.drawImage(pressedImage, 1, 1);
else if(hover) {
gc.drawImage(hoverImage, 1, 1);
} else if(isEnabled()){
gc.drawImage(normalImage, 1, 1);
} else
gc.drawImage(disabledImage, 1, 1);
}
}
public void setImage(Image[] imgs) {
assert(imgs.length==3);
Display d = Display.getDefault();
normalImage = new Image(d, imgs[0], SWT.IMAGE_COPY);
hoverImage = new Image(d, imgs[1], SWT.IMAGE_COPY);
pressedImage = new Image(d, imgs[2], SWT.IMAGE_COPY);
disabledImage = new Image(d, imgs[0], SWT.IMAGE_DISABLE);
width = imgs[0].getBounds().width;
height = imgs[0].getBounds().height;
redraw();
}
@Override
public Point computeSize(int wHint, int hHint, boolean changed) {
int overallWidth = width;
int overallHeight = height;
if (wHint != SWT.DEFAULT && wHint < overallWidth)
overallWidth = wHint;
if (hHint != SWT.DEFAULT && hHint < overallHeight)
overallHeight = hHint;
return new Point(overallWidth + 2, overallHeight + 2);
}
/**
* Adds the listener to the collection of listeners who will be notified when
* the user changes the receiver's value, by sending it one of the messages
* defined in the <code>SelectionListener</code> interface.
* <p>
* <code>widgetSelected</code> is called when the user changes the receiver's
* value. <code>widgetDefaultSelected</code> is not called.
* </p>
*
* @param listener the listener which should be notified
*
* @exception IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*
* @see SelectionListener
* @see #removeSelectionListener
*/
public void addSelectionListener(final SelectionListener listener) {
checkWidget();
SelectionListenerUtil.addSelectionListener(this, listener);
}
/**
* Removes the listener from the collection of listeners who will be notified
* when the user changes the receiver's value.
*
* @param listener the listener which should no longer be notified
*
* @exception IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
* </ul>
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*
* @see SelectionListener
* @see #addSelectionListener
*/
public void removeSelectionListener(final SelectionListener listener) {
checkWidget();
SelectionListenerUtil.removeSelectionListener(this, listener);
}
private void notifyListeners() {
Event e = new Event();
e.widget=this;
e.type=SWT.Selection;
SelectionListenerUtil.fireSelectionListeners(this,e);
}
public boolean isAutoFire() {
return autoFire;
}
public void setAutoFire(boolean autoFire) {
this.autoFire = autoFire;
}
}

View File

@ -0,0 +1,631 @@
package com.minres.scviewer.database.ui.swt.internal.slider;
import java.text.Format;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.wb.swt.SWTResourceManager;
public class RangeSlider extends Canvas {
private static final int NONE = 0;
private static final int UPPER = 1 << 0;
private static final int LOWER = 1 << 1;
private static final int BOTH = UPPER | LOWER;
private final int minHeight;
private final int markerWidth;
private final int thumbWidth = 0;
private final Image[] slider, sliderHover, sliderDrag;
private int minimum;
private int maximum;
private int lowerValue;
private int upperValue;
private int increment;
private int pageIncrement;
private int selectedElement;
private boolean upperHover, lowerHover;
private int previousUpperValue, previousLowerValue;
private int startDragUpperValue, startDragLowerValue;
private Point startDragPoint;
private final boolean isFullSelection=false;
private final boolean isHighQuality;
private final boolean isOn;
private Format toolTipFormatter;
private String clientToolTipText;
private StringBuffer toolTip;
private Point coordUpper;
private Point coordLower;
public RangeSlider(final Composite parent, final int style) {
super(parent, SWT.DOUBLE_BUFFERED | ((style & SWT.BORDER) == SWT.BORDER ? SWT.BORDER : SWT.NONE));
slider = new Image[] {
SWTResourceManager.getImage(this.getClass(), "marker_l.png"),
SWTResourceManager.getImage(this.getClass(), "marker_r.png")};
sliderHover = new Image[] {
SWTResourceManager.getImage(this.getClass(), "marker_l_hover.png"),
SWTResourceManager.getImage(this.getClass(), "marker_r_hover.png")};
sliderDrag = new Image[] {
SWTResourceManager.getImage(this.getClass(), "marker_l_pressed.png"),
SWTResourceManager.getImage(this.getClass(), "marker_r_pressed.png")};
Rectangle imgSize = slider[0].getBounds();
minHeight =imgSize.height+2;
markerWidth = imgSize.width;
minimum = lowerValue = 0;
maximum = upperValue = 100;
increment = 1;
pageIncrement = 10;
isHighQuality = (style & SWT.HIGH) == SWT.HIGH;
isOn = (style & SWT.ON) == SWT.ON;
selectedElement = NONE;
addMouseListeners();
addListener(SWT.Resize, event -> {
});
addListener(SWT.KeyDown, event -> {
handleKeyDown(event);
});
addPaintListener(event -> {
drawWidget(event);
});
}
@Override
public int getStyle() {
return super.getStyle() | //
(isOn ? SWT.ON : SWT.NONE) | //
(isFullSelection ? SWT.CONTROL : SWT.NONE) | //
(isHighQuality ? SWT.HIGH : SWT.NONE);
}
private void addMouseListeners() {
addListener(SWT.MouseDown, e -> {
if (e.button == 1 || e.button == 2) {
selectKnobs(e);
selectedElement = (lowerHover ? LOWER : NONE) | (upperHover ? UPPER : NONE);
if (selectedElement!=NONE) {
if((e.stateMask & SWT.CTRL)==0 && e.button != 2)
selectedElement=BOTH;
startDragLowerValue = previousLowerValue = lowerValue;
startDragUpperValue = previousUpperValue = upperValue;
startDragPoint = new Point(e.x, e.y);
}
}
});
addListener(SWT.MouseUp, e -> {
if (selectedElement!=NONE) {
startDragPoint = null;
validateNewValues(e);
super.setToolTipText(clientToolTipText);
selectedElement=NONE;
redraw();
} else if (e.button == 1) {
if(e.x<coordLower.x) {
translateValues(-pageIncrement);
validateNewValues(e);
redraw();
} else if(e.x>coordUpper.x+markerWidth) {
translateValues(pageIncrement);
validateNewValues(e);
redraw();
}
}
});
addListener(SWT.MouseDoubleClick, event -> {
handleMouseDoubleClick(event);
});
addListener(SWT.MouseMove, event -> {
handleMouseMove(event);
});
addListener(SWT.MouseWheel, event -> {
handleMouseWheel(event);
});
addListener(SWT.MouseHover, event -> {
handleMouseHover(event);
});
addMouseTrackListener(new MouseTrackAdapter() {
public void mouseExit(MouseEvent event) {
lowerHover = upperHover = false;
redraw();
}
});
}
private void validateNewValues(final Event e) {
if (upperValue != previousUpperValue || lowerValue != previousLowerValue) {
if (!SelectionListenerUtil.fireSelectionListeners(this,e)) {
upperValue = previousUpperValue;
lowerValue = previousLowerValue;
}
previousUpperValue = upperValue;
previousLowerValue = lowerValue;
increment = Math.max(1, (upperValue-lowerValue)/100);
pageIncrement = Math.max(1, (upperValue-lowerValue)/2);
redraw();
}
}
private boolean busy = false;
private void handleMouseMove(final Event e) {
if (selectedElement==NONE) {
final boolean wasUpper = upperHover;
final boolean wasLower = lowerHover;
selectKnobs(e);
if (wasUpper != upperHover || wasLower != lowerHover) {
redraw();
}
} else { // dragInProgress
final int x = e.x;
if (selectedElement == BOTH) {
final int diff = (int) ((startDragPoint.x - x) / computePixelSizeForSlider()) + minimum;
int newUpper = startDragUpperValue - diff;
int newLower = startDragLowerValue - diff;
if (newUpper > maximum) {
newUpper = maximum;
newLower = maximum - (startDragUpperValue - startDragLowerValue);
} else if (newLower < minimum) {
newLower = minimum;
newUpper = minimum + startDragUpperValue - startDragLowerValue;
}
upperValue = newUpper;
lowerValue = newLower;
handleToolTip(lowerValue, upperValue);
} else if (selectedElement == UPPER) {
upperValue = (int) Math.round((double)(x - markerWidth) / computePixelSizeForSlider()) + minimum;
checkUpperValue();
handleToolTip(upperValue);
} else if (selectedElement == LOWER){
lowerValue = (int) Math.round((double)(x - markerWidth) / computePixelSizeForSlider()) + minimum;
checkLowerValue();
handleToolTip(lowerValue);
}
if (isOn && !busy) {
validateNewValues(e);
busy=true;
getDisplay().timerExec(50, ()->{busy=false;});
} else {
redraw();
}
}
}
private boolean isBetweenKnobs(int x, int y) {
return x < coordUpper.x && x > coordLower.x && y >= minHeight/3 && y <= minHeight/3 + getClientArea().height - 2*minHeight/3;
}
private void selectKnobs(final Event e) {
if (coordLower != null) {
final Rectangle imgBounds = slider[0].getBounds();
final int x = e.x, y = e.y;
lowerHover = x >= coordLower.x && x <= coordLower.x + imgBounds.width && y >= coordLower.y && y <= coordLower.y + imgBounds.height;
upperHover = ((e.stateMask & (SWT.CTRL | SWT.SHIFT)) != 0 || !lowerHover) && //
x >= coordUpper.x && x <= coordUpper.x + imgBounds.width && //
y >= coordUpper.y && y <= coordUpper.y + imgBounds.height;
lowerHover &= (e.stateMask & SWT.CTRL) != 0 || !upperHover;
if (!lowerHover && !upperHover && isBetweenKnobs(x, y)) {
lowerHover = upperHover = true;
}
}
}
private int getCursorValue(int x, int y) {
int value = -1;
final Rectangle clientArea = getClientArea();
if (x < clientArea.width - 2*markerWidth && x >= markerWidth && y >= minHeight/3 && y <= clientArea.height - minHeight/3) {
value = (int) Math.round((x - 9d) / computePixelSizeForSlider()) + minimum;
}
return value;
}
private void handleMouseDoubleClick(final Event e) {
final int value = getCursorValue(e.x, e.y);
if (value >= 0) {
if (value > upperValue) {
translateValues(value-upperValue);
} else if (value < lowerValue) {
translateValues(value-lowerValue);
}
validateNewValues(e);
}
}
private void handleToolTip(int... values) {
if (toolTipFormatter != null) {
try {
if (values.length == 1) {
toolTip.setLength(0);
toolTipFormatter.format(values[0], toolTip, null);
super.setToolTipText(toolTip.toString());
} else if (values.length == 2) {
toolTip.setLength(0);
toolTipFormatter.format(values[0], toolTip, null);
toolTip.append(" \u2194 "); // LEFT RIGHT ARROW
toolTipFormatter.format(values[1], toolTip, null);
super.setToolTipText(toolTip.toString());
}
} catch (final IllegalArgumentException ex) {
super.setToolTipText(clientToolTipText);
}
}
}
private void handleMouseHover(final Event e) {
if (selectedElement!=NONE && toolTipFormatter != null) {
final int value = getCursorValue(e.x, e.y);
if (value >= 0) {
try {
toolTip.setLength(0);
toolTipFormatter.format(value, toolTip, null);
super.setToolTipText(toolTip.toString());
} catch (final IllegalArgumentException ex) {
super.setToolTipText(clientToolTipText);
}
} else {
super.setToolTipText(clientToolTipText);
}
}
}
public void setToolTipFormatter(Format formatter) {
toolTip = formatter != null ? new StringBuffer() : null;
toolTipFormatter = formatter;
}
@Override
public void setToolTipText(String string) {
super.setToolTipText(clientToolTipText = string);
}
private void handleMouseWheel(final Event e) {
previousLowerValue = lowerValue;
previousUpperValue = upperValue;
final int amount = Math.max(1, ((e.stateMask & SWT.SHIFT) != 0 ? (upperValue-lowerValue)/6 : (upperValue-lowerValue)/15));
if ((e.stateMask&SWT.CTRL)==0) {
int newLower = lowerValue + e.count * amount;
int newUpper = upperValue + e.count * amount;
if (newUpper > maximum) {
newUpper = maximum;
newLower = maximum - (upperValue - lowerValue);
} else if (newLower < minimum) {
newLower = minimum;
newUpper = minimum + upperValue - lowerValue;
}
upperValue = newUpper;
lowerValue = newLower;
} else {
int newLower = lowerValue + e.count * amount/2;
int newUpper = upperValue - e.count * amount/2;
int dist = newUpper - newLower;
if (newUpper > maximum) {
newUpper = maximum;
newLower = maximum - dist;
} else if (newLower < minimum) {
newLower = minimum;
newUpper = minimum + dist;
}
if(newUpper<=newLower) {
newLower=lowerValue + (upperValue - lowerValue)/2;
newUpper=newLower+1;
}
upperValue = newUpper;
lowerValue = newLower;
}
validateNewValues(e);
e.doit = false; // we are consuming this event
}
private void checkLowerValue() {
if (lowerValue < minimum) {
lowerValue = minimum;
} else if (lowerValue > (upperValue-thumbWidth)) {
lowerValue = (upperValue-thumbWidth);
}
}
private void checkUpperValue() {
if (upperValue > maximum) {
upperValue = maximum;
} else if (upperValue < (lowerValue+thumbWidth)) {
upperValue = lowerValue+thumbWidth;
}
}
private float computePixelSizeForSlider() {
return (getClientArea().width - 2.0f*markerWidth) / (maximum - minimum);
}
private void drawWidget(final PaintEvent e) {
final Rectangle rect = getClientArea();
if (rect.width == 0 || rect.height == 0) {
return;
}
e.gc.setAdvanced(true);
e.gc.setAntialias(SWT.ON);
drawBackground(e.gc);
if (lowerHover || (selectedElement & LOWER) != 0) {
coordUpper = drawMarker(e.gc, upperValue, true);
coordLower = drawMarker(e.gc, lowerValue, false);
} else {
coordLower = drawMarker(e.gc, lowerValue, false);
coordUpper = drawMarker(e.gc, upperValue, true);
}
}
private void drawBackground(final GC gc) {
final Rectangle clientArea = getClientArea();
gc.setBackground(getBackground());
gc.fillRectangle(clientArea);
if (isEnabled()) {
gc.setForeground(getForeground());
} else {
gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_GRAY));
}
gc.drawRoundRectangle(markerWidth, minHeight/3, clientArea.width - 2*markerWidth, clientArea.height - 2*minHeight/3, 3, 3);
final float pixelSize = computePixelSizeForSlider();
final int startX = (int) (pixelSize * lowerValue);
final int endX = (int) (pixelSize * upperValue);
if (isEnabled()) {
gc.setBackground(getForeground());
} else {
gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GRAY));
}
gc.fillRectangle(markerWidth+startX, minHeight/3, endX - startX, clientArea.height - 2*minHeight/3);
}
private Point drawMarker(final GC gc, final int value, final boolean upper) {
final float pixelSize = computePixelSizeForSlider();
int x = (int) (pixelSize * value);
final int idx = upper?1:0;
Image image;
if (upper) {
if (upperHover) {
image = (selectedElement & UPPER) != 0 ? sliderDrag[idx] : sliderHover[idx];
} else {
image = slider[idx];
}
} else {
if (lowerHover) {
image = (selectedElement & LOWER) != 0 ? sliderDrag[idx] : sliderHover[idx];
} else {
image = slider[idx];
}
}
if(upper)
x+=slider[idx].getBounds().width;
if (isEnabled()) {
gc.drawImage(image, x, getClientArea().height / 2 - slider[idx].getBounds().height / 2);
} else {
final Image temp = new Image(getDisplay(), image, SWT.IMAGE_DISABLE);
gc.drawImage(temp, x, getClientArea().height / 2 - slider[idx].getBounds().height / 2);
temp.dispose();
}
return new Point(x, getClientArea().height / 2 - slider[idx].getBounds().height / 2);
}
private void moveCursorPosition(int xDelta, int yDelta) {
final Point cursorPosition = getDisplay().getCursorLocation();
cursorPosition.x += xDelta;
cursorPosition.y += yDelta;
getDisplay().setCursorLocation(cursorPosition);
}
private void handleKeyDown(final Event event) {
int accelerator = (event.stateMask & SWT.SHIFT) != 0 ? 10 : (event.stateMask & SWT.CTRL) != 0 ? 2 : 1;
if (selectedElement != NONE) {
switch (event.keyCode) {
case SWT.ESC:
startDragPoint = null;
upperValue = startDragUpperValue;
lowerValue = startDragLowerValue;
validateNewValues(event);
selectedElement = NONE;
if (!isOn) {
redraw();
}
event.doit = false;
break;
case SWT.ARROW_UP:
accelerator = -accelerator;
case SWT.ARROW_LEFT:
moveCursorPosition(-accelerator, 0);
event.doit = false;
break;
case SWT.ARROW_DOWN:
accelerator = -accelerator;
case SWT.ARROW_RIGHT:
moveCursorPosition(accelerator, 0);
event.doit = false;
break;
}
return;
}
previousLowerValue = lowerValue;
previousUpperValue = upperValue;
switch (event.keyCode) {
case SWT.HOME:
if ((event.stateMask & (SWT.SHIFT| SWT.CTRL)) == 0) {
upperValue = minimum + upperValue - lowerValue;
lowerValue = minimum;
}
break;
case SWT.END:
if ((event.stateMask & (SWT.SHIFT| SWT.CTRL)) == 0) {
lowerValue = maximum - (upperValue - lowerValue);
upperValue = maximum;
}
break;
case SWT.PAGE_UP:
translateValues(-accelerator * pageIncrement);
break;
case SWT.PAGE_DOWN:
translateValues( accelerator * pageIncrement);
break;
case SWT.ARROW_DOWN:
case SWT.ARROW_RIGHT:
translateValues( accelerator * increment);
break;
case SWT.ARROW_UP:
case SWT.ARROW_LEFT:
translateValues(-accelerator * increment);
break;
}
if (previousLowerValue != lowerValue || previousUpperValue != upperValue) {
checkLowerValue();
checkUpperValue();
validateNewValues(event);
}
}
private void translateValues(int amount) {
int newLower = lowerValue + amount;
int newUpper = upperValue + amount;
if (newUpper > maximum) {
newUpper = maximum;
newLower = maximum - (upperValue - lowerValue);
} else if (newLower < minimum) {
newLower = minimum;
newUpper = minimum + upperValue - lowerValue;
}
upperValue = newUpper;
lowerValue = newLower;
}
public void addSelectionListener(final SelectionListener listener) {
checkWidget();
SelectionListenerUtil.addSelectionListener(this, listener);
}
public void removeSelectionListener(final SelectionListener listener) {
checkWidget();
SelectionListenerUtil.removeSelectionListener(this, listener);
}
@Override
public Point computeSize(final int wHint, final int hHint, final boolean changed) {
checkWidget();
final int width = Math.max(2*markerWidth+100, wHint);
final int height = Math.max(minHeight, hHint);
return new Point(width, height);
}
public int[] getSelection() {
checkWidget();
return new int[] {lowerValue, upperValue};
}
public int getIncrement() {
checkWidget();
return increment;
}
public int getMaximum() {
checkWidget();
return maximum;
}
public int getMinimum() {
checkWidget();
return minimum;
}
public int getPageIncrement() {
checkWidget();
return pageIncrement;
}
public void setMaximum(final int value) {
setLimits(minimum, value);
}
public void setMinimum(final int value) {
setLimits(value, maximum);
}
public void setLimits(final int min, final int max) {
checkWidget();
if (min >= 0 && min < max && (min != minimum || max != maximum)) {
minimum = min;
maximum = max;
if (lowerValue < minimum) {
lowerValue = minimum;
} else if (lowerValue > maximum) {
lowerValue = maximum;
}
if (upperValue < minimum) {
upperValue = minimum;
} else if (upperValue > maximum) {
upperValue = maximum;
}
redraw();
}
}
public int getUpperValue() {
checkWidget();
return upperValue;
}
public void setUpperValue(final int value) {
setValues(lowerValue, value);
}
public int getLowerValue() {
checkWidget();
return lowerValue;
}
public void setLowerValue(final int value) {
setValues(value, upperValue);
}
public void setValues(final int[] values) {
if (values.length == 2) {
setValues(values[0], values[1]);
}
}
public void setValues(final int lowerValue, final int upperValue) {
setValues(lowerValue, upperValue, false);
}
public void setValues(final int lowerValue, final int upperValue, boolean update) {
checkWidget();
if (lowerValue <= upperValue && lowerValue >= minimum && upperValue <= maximum && (this.lowerValue != lowerValue || this.upperValue != upperValue)) {
this.lowerValue = lowerValue;
this.upperValue = upperValue;
if(update) {
Event e = new Event();
e.type=SWT.Selection;
e.doit=true;
validateNewValues(e);
} else {
increment = Math.max(1, (upperValue-lowerValue)/100);
pageIncrement = Math.max(1, (upperValue-lowerValue)/2);
}
redraw();
}
}
}

View File

@ -0,0 +1,48 @@
package com.minres.scviewer.database.ui.swt.internal.slider;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionUtils {
/**
* Call a method using introspection (so ones can call a private or protected method)
* @param object object on which the method will be called
* @param methodName method name
* @param args arguments of this method (can be null)
* @return the value returned by this method (if this method returns a value)
*/
public static Object callMethod(final Object object, final String methodName, final Object... args) {
if (object == null) {
return null;
}
final Class<?>[] array = new Class<?>[args == null ? 0 : args.length];
int index = 0;
if (args != null) {
for (final Object o : args) {
array[index++] = o == null ? Object.class : o.getClass();
}
}
return callMethodWithClassType(object, methodName, array, args);
}
private static Object callMethodWithClassType(final Object object, final String methodName, final Class<?>[] array, final Object... args) {
Class<?> currentClass = object.getClass();
Method method = null;
while (currentClass != null) {
try {
method = currentClass.getDeclaredMethod(methodName, array);
break;
} catch (final NoSuchMethodException nsme) {
currentClass = currentClass.getSuperclass();
}
}
try {
method.setAccessible(true);
return method.invoke(object, args);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,74 @@
package com.minres.scviewer.database.ui.swt.internal.slider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.TypedListener;
public class SelectionListenerUtil {
/**
* Add a <code>SelectionListener</code> to a given Control
*
* @param control control on which the selection listener is added
* @param listener listener to add
*/
public static void addSelectionListener(final Control control, final SelectionListener listener) {
if (listener == null) {
SWT.error(SWT.ERROR_NULL_ARGUMENT);
}
TypedListener typedListener = new TypedListener(listener);
control.addListener(SWT.Selection, typedListener);
}
/**
* Remove a <code>SelectionListener</code> of a given Control
*
* @param control control on which the selection listener is removed
* @param listener listener to remove
*/
public static void removeSelectionListener(final Control control, final SelectionListener listener) {
if (listener == null) {
SWT.error(SWT.ERROR_NULL_ARGUMENT);
}
final Listener[] listeners = control.getListeners(SWT.Selection);
for (Listener l : listeners) {
if (l instanceof TypedListener) {
TypedListener typedListener = (TypedListener) l;
if (typedListener.getEventListener() == listener) {
ReflectionUtils.callMethod(control, "removeListener", SWT.Selection, ((TypedListener) l).getEventListener());
return;
}
}
}
}
/**
* Fire the selection listeners of a given control
*
* @param control the control that fires the event
* @param sourceEvent mouse event
* @return true if the selection could be changed, false otherwise
*/
public static boolean fireSelectionListeners(final Control control, final Event sourceEvent) {
for (final Listener listener : control.getListeners(SWT.Selection)) {
final Event event = new Event();
event.button = sourceEvent==null?1:sourceEvent.button;
event.display = control.getDisplay();
event.item = null;
event.widget = control;
event.data = sourceEvent == null ? null : sourceEvent.data;
event.time = sourceEvent == null ? 0 : sourceEvent.time;
event.x = sourceEvent == null ? 0 : sourceEvent.x;
event.y = sourceEvent == null ? 0 : sourceEvent.y;
event.type = SWT.Selection;
listener.handleEvent(event);
if (!event.doit) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,127 @@
package com.minres.scviewer.database.ui.swt.internal.slider;
import java.text.Format;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.wb.swt.SWTResourceManager;
public class ZoomBar extends Composite {
static public interface IProvider {
ZoomBar getScrollBar();
}
final RangeSlider timeSlider;
final ImageButton leftButton;
final ImageButton rightButton;
/**
* Create the composite.
* @param parent
* @param style
*/
public ZoomBar(Composite parent, int style) {
super(parent, SWT.NO_FOCUS);
GridLayout gridLayout = new GridLayout(3, false);
gridLayout.horizontalSpacing = 0;
gridLayout.verticalSpacing = 0;
gridLayout.marginWidth = 0;
gridLayout.marginHeight = 0;
setLayout(gridLayout);
leftButton = new ImageButton(this, SWT.NONE);
GridData gd_leftButton = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
gd_leftButton.widthHint=14;
gd_leftButton.heightHint=18;
leftButton.setLayoutData(gd_leftButton);
leftButton.setImage(new Image[] {
SWTResourceManager.getImage(this.getClass(), "arrow_left.png"),
SWTResourceManager.getImage(this.getClass(), "arrow_left_hover.png"),
SWTResourceManager.getImage(this.getClass(), "arrow_left_pressed.png")});
leftButton.setAutoFire(true);
timeSlider = new RangeSlider(this, SWT.ON|SWT.HIGH|SWT.SMOOTH|SWT.CONTROL);
timeSlider.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
rightButton = new ImageButton(this, SWT.NONE);
GridData gd_rightButton = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
gd_rightButton.widthHint=18;
gd_rightButton.heightHint=18;
rightButton.setLayoutData(gd_rightButton);
rightButton.setImage(new Image[] {
SWTResourceManager.getImage(this.getClass(), "arrow_right.png"),
SWTResourceManager.getImage(this.getClass(), "arrow_right_hover.png"),
SWTResourceManager.getImage(this.getClass(), "arrow_right_pressed.png")});
rightButton.setAutoFire(true);
leftButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
int[] value = timeSlider.getSelection();
int incr=timeSlider.getIncrement();
int lower = Math.max(timeSlider.getMinimum(), value[0]-incr);
timeSlider.setValues(lower, lower + (value[1]-value[0]), true);
}
});
rightButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
int[] value = timeSlider.getSelection();
int incr=timeSlider.getIncrement();
int upper = Math.min(timeSlider.getMaximum(), value[1]+incr);
timeSlider.setValues(upper - (value[1]-value[0]), upper, true);
}
});
}
@Override
public void setEnabled (boolean enabled) {
timeSlider.setEnabled(enabled);
leftButton.setEnabled(enabled);
rightButton.setEnabled(enabled);
super.setEnabled(enabled);
redraw();
}
public void setButtonsEnabled (boolean enabled) {
leftButton.setEnabled(enabled);
rightButton.setEnabled(enabled);
redraw();
}
public void setToolTipFormatter(Format formatter){
timeSlider.setToolTipFormatter(formatter);
}
public void setToolTipText(String string) {
timeSlider.setToolTipText(string);
}
public void setSelection(int sel) {
timeSlider.setLowerValue(sel);
}
public void setSelection(int[] sel) {
assert(sel.length==2);
timeSlider.setValues(sel[0], sel[1]);
}
public int[] getSelection() {
return timeSlider.getSelection();
}
public void addSelectionListener(SelectionListener selectionListener) {
timeSlider.addSelectionListener(selectionListener);
}
public void setMinimum(int value) {
timeSlider.setMinimum(value);
}
public void setMaximum(int value) {
timeSlider.setMaximum(value);
}
public int getMaximum() {
return timeSlider.getMaximum();
}
public int getMinimum() {
return timeSlider.getMinimum();
}
}

View File

@ -0,0 +1,18 @@
package com.minres.scviewer.database.ui.swt.sb;
import org.eclipse.swt.widgets.Display;
public class ActionScheduler {
private final Display display;
private final Runnable action;
public ActionScheduler( Display display, Runnable action ) {
this.display = display;
this.action = action;
}
public void schedule( int delay ) {
display.timerExec( delay, action );
}
}

View File

@ -0,0 +1,45 @@
package com.minres.scviewer.database.ui.swt.sb;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Control;
public class ButtonClick {
public static final int LEFT_BUTTON = 1;
private boolean armed;
public boolean isArmed() {
return armed;
}
public void arm( MouseEvent event ) {
if( event.button == LEFT_BUTTON ) {
armed = true;
}
}
public void disarm() {
armed = false;
}
public void trigger( MouseEvent event, Runnable action ) {
try {
doTrigger( event, action );
} finally {
disarm();
}
}
private void doTrigger( MouseEvent event, Runnable action ) {
if( armed && inRange( event ) ) {
action.run();
}
}
private static boolean inRange( MouseEvent event ) {
Point size = ( ( Control )event.widget ).getSize();
return event.x >= 0 && event.x <= size.x && event.y >= 0 && event.y <= size.y;
}
}

View File

@ -0,0 +1,98 @@
package com.minres.scviewer.database.ui.swt.sb;
import org.eclipse.swt.SWT;
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.MouseTrackListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
class ClickControl extends ControlAdapter implements ViewComponent, MouseDownActionTimer.TimerAction, MouseListener, MouseTrackListener {
private final MouseDownActionTimer mouseDownActionTimer;
private final ClickAction clickAction;
private final ButtonClick buttonClick;
private final Label control;
private final ImageUpdate imageUpdate;
public interface ClickAction extends Runnable {
void setCoordinates( int x, int y );
}
ClickControl( Composite parent, ClickAction clickAction, int maxExtension ) {
this.control = new Label( parent, SWT.NONE );
this.imageUpdate = new ImageUpdate( control, maxExtension );
this.buttonClick = new ButtonClick();
this.mouseDownActionTimer = new MouseDownActionTimer( this, buttonClick, control.getDisplay() );
this.clickAction = clickAction;
this.control.addMouseTrackListener( this );
this.control.addMouseListener( this );
this.control.addControlListener( this );
}
@Override
public void controlResized( ControlEvent event ) {
imageUpdate.update();
}
@Override
public Label getControl() {
return control;
}
@Override
public void mouseDown( MouseEvent event ) {
buttonClick.arm( event );
clickAction.setCoordinates( event.x, event.y );
mouseDownActionTimer.activate();
}
@Override
public void mouseUp( MouseEvent event ) {
buttonClick.trigger( event, clickAction );
}
@Override
public void run() {
clickAction.run();
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public void mouseExit( MouseEvent event ) {
buttonClick.disarm();
}
void setForeground( Color color ) {
imageUpdate.setForeground( color );
}
Color getForeground() {
return imageUpdate.getForeground();
}
void setBackground( Color color ) {
imageUpdate.setBackground( color );
}
Color getBackground() {
return imageUpdate.getBackground();
}
@Override
public void mouseEnter( MouseEvent event ) {}
@Override
public void mouseHover( MouseEvent event ) {}
@Override
public void mouseDoubleClick( MouseEvent event ) {}
}

View File

@ -0,0 +1,105 @@
package com.minres.scviewer.database.ui.swt.sb;
import static java.lang.Math.max;
import java.math.BigDecimal;
import java.math.RoundingMode;
class ComponentDistribution {
private static final int MIN_DRAG_LENGTH = 17;
final int upFastLength;
final int dragStart;
final int dragLength;
final int downFastStart;
final int downFastLength;
final int downStart;
final int buttonLen;
ComponentDistribution( int buttonLen, int len, int range, int pos, int thumb ) {
int slideLen = slideLen( buttonLen, len );
int relDragLen = relDragLen( slideLen, range, thumb );
int minDragLength = max( MIN_DRAG_LENGTH, buttonLen );
int interval = interval( range, relDragLen, minDragLength );
this.dragLength = dragLen( minDragLength, relDragLen );
this.upFastLength = upFastLen( minDragLength, interval, pos, slideLen, relDragLen, dragLength );
this.downStart = downStart( buttonLen, len );
this.downFastStart = downFastStart( buttonLen, upFastLength, dragLength );
this.dragStart = dragStart( buttonLen, upFastLength );
this.downFastLength = downFastLen( minDragLength, interval, pos, slideLen, relDragLen, dragLength, upFastLength );
this.buttonLen = buttonLen;
}
private static int slideLen( int buttonLen, int len ) {
return len - buttonLen * 2;
}
private static int relDragLen( int slideLen, int range, int thumb ) {
return divide( slideLen * thumb, range );
}
private static int interval( int range, int relDragLen, int minDragLength ) {
int result = range;
if( useMinDragLen( minDragLength, relDragLen ) ) {
result += minDragLength - relDragLen / 2;
}
return result;
}
private static int dragLen( int buttonLen, int relDragLen ) {
return max( relDragLen, buttonLen );
}
private static int upFastLen( int buttonLen, int range, int pos, int slideLen, int relDragLen, int dragLen ) {
int result = slideLen * pos / range;
if( useMinDragLen( buttonLen, relDragLen ) ) {
result -= divide( ( dragLen - relDragLen ) * pos, range );
}
return result;
}
private static int downStart( int buttonLen, int len ) {
return len - buttonLen;
}
private static int downFastStart( int buttonLen, int upFastLength, int dragLength ) {
return buttonLen + upFastLength + dragLength;
}
private static int dragStart( int buttonLen, int upFastLen ) {
return buttonLen + upFastLen;
}
private static int downFastLen(
int buttonLen, int range, int pos, int slideLen, int relDragLen, int dragLen, int upFastLen )
{
int result = divide( slideLen * ( range - pos ), range ) - dragLen;
if( useMinDragLen( buttonLen, relDragLen ) ) {
result += divide( ( dragLen - relDragLen ) * pos, range );
}
return adjustDownFastLen( result, slideLen, dragLen, upFastLen );
}
private static boolean useMinDragLen( int buttonLen, int relDragLen ) {
return relDragLen < buttonLen;
}
static int divide( int dividend, int divisor ) {
BigDecimal bigDividend = new BigDecimal( dividend );
BigDecimal bigDivisor = new BigDecimal( divisor );
return bigDividend .divide( bigDivisor, 0, RoundingMode.HALF_EVEN ) .intValue();
}
private static int adjustDownFastLen( int tentative, int slideLen, int dragLen, int upFastLen ) {
// TODO [fappel]: Without this there is a flickering of the downFast label of one pixel.
// Check whether this can be resolved by better rounding or whatsoever.
int result = tentative;
if( slideLen < upFastLen + dragLen + result ) {
result--;
} else if( slideLen > upFastLen + dragLen + result ) {
result++;
}
return result;
}
}

View File

@ -0,0 +1,24 @@
package com.minres.scviewer.database.ui.swt.sb;
import org.eclipse.swt.SWT;
import com.minres.scviewer.database.ui.swt.sb.ClickControl.ClickAction;
class Decrementer implements ClickAction {
private final FlatScrollBar scrollBar;
Decrementer( FlatScrollBar scrollBar ) {
this.scrollBar = scrollBar;
}
@Override
public void run() {
int selection = scrollBar.getSelection() - scrollBar.getIncrement();
scrollBar.setSelectionInternal( selection, SWT.ARROW_UP );
}
@Override
public void setCoordinates( int x, int y ) {
}
}

View File

@ -0,0 +1,203 @@
package com.minres.scviewer.database.ui.swt.sb;
import static com.minres.scviewer.database.ui.swt.sb.FlatScrollBar.BAR_BREADTH;
import static java.lang.Math.max;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
enum Direction {
HORIZONTAL( SWT.HORIZONTAL ) {
@Override
protected void layout( FlatScrollBar scrollBar, int buttonLength ) {
ComponentDistribution distribution = calculateComponentDistribution( scrollBar, buttonLength );
Rectangle[] componentBounds = calculateComponentBounds( distribution, scrollBar );
applyComponentBounds( scrollBar, componentBounds );
}
private ComponentDistribution calculateComponentDistribution( FlatScrollBar scrollBar, int buttonLength ) {
return calculateComponentDistribution( scrollBar, buttonLength, getControlBounds( scrollBar ).width );
}
private Rectangle[] calculateComponentBounds( ComponentDistribution distribution, FlatScrollBar scrollBar ) {
int width = getControlBounds( scrollBar ).width;
int height = getControlBounds( scrollBar ).height - FlatScrollBar.BAR_BREADTH + 1;
int balance = getRoundingBalance( distribution, scrollBar );
return new Rectangle[] {
calcButtons( distribution, width, $( 0, CLEARANCE, distribution.buttonLen, height ) ),
$( distribution.buttonLen, CLEARANCE, distribution.upFastLength, height ),
calcDrag( distribution, $( distribution.dragStart, CLEARANCE, distribution.dragLength + balance, height ) ),
$( distribution.downFastStart, CLEARANCE, distribution.downFastLength - balance, height ),
calcButtons( distribution, width, $( distribution.downStart, CLEARANCE, distribution.buttonLen, height ) )
};
}
private Rectangle calcButtons( ComponentDistribution distribution, int length, Rectangle bounds ) {
Rectangle result = bounds;
if( length <= distribution.buttonLen* 2 ) {
int downStart = calcDownStartForSmallLength( bounds.x, length );
result = $( downStart, CLEARANCE, length / 2, bounds.height );
}
return result;
}
@Override
protected void setDefaultSize( Control control ) {
Point size = control.getSize();
control.setSize( size.x, FlatScrollBar.BAR_BREADTH );
}
@Override
protected Point computeSize( Composite composite, int wHint, int hHint, boolean changed ) {
int x = wHint == SWT.DEFAULT ? composite.getParent().getClientArea().width : wHint;
return new Point( x, FlatScrollBar.BAR_BREADTH );
}
@Override
protected void expand( Control control, int maxExpansion ) {
Rectangle bounds = control.getBounds();
int expand = expand( bounds.height, maxExpansion );
control.setBounds( bounds.x, bounds.y - expand, bounds.width, bounds.height + expand );
}
},
VERTICAL( SWT.VERTICAL ) {
@Override
protected void layout( FlatScrollBar scrollBar, int buttonLength ) {
ComponentDistribution calculation = calculateComponentDistribution( scrollBar, buttonLength );
applyComponentBounds( scrollBar, calculateComponentBounds( calculation, scrollBar ) );
}
private ComponentDistribution calculateComponentDistribution( FlatScrollBar scrollBar, int buttonLength ) {
return calculateComponentDistribution( scrollBar, buttonLength, getControlBounds( scrollBar ).height );
}
private Rectangle[] calculateComponentBounds( ComponentDistribution distribution, FlatScrollBar scrollBar ) {
int width = getControlBounds( scrollBar ).width - FlatScrollBar.BAR_BREADTH + 1;
int height = getControlBounds( scrollBar ).height;
int balance = getRoundingBalance( distribution, scrollBar );
return new Rectangle[] {
calculateButtons( distribution, height, $( CLEARANCE, 0, width, distribution.buttonLen ) ),
$( CLEARANCE, distribution.buttonLen, width, distribution.upFastLength ),
calcDrag( distribution, $( CLEARANCE, distribution.dragStart, width, distribution.dragLength + balance ) ),
$( CLEARANCE, distribution.downFastStart, width, distribution.downFastLength - balance ),
calculateButtons( distribution, height, $( CLEARANCE, distribution.downStart, width, distribution.buttonLen ) )
};
}
private Rectangle calculateButtons( ComponentDistribution distribution, int length, Rectangle bounds ) {
Rectangle result = bounds;
if( length <= distribution.buttonLen * 2 ) {
int downStart = calcDownStartForSmallLength( bounds.y, length );
result = $( CLEARANCE, downStart, bounds.width, length / 2 );
}
return result;
}
@Override
protected void setDefaultSize( Control control ) {
Point size = control.getSize();
control.setSize( FlatScrollBar.BAR_BREADTH, size.y );
}
@Override
protected Point computeSize( Composite composite, int wHint, int hHint, boolean changed ) {
int y = hHint == SWT.DEFAULT ? composite.getParent().getClientArea().height : hHint;
return new Point( FlatScrollBar.BAR_BREADTH, y );
}
@Override
protected void expand( Control control, int maxExpansion ) {
Rectangle bounds = control.getBounds();
int expand = expand( bounds.width, maxExpansion );
control.setBounds( bounds.x - expand, bounds.y, bounds.width + expand, bounds.height );
}
};
static final Rectangle EMPTY_RECTANGLE = $( 0, 0, 0, 0 );
static final int CLEARANCE = BAR_BREADTH - 2;
private final int value;
protected abstract void layout( FlatScrollBar scrollBar, int buttonLength );
protected abstract void setDefaultSize( Control control );
protected abstract Point computeSize( Composite comp, int wHint, int hHint, boolean changed );
protected abstract void expand( Control control, int maxExpansion );
Direction( int value ) {
this.value = value;
}
public int value() {
return value;
}
private static ComponentDistribution calculateComponentDistribution(
FlatScrollBar scrollBar , int buttonLength , int length )
{
int range = scrollBar.getMaximum() - scrollBar.getMinimum();
int position = scrollBar.getSelection() - scrollBar.getMinimum();
int thumb = scrollBar.getThumb();
return new ComponentDistribution( buttonLength, length, range, position, thumb );
}
private static Rectangle getControlBounds( FlatScrollBar scrollBar ) {
return scrollBar.getClientArea();
}
private static void applyComponentBounds( FlatScrollBar scrollBar, Rectangle[] bounds ) {
scrollBar.up.getControl().setBounds( bounds[ 0 ] );
scrollBar.upFast.getControl().setBounds( bounds[ 1 ] );
scrollBar.drag.getControl().setBounds( bounds[ 2 ] );
scrollBar.downFast.getControl().setBounds( bounds[ 3 ] );
scrollBar.down.getControl().setBounds( bounds[ 4 ] );
}
// TODO [fappel]: There is a 1 pixel rounding problem at the seam of drag/downFast with down.
// Seems to work but I would prefer a better solution if possible
private static int getRoundingBalance( ComponentDistribution calculation, FlatScrollBar scrollBar ) {
int result = 0;
int maximumSelection = scrollBar.getMaximum() - scrollBar.getThumb();
if( scrollBar.getSelection() == maximumSelection && 0 != calculation.downFastLength ) {
result = 1;
}
return result;
}
private static int expand( int toExpand, int maxExpansion ) {
return max( 0, FlatScrollBar.BAR_BREADTH + maxExpansion - max( FlatScrollBar.BAR_BREADTH, toExpand ) );
}
private static Rectangle calcDrag( ComponentDistribution distribution, Rectangle bounds ) {
Rectangle result = bounds;
if( isUndercutOfDragVisibility( distribution ) ) {
result = EMPTY_RECTANGLE;
}
return result;
}
private static boolean isUndercutOfDragVisibility( ComponentDistribution distribution ) {
return distribution.dragLength + distribution.buttonLen >= distribution.downStart;
}
private static int calcDownStartForSmallLength( int position, int length ) {
int result = position;
if( isDownStartPosition( position ) ) {
result = length / 2;
}
return result;
}
private static boolean isDownStartPosition( int position ) {
return position > 0 || position < 0;
}
private static Rectangle $( int x, int y, int width, int height ) {
return new Rectangle( x, y , width , height );
}
}

View File

@ -0,0 +1,105 @@
package com.minres.scviewer.database.ui.swt.sb;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DragDetectEvent;
import org.eclipse.swt.events.DragDetectListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
class DragControl
extends ControlAdapter
implements ViewComponent, DragDetectListener, MouseListener, MouseMoveListener
{
private final DragDetector dragDetector;
private final ImageUpdate imageUpdate;
private final DragAction dragAction;
private final Label control;
private Point startingPosition;
public interface DragAction {
void start();
void run( int startX, int startY, int currentX, int currentY );
void end();
}
DragControl( Composite parent, DragAction dragAction, int maxExpansion ) {
this.control = new Label( parent, SWT.NONE );
this.imageUpdate = new ImageUpdate( control, maxExpansion );
this.dragDetector = new DragDetector( control, 0 );
this.dragAction = dragAction;
initializeControl();
}
@Override
public Label getControl() {
return control;
}
@Override
public void dragDetected( DragDetectEvent event ) {
if( startingPosition != null ) {
dragAction.run( startingPosition.x, startingPosition.y, event.x, event.y );
}
dragDetector.dragHandled();
}
@Override
public void mouseDown( MouseEvent event ) {
startingPosition = new Point( event.x, event.y );
dragAction.start();
}
@Override
public void mouseUp( MouseEvent event ) {
if( startingPosition != null ) {
dragAction.end();
}
startingPosition = null;
}
@Override
public void mouseMove( MouseEvent event ) {
dragDetector.mouseMove( event );
}
@Override
public void controlResized( ControlEvent event ) {
imageUpdate.update();
}
void setForeground( Color color ) {
imageUpdate.setForeground( color );
}
Color getForeground() {
return imageUpdate.getForeground();
}
Color getBackground() {
return imageUpdate.getBackground();
}
void setBackground( Color color ) {
imageUpdate.setBackground( color );
}
private void initializeControl( ) {
control.addMouseListener( this );
control.addMouseMoveListener( this );
control.addControlListener( this );
control.addDragDetectListener( this );
}
@Override
public void mouseDoubleClick( MouseEvent event ) {}
}

View File

@ -0,0 +1,60 @@
package com.minres.scviewer.database.ui.swt.sb;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
// TODO [fappel]: This is a workaround for a problem described here:
// http://stackoverflow.com/questions/3908290/mousedown-events-are-not-delivered-until-mouseup-when-a-drag-source-is-present
// This seems to be related to https://bugs.eclipse.org/bugs/show_bug.cgi?id=328396
// which is resolved. As it did not work on my setup I adapted the workaround of the last
// stackoverflow answer.
public class DragDetector {
int lastMouseX;
int lastMouseY;
boolean dragEventGenerated;
private final Control control;
private final int sensibility;
public DragDetector( Control control, int sensibility ) {
this.control = control;
this.sensibility = sensibility;
this.control.setDragDetect( false );
}
public void mouseMove( MouseEvent e ) {
if( ( e.stateMask & SWT.BUTTON1 ) > 0 ) {
int deltaX = lastMouseX - e.x;
int deltaY = lastMouseY - e.y;
int dragDistance = deltaX * deltaX + deltaY * deltaY;
if( !dragEventGenerated && dragDistance > sensibility ) {
dragEventGenerated = true;
Event event = createDragEvent( e );
control.notifyListeners( SWT.DragDetect, event );
}
lastMouseX = e.x;
lastMouseY = e.y;
}
}
public void dragHandled() {
dragEventGenerated = false;
}
private Event createDragEvent( MouseEvent e ) {
Event event = new Event();
event.type = SWT.DragDetect;
event.display = control.getDisplay();
event.widget = control;
event.button = e.button;
event.stateMask = e.stateMask;
event.time = e.time;
event.x = e.x;
event.y = e.y;
return event;
}
}

View File

@ -0,0 +1,59 @@
package com.minres.scviewer.database.ui.swt.sb;
import static com.minres.scviewer.database.ui.swt.sb.Direction.HORIZONTAL;
import static com.minres.scviewer.database.ui.swt.sb.ShiftData.calculateSelectionRange;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import com.minres.scviewer.database.ui.swt.sb.DragControl.DragAction;
final class DragShifter implements DragAction {
private final FlatScrollBar scrollBar;
private final int buttonLength;
public DragShifter( FlatScrollBar scrollBar, int buttonLength ) {
this.scrollBar = scrollBar;
this.buttonLength = buttonLength;
}
@Override
public void start() {
scrollBar.notifyListeners( SWT.DRAG );
}
@Override
public void run( int startX, int startY, int currentX, int currentY ) {
ShiftData shiftData = newShiftData( startX, startY, currentX, currentY );
if( shiftData.canShift() ) {
int selectionRange = calculateSelectionRange( scrollBar );
int selectionDelta = shiftData.calculateSelectionDelta( selectionRange );
int selection = scrollBar.getSelection() + selectionDelta;
scrollBar.setSelectionInternal( selection, SWT.DRAG );
}
}
@Override
public void end() {
scrollBar.notifyListeners( SWT.NONE );
}
private ShiftData newShiftData( int startX, int startY, int currentX, int currentY ) {
ShiftData result;
if( scrollBar.direction == HORIZONTAL ) {
result = new ShiftData( buttonLength, getScrollBarSize().x, getDragSize().x, currentX - startX );
} else {
result = new ShiftData( buttonLength, getScrollBarSize().y, getDragSize().y, currentY - startY );
}
return result;
}
private Point getScrollBarSize() {
return scrollBar.getSize();
}
private Point getDragSize() {
return scrollBar.drag.getControl().getSize();
}
}

View File

@ -0,0 +1,49 @@
package com.minres.scviewer.database.ui.swt.sb;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import com.minres.scviewer.database.ui.swt.sb.ClickControl.ClickAction;
class FastDecrementer implements ClickAction {
private final FlatScrollBar scrollBar;
private int x;
private int y;
FastDecrementer( FlatScrollBar scrollBar ) {
this.scrollBar = scrollBar;
}
@Override
public void run() {
Rectangle drag = getDragBounds();
Point mouse = getMouseLocation();
if( mouse.x <= drag.x || mouse.y <= drag.y ) {
int selection = scrollBar.getSelection() - scrollBar.getPageIncrement();
scrollBar.setSelectionInternal( selection, SWT.PAGE_UP );
}
}
@Override
public void setCoordinates( int x, int y ) {
this.x = x;
this.y = y;
}
private Point getMouseLocation() {
return getDisplay().map( scrollBar.upFast.getControl(), null, x, y );
}
private Rectangle getDragBounds() {
Rectangle dragBounds = scrollBar.drag.getControl().getBounds();
return getDisplay().map( scrollBar, null, dragBounds );
}
private Display getDisplay() {
return scrollBar.getDisplay();
}
}

View File

@ -0,0 +1,42 @@
package com.minres.scviewer.database.ui.swt.sb;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import com.minres.scviewer.database.ui.swt.sb.ClickControl.ClickAction;
class FastIncrementer implements ClickAction {
private final FlatScrollBar scrollBar;
private Point mouse;
FastIncrementer( FlatScrollBar scrollBar ) {
this.scrollBar = scrollBar;
}
@Override
public void run() {
Rectangle drag = getDragBounds();
if( mouse.x > drag.x + drag.width || mouse.y > drag.y + drag.height ) {
int selection = scrollBar.getSelection() + scrollBar.getPageIncrement();
scrollBar.setSelectionInternal( selection, SWT.PAGE_DOWN );
}
}
@Override
public void setCoordinates( int x, int y ) {
mouse = getMouseLocation( x, y );
}
private Point getMouseLocation(int x, int y) {
return Display.getCurrent().map( scrollBar.downFast.getControl(), null, x, y );
}
private Rectangle getDragBounds() {
Rectangle dragBounds = scrollBar.drag.getControl().getBounds();
return Display.getCurrent().map( scrollBar, null, dragBounds );
}
}

View File

@ -0,0 +1,295 @@
package com.minres.scviewer.database.ui.swt.sb;
import static com.minres.scviewer.database.ui.swt.sb.UntypedSelectionAdapter.lookup;
import java.util.Collection;
import java.util.HashSet;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Listener;
public class FlatScrollBar extends Composite {
public static final int BAR_BREADTH = 6;
static final int DEFAULT_MINIMUM = 0;
static final int DEFAULT_MAXIMUM = 100;
static final int DEFAULT_INCREMENT = 1;
static final int DEFAULT_THUMB = 10;
static final int DEFAULT_PAGE_INCREMENT = DEFAULT_THUMB;
static final int DEFAULT_SELECTION = 0;
static final int DEFAULT_BUTTON_LENGTH = 0;
static final int DEFAULT_MAX_EXPANSION = Direction.CLEARANCE + 2;
final ClickControl up;
final ClickControl upFast;
final DragControl drag;
final ClickControl downFast;
final ClickControl down;
final Direction direction;
final MouseWheelShifter mouseWheelHandler;
final Collection<SelectionListener> listeners;
private int minimum;
private int maximum;
private int increment;
private int pageIncrement;
private int thumb;
private int selection;
private boolean onDrag;
private int buttonLength;
public FlatScrollBar( final Composite parent, int style ) {
this( parent, style, DEFAULT_BUTTON_LENGTH, DEFAULT_MAX_EXPANSION );
}
FlatScrollBar( Composite parent, int style, int buttonLength, int maxExpansion ) {
super( parent, SWT.NONE );
super.setLayout( new FlatScrollBarLayout( getDirection( style ) ) );
this.minimum = DEFAULT_MINIMUM;
this.maximum = DEFAULT_MAXIMUM;
this.increment = DEFAULT_INCREMENT;
this.pageIncrement = DEFAULT_PAGE_INCREMENT;
this.thumb = DEFAULT_THUMB;
this.selection = DEFAULT_SELECTION;
this.buttonLength = buttonLength;
this.direction = getDirection( style );
this.direction.setDefaultSize( this );
this.up = new ClickControl( this, new Decrementer( this ), maxExpansion );
this.upFast = new ClickControl( this, new FastDecrementer( this ), maxExpansion );
this.drag = new DragControl( this, new DragShifter( this, buttonLength ), maxExpansion );
this.downFast = new ClickControl( this, new FastIncrementer( this ), maxExpansion );
this.down = new ClickControl( this, new Incrementer( this ), maxExpansion );
this.mouseWheelHandler = new MouseWheelShifter( this, parent, buttonLength );
this.listeners = new HashSet<SelectionListener>();
addMouseTrackListener( new MouseTracker( this, maxExpansion ) );
addControlListener( new ResizeObserver( this ) );
setDefaultColorScheme();
}
@Override
public void setLayout( Layout layout ) {
throw new UnsupportedOperationException( FlatScrollBar.class.getName() + " does not allow to change layout." );
};
@Override
public int getStyle() {
return direction != null ? super.getStyle() | direction.value() : super.getStyle();
};
Direction getDirection() {
return direction;
}
public void setMinimum( int minimum ) {
if( this.minimum != minimum && minimum >= 0 && minimum < maximum ) {
this.minimum = minimum;
adjustThumb();
adjustSelection();
layout();
}
}
public int getMinimum() {
return minimum;
}
public void setMaximum( int maximum ) {
if( this.maximum != maximum && maximum >= 0 && maximum > minimum ) {
this.maximum = maximum;
adjustThumb();
adjustSelection();
layout();
}
}
public int getMaximum() {
return maximum;
}
public void setThumb( int thumb ) {
if( this.thumb != thumb && thumb >= 1 ) {
this.thumb = thumb;
adjustThumb();
adjustSelection();
layout();
}
}
public int getThumb() {
return thumb;
}
public void setIncrement( int increment ) {
if( this.increment != increment ) {
this.increment = increment;
layout();
}
}
public int getIncrement() {
return increment;
}
public void setPageIncrement( int pageIncrement ) {
this.pageIncrement = pageIncrement;
}
public int getPageIncrement() {
return pageIncrement;
}
public void setSelection( int selection ) {
if( !onDrag ) {
updateSelection( selection );
}
}
public int getSelection() {
return selection;
}
public void addSelectionListener( SelectionListener selectionListener ) {
listeners.add( selectionListener );
}
public void removeSelectionListener( SelectionListener selectionListener ) {
listeners.remove( selectionListener );
}
@Override
public void addListener( int eventType, final Listener listener ) {
if( eventType == SWT.Selection ) {
addSelectionListener( new UntypedSelectionAdapter( listener ) );
} else {
super.addListener( eventType, listener );
}
}
@Override
public void removeListener( int eventType, Listener listener ) {
if( eventType == SWT.Selection ) {
removeSelectionListener( lookup( listeners, listener ) );
} else {
super.removeListener( eventType, listener );
}
}
@Override
public void layout() {
direction.layout( this, buttonLength );
update();
}
public void setIncrementButtonLength( int length ) {
this.buttonLength = length;
layout();
}
public int getIncrementButtonLength() {
return buttonLength;
}
public void setIncrementColor( Color color ) {
up.setForeground( color );
down.setForeground( color );
}
public Color getIncrementColor() {
return up.getForeground();
}
public void setPageIncrementColor( Color color ) {
upFast.setForeground( color );
downFast.setForeground( color );
}
public Color getPageIncrementColor() {
return upFast.getForeground();
}
public void setThumbColor( Color color ) {
drag.setForeground( color );
}
public Color getThumbColor() {
return drag.getForeground();
}
@Override
public void setBackground( Color color ) {
up.setBackground( color );
upFast.setBackground( color );
drag.setBackground( color );
downFast.setBackground( color );
down.setBackground( color );
super.setBackground( color );
}
protected void setSelectionInternal( int selection, int detail ) {
int oldSelection = this.selection;
updateSelection( selection );
if( oldSelection != this.selection ) {
notifyListeners( detail );
}
}
private void updateSelection( int selection ) {
if( this.selection != selection ) {
this.selection = selection;
adjustSelection();
layout();
}
}
public void notifyListeners( int detail ) {
updateOnDrag( detail );
SelectionEvent selectionEvent = createEvent( detail );
for( SelectionListener listener : listeners ) {
listener.widgetSelected( selectionEvent );
}
}
private void updateOnDrag( int detail ) {
onDrag = ( onDrag || ( SWT.DRAG & detail ) > 0 ) && ( SWT.NONE != detail );
}
private SelectionEvent createEvent( int detail ) {
Event event = new Event();
event.widget = this;
event.detail = detail;
return new SelectionEvent( event );
}
private void adjustThumb() {
if( thumb > maximum - minimum ) {
thumb = Math.min( maximum - minimum, thumb );
thumb = Math.max( 1, thumb );
}
}
private void adjustSelection() {
selection = Math.min( selection, maximum - thumb );
selection = Math.max( selection, minimum );
}
private static Direction getDirection( int style ) {
return ( style & SWT.HORIZONTAL ) > 0 ? Direction.HORIZONTAL : Direction.VERTICAL;
}
private void setDefaultColorScheme() {
up.setForeground( Display.getCurrent().getSystemColor( SWT.COLOR_WIDGET_NORMAL_SHADOW ) );
upFast.setForeground( Display.getCurrent().getSystemColor( SWT.COLOR_WIDGET_BACKGROUND ) );
drag.setForeground( Display.getCurrent().getSystemColor( SWT.COLOR_WIDGET_FOREGROUND ) );
downFast.setForeground( Display.getCurrent().getSystemColor( SWT.COLOR_WIDGET_BACKGROUND ) );
down.setForeground( Display.getCurrent().getSystemColor( SWT.COLOR_WIDGET_NORMAL_SHADOW ) );
setBackground( getBackground() );
}
}

View File

@ -0,0 +1,23 @@
package com.minres.scviewer.database.ui.swt.sb;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Layout;
class FlatScrollBarLayout extends Layout {
private final Direction direction;
public FlatScrollBarLayout( Direction orientation ) {
this.direction = orientation;
}
@Override
protected void layout( Composite composite, boolean flushCache ) {
}
@Override
protected Point computeSize( Composite composite, int wHint, int hHint, boolean flushCache ) {
return direction.computeSize( composite, wHint, hHint, flushCache );
}
}

View File

@ -0,0 +1,109 @@
package com.minres.scviewer.database.ui.swt.sb;
import static java.lang.Math.min;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
class ImageDrawer {
static final String IMAGE_DRAWER_IS_DISPOSED = "ImageDrawer is disposed.";
private final int maxExpansion;
private Color background;
private Color foreground;
ImageDrawer( int expansion ) {
this( expansion, getSystemColor( SWT.COLOR_WIDGET_DARK_SHADOW ), getSystemColor( SWT.COLOR_LIST_BACKGROUND ) );
}
ImageDrawer( int expansion, Color background, Color foreground ) {
this.maxExpansion = expansion;
this.foreground = defensiveCopy( background );
this.background = defensiveCopy( foreground );
}
void setForeground( Color foreground ) {
checkDisposed();
if( foreground != null ) {
this.foreground = prepareColorAttribute( this.foreground, foreground );
}
}
Color getForeground() {
checkDisposed();
return foreground;
}
void setBackground( Color background ) {
checkDisposed();
if( background != null ) {
this.background = prepareColorAttribute( this.background, background );
}
}
Color getBackground() {
checkDisposed();
return background;
}
Image draw( int width, int height ) {
checkDisposed();
Image result = new Image( getDisplay(), width, height );
GC gc = new GC( result );
try {
draw( gc, width, height );
} finally {
gc.dispose();
}
return result;
}
boolean isDisposed() {
return background.isDisposed();
}
void dispose() {
if( !isDisposed() ) {
this.background.dispose();
this.foreground.dispose();
}
}
private void draw( GC gc, int width, int height ) {
gc.setBackground( background );
gc.fillRectangle( 0, 0, width, height );
gc.setBackground( foreground );
gc.setAdvanced( true );
gc.setAntialias( SWT.ON );
int arc = min( width, height ) == 1 ? 1 : maxExpansion + 2;
gc.fillRoundRectangle( 0, 0, width, height, arc, arc );
}
private void checkDisposed() {
if( isDisposed() ) {
throw new IllegalStateException( IMAGE_DRAWER_IS_DISPOSED );
}
}
private static Color getSystemColor( int colorCode ) {
return getDisplay().getSystemColor( colorCode );
}
private static Color prepareColorAttribute( Color oldColor, Color newColor ) {
oldColor.dispose();
return defensiveCopy( newColor );
}
private static Color defensiveCopy( Color background ) {
return new Color( getDisplay(), background.getRGB() );
}
private static Display getDisplay() {
return Display.getCurrent();
}
}

View File

@ -0,0 +1,46 @@
package com.minres.scviewer.database.ui.swt.sb;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Label;
class ImageUpdate {
private final ImageDrawer imageDrawer;
private final Label control;
ImageUpdate( Label control, int maxExpansion ) {
this.imageDrawer = new ImageDrawer( maxExpansion );
this.control = control;
this.control.addListener( SWT.Dispose, evt -> imageDrawer.dispose() );
}
void setForeground( Color color ) {
imageDrawer.setForeground( color );
}
Color getForeground() {
return imageDrawer.getForeground();
}
void setBackground( Color color ) {
imageDrawer.setBackground( color );
}
Color getBackground() {
return imageDrawer.getBackground();
}
void update() {
if( !control.isDisposed() ) {
if( control.getImage() != null ) {
control.getImage().dispose();
}
Point size = control.getSize();
if( size.x > 0 && size.y > 0 ) {
control.setImage( imageDrawer.draw( size.x, size.y ) );
}
}
}
}

View File

@ -0,0 +1,24 @@
package com.minres.scviewer.database.ui.swt.sb;
import org.eclipse.swt.SWT;
import com.minres.scviewer.database.ui.swt.sb.ClickControl.ClickAction;
class Incrementer implements ClickAction {
private final FlatScrollBar scrollBar;
Incrementer( FlatScrollBar scrollBar ) {
this.scrollBar = scrollBar;
}
@Override
public void run() {
int selection = scrollBar.getSelection() + scrollBar.getIncrement();
scrollBar.setSelectionInternal( selection, SWT.ARROW_DOWN );
}
@Override
public void setCoordinates( int x, int y ) {
}
}

View File

@ -0,0 +1,37 @@
package com.minres.scviewer.database.ui.swt.sb;
import org.eclipse.swt.widgets.Display;
public class MouseDownActionTimer implements Runnable {
public static final int INITIAL_DELAY = 300;
public static final int FAST_DELAY = 50;
private final ActionScheduler scheduler;
private final TimerAction timerAction;
private final ButtonClick mouseClick;
public interface TimerAction extends Runnable {
boolean isEnabled();
}
public MouseDownActionTimer( TimerAction timerAction, ButtonClick mouseClick, Display display ) {
this.scheduler = new ActionScheduler( display, this );
this.timerAction = timerAction;
this.mouseClick = mouseClick;
}
public void activate() {
if( timerAction.isEnabled() ) {
scheduler.schedule( INITIAL_DELAY );
}
}
@Override
public void run() {
if( mouseClick.isArmed() && timerAction.isEnabled() ) {
timerAction.run();
scheduler.schedule( FAST_DELAY );
}
}
}

View File

@ -0,0 +1,66 @@
package com.minres.scviewer.database.ui.swt.sb;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
class MouseTracker extends MouseTrackAdapter implements Runnable, DisposeListener {
static final int DELAY = 500;
private final FlatScrollBar scrollBar;
private final int maxExpansion;
private Rectangle expandedBounds;
private Rectangle originBounds;
private boolean mouseOver;
private boolean disposed;
MouseTracker( FlatScrollBar scrollBar, int maxExpansion ) {
this.scrollBar = scrollBar;
this.maxExpansion = maxExpansion;
this.scrollBar.addDisposeListener( this );
this.scrollBar.up.getControl().addMouseTrackListener( this );
this.scrollBar.upFast.getControl().addMouseTrackListener( this );
this.scrollBar.drag.getControl().addMouseTrackListener( this );
this.scrollBar.downFast.getControl().addMouseTrackListener( this );
this.scrollBar.down.getControl().addMouseTrackListener( this );
}
@Override
public void mouseEnter( MouseEvent event ) {
mouseOver = true;
if( !disposed && originBounds == null ) {
originBounds = scrollBar.getBounds();
scrollBar.getDirection().expand( scrollBar, maxExpansion );
expandedBounds = scrollBar.getBounds();
}
}
@Override
public void mouseExit( MouseEvent event ) {
mouseOver = false;
if( !disposed ) {
Display.getCurrent().timerExec( DELAY, this );
}
}
@Override
public void run() {
if( !disposed && !mouseOver ) {
if( scrollBar.getBounds().equals( expandedBounds ) ) {
scrollBar.setBounds( originBounds );
}
originBounds = null;
expandedBounds = null;
}
}
@Override
public void widgetDisposed( DisposeEvent e ) {
disposed = true;
}
}

View File

@ -0,0 +1,69 @@
package com.minres.scviewer.database.ui.swt.sb;
import static com.minres.scviewer.database.ui.swt.sb.Direction.HORIZONTAL;
import static com.minres.scviewer.database.ui.swt.sb.ShiftData.calculateSelectionRange;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
public class MouseWheelShifter implements Listener, DisposeListener {
private final FlatScrollBar scrollBar;
private final Composite parent;
private final int buttonLength;
MouseWheelShifter( FlatScrollBar scrollBar, Composite parent, int buttonLength ) {
this.scrollBar = scrollBar;
this.parent = parent;
this.buttonLength = buttonLength;
parent.addListener( getListenerType(), this );
scrollBar.addDisposeListener( this );
}
@Override
public void handleEvent( Event event ) {
handleMouseWheelScroll( event );
}
@Override
public void widgetDisposed( DisposeEvent e ) {
parent.removeListener( getListenerType(), this );
}
private void handleMouseWheelScroll( Event event ) {
ShiftData shiftData = newShiftData( event.count );
if( shiftData.canShift() ) {
int selectionRange = calculateSelectionRange( scrollBar );
int selectionDelta = shiftData.calculateSelectionDelta( selectionRange );
int selection = scrollBar.getSelection() - selectionDelta;
scrollBar.setSelectionInternal( selection, scrollBar.direction.value() );
}
}
private ShiftData newShiftData( int delta ) {
ShiftData result;
if( scrollBar.direction == Direction.HORIZONTAL ) {
result = new ShiftData( buttonLength, getScrollBarSize().x, getDragSize().x, delta );
} else {
result = new ShiftData( buttonLength, getScrollBarSize().y, getDragSize().y, delta );
}
return result;
}
private Point getScrollBarSize() {
return scrollBar.getSize();
}
private Point getDragSize() {
return scrollBar.drag.getControl().getSize();
}
private int getListenerType() {
return scrollBar.direction == HORIZONTAL ? SWT.MouseHorizontalWheel: SWT.MouseVerticalWheel;
}
}

View File

@ -0,0 +1,19 @@
package com.minres.scviewer.database.ui.swt.sb;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
class ResizeObserver extends ControlAdapter {
private final FlatScrollBar flatScrollBar;
public ResizeObserver( FlatScrollBar flatScrollBar ) {
this.flatScrollBar = flatScrollBar;
}
@Override
public void controlResized( ControlEvent event ) {
flatScrollBar.layout();
flatScrollBar.moveAbove( null );
}
}

View File

@ -0,0 +1,32 @@
package com.minres.scviewer.database.ui.swt.sb;
import static com.minres.scviewer.database.ui.swt.sb.ComponentDistribution.divide;
class ShiftData {
private final int slidePixels;
private final int movedPixels;
private final int buttonLength;
ShiftData( int buttonLength, int scrollBarPixels, int dragPixels, int movedPixels ) {
this.buttonLength = buttonLength;
this.slidePixels = calculateSlidePixels( scrollBarPixels, dragPixels );
this.movedPixels = movedPixels;
}
boolean canShift( ) {
return slidePixels > 0;
}
int calculateSelectionDelta( int selectionRange ) {
return divide( movedPixels * selectionRange, slidePixels );
}
static int calculateSelectionRange( FlatScrollBar scrollBar ) {
return scrollBar.getMaximum() - scrollBar.getMinimum() - scrollBar.getThumb();
}
private int calculateSlidePixels( int scrollBarPixels, int dragPixels ) {
return scrollBarPixels - 2 * buttonLength - dragPixels;
}
}

View File

@ -0,0 +1,43 @@
package com.minres.scviewer.database.ui.swt.sb;
import java.util.Collection;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
class UntypedSelectionAdapter extends SelectionAdapter {
final Listener listener;
UntypedSelectionAdapter( Listener listener ) {
this.listener = listener;
}
@Override
public void widgetSelected( SelectionEvent selectionEvent ) {
Event event = new Event();
event.widget = selectionEvent.widget;
event.detail = selectionEvent.detail;
listener.handleEvent( event );
}
static SelectionListener lookup( Collection<SelectionListener> listeners, Listener untypedListener ) {
for( SelectionListener listener : listeners ) {
if( isAdapterType( listener ) && matches( untypedListener, listener ) ) {
return listener;
}
}
return null;
}
private static boolean isAdapterType( SelectionListener listener ) {
return listener instanceof UntypedSelectionAdapter;
}
private static boolean matches( Listener untypedListener, SelectionListener listener ) {
return ( ( UntypedSelectionAdapter )listener ).listener == untypedListener;
}
}

View File

@ -0,0 +1,7 @@
package com.minres.scviewer.database.ui.swt.sb;
import org.eclipse.swt.widgets.Control;
public interface ViewComponent {
Control getControl();
}

View File

@ -29,17 +29,14 @@ import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Display;
/** /**
* Utility class for managing OS resources associated with SWT controls such as * Utility class for managing OS resources associated with SWT controls such as colors, fonts, images, etc.
* colors, fonts, images, etc.
* <p> * <p>
* !!! IMPORTANT !!! Application code must explicitly invoke the * !!! IMPORTANT !!! Application code must explicitly invoke the <code>dispose()</code> method to release the
* <code>dispose()</code> method to release the operating system resources * operating system resources managed by cached objects when those objects and OS resources are no longer
* managed by cached objects when those objects and OS resources are no longer
* needed (e.g. on application shutdown) * needed (e.g. on application shutdown)
* <p> * <p>
* This class may be freely distributed as part of any application or plugin. * This class may be freely distributed as part of any application or plugin.
* <p> * <p>
*
* @author scheglov_ke * @author scheglov_ke
* @author Dan Rubel * @author Dan Rubel
*/ */
@ -49,54 +46,57 @@ public class SWTResourceManager {
// Color // Color
// //
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
private static Map<RGB, Color> colorMap = new HashMap<>(); private static Map<RGB, Color> m_colorMap = new HashMap<RGB, Color>();
private SWTResourceManager() {}
/** /**
* Returns the system {@link Color} matching the specific ID. * Returns the system {@link Color} matching the specific ID.
* *
* @param systemColorID the ID value for the color * @param systemColorID
* the ID value for the color
* @return the system {@link Color} matching the specific ID * @return the system {@link Color} matching the specific ID
*/ */
public static Color getColor(int systemColorID) { public static Color getColor(int systemColorID) {
Display display = Display.getCurrent(); Display display = Display.getCurrent();
return display.getSystemColor(systemColorID); return display.getSystemColor(systemColorID);
} }
/** /**
* Returns a {@link Color} given its red, green and blue component values. * Returns a {@link Color} given its red, green and blue component values.
* *
* @param r the red component of the color * @param r
* @param g the green component of the color * the red component of the color
* @param b the blue component of the color * @param g
* @return the {@link Color} matching the given red, green and blue component * the green component of the color
* values * @param b
* the blue component of the color
* @return the {@link Color} matching the given red, green and blue component values
*/ */
public static Color getColor(int r, int g, int b) { public static Color getColor(int r, int g, int b) {
return getColor(new RGB(r, g, b)); return getColor(new RGB(r, g, b));
} }
/** /**
* Returns a {@link Color} given its RGB value. * Returns a {@link Color} given its RGB value.
* *
* @param rgb the {@link RGB} value of the color * @param rgb
* the {@link RGB} value of the color
* @return the {@link Color} matching the RGB value * @return the {@link Color} matching the RGB value
*/ */
public static Color getColor(RGB rgb) { public static Color getColor(RGB rgb) {
return colorMap.computeIfAbsent(rgb, k -> new Color(Display.getCurrent(), rgb)); Color color = m_colorMap.get(rgb);
if (color == null) {
Display display = Display.getCurrent();
color = new Color(display, rgb);
m_colorMap.put(rgb, color);
}
return color;
} }
/** /**
* Dispose of all the cached {@link Color}'s. * Dispose of all the cached {@link Color}'s.
*/ */
public static void disposeColors() { public static void disposeColors() {
for (Color color : colorMap.values()) { for (Color color : m_colorMap.values()) {
color.dispose(); color.dispose();
} }
colorMap.clear(); m_colorMap.clear();
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// //
// Image // Image
@ -105,12 +105,12 @@ public class SWTResourceManager {
/** /**
* Maps image paths to images. * Maps image paths to images.
*/ */
private static Map<String, Image> imageMap = new HashMap<>(); private static Map<String, Image> m_imageMap = new HashMap<String, Image>();
/** /**
* Returns an {@link Image} encoded by the specified {@link InputStream}. * Returns an {@link Image} encoded by the specified {@link InputStream}.
* *
* @param stream the {@link InputStream} encoding the image data * @param stream
* the {@link InputStream} encoding the image data
* @return the {@link Image} encoded by the specified input stream * @return the {@link Image} encoded by the specified input stream
*/ */
protected static Image getImage(InputStream stream) throws IOException { protected static Image getImage(InputStream stream) throws IOException {
@ -125,55 +125,52 @@ public class SWTResourceManager {
stream.close(); stream.close();
} }
} }
/** /**
* Returns an {@link Image} stored in the file at the specified path. * Returns an {@link Image} stored in the file at the specified path.
* *
* @param path the path to the image file * @param path
* the path to the image file
* @return the {@link Image} stored in the file at the specified path * @return the {@link Image} stored in the file at the specified path
*/ */
public static Image getImage(String path) { public static Image getImage(String path) {
Image image = imageMap.get(path); Image image = m_imageMap.get(path);
if (image == null) { if (image == null) {
try { try {
image = getImage(new FileInputStream(path)); image = getImage(new FileInputStream(path));
imageMap.put(path, image); m_imageMap.put(path, image);
} catch (Exception e) { } catch (Exception e) {
image = getMissingImage(); image = getMissingImage();
imageMap.put(path, image); m_imageMap.put(path, image);
} }
} }
return image; return image;
} }
/** /**
* Returns an {@link Image} stored in the file at the specified path relative to * Returns an {@link Image} stored in the file at the specified path relative to the specified class.
* the specified class.
* *
* @param clazz the {@link Class} relative to which to find the image * @param clazz
* @param path the path to the image file, if starts with <code>'/'</code> * the {@link Class} relative to which to find the image
* @param path
* the path to the image file, if starts with <code>'/'</code>
* @return the {@link Image} stored in the file at the specified path * @return the {@link Image} stored in the file at the specified path
*/ */
public static Image getImage(Class<?> clazz, String path) { public static Image getImage(Class<?> clazz, String path) {
String key = clazz.getName() + '|' + path; String key = clazz.getName() + '|' + path;
Image image = imageMap.get(key); Image image = m_imageMap.get(key);
if (image == null) { if (image == null) {
try { try {
image = getImage(clazz.getResourceAsStream(path)); image = getImage(clazz.getResourceAsStream(path));
imageMap.put(key, image); m_imageMap.put(key, image);
} catch (Exception e) { } catch (Exception e) {
image = getMissingImage(); image = getMissingImage();
imageMap.put(key, image); m_imageMap.put(key, image);
} }
} }
return image; return image;
} }
private static final int MISSING_IMAGE_SIZE = 10; private static final int MISSING_IMAGE_SIZE = 10;
/** /**
* @return the small {@link Image} that can be used as placeholder for missing * @return the small {@link Image} that can be used as placeholder for missing image.
* image.
*/ */
private static Image getMissingImage() { private static Image getMissingImage() {
Image image = new Image(Display.getCurrent(), MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE); Image image = new Image(Display.getCurrent(), MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE);
@ -185,7 +182,6 @@ public class SWTResourceManager {
// //
return image; return image;
} }
/** /**
* Style constant for placing decorator image in top left corner of base image. * Style constant for placing decorator image in top left corner of base image.
*/ */
@ -195,13 +191,11 @@ public class SWTResourceManager {
*/ */
public static final int TOP_RIGHT = 2; public static final int TOP_RIGHT = 2;
/** /**
* Style constant for placing decorator image in bottom left corner of base * Style constant for placing decorator image in bottom left corner of base image.
* image.
*/ */
public static final int BOTTOM_LEFT = 3; public static final int BOTTOM_LEFT = 3;
/** /**
* Style constant for placing decorator image in bottom right corner of base * Style constant for placing decorator image in bottom right corner of base image.
* image.
*/ */
public static final int BOTTOM_RIGHT = 4; public static final int BOTTOM_RIGHT = 4;
/** /**
@ -212,77 +206,83 @@ public class SWTResourceManager {
* Maps images to decorated images. * Maps images to decorated images.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static Map<Image, Map<Image, Image>>[] decoratedImageMap = new Map[LAST_CORNER_KEY]; private static Map<Image, Map<Image, Image>>[] m_decoratedImageMap = new Map[LAST_CORNER_KEY];
/** /**
* Returns an {@link Image} composed of a base image decorated by another image. * Returns an {@link Image} composed of a base image decorated by another image.
* *
* @param baseImage the base {@link Image} that should be decorated * @param baseImage
* @param decorator the {@link Image} to decorate the base image * the base {@link Image} that should be decorated
* @param decorator
* the {@link Image} to decorate the base image
* @return {@link Image} The resulting decorated image * @return {@link Image} The resulting decorated image
*/ */
public static Image decorateImage(Image baseImage, Image decorator) { public static Image decorateImage(Image baseImage, Image decorator) {
return decorateImage(baseImage, decorator, BOTTOM_RIGHT); return decorateImage(baseImage, decorator, BOTTOM_RIGHT);
} }
/** /**
* Returns an {@link Image} composed of a base image decorated by another image. * Returns an {@link Image} composed of a base image decorated by another image.
* *
* @param baseImage the base {@link Image} that should be decorated * @param baseImage
* @param decorator the {@link Image} to decorate the base image * the base {@link Image} that should be decorated
* @param corner the corner to place decorator image * @param decorator
* the {@link Image} to decorate the base image
* @param corner
* the corner to place decorator image
* @return the resulting decorated {@link Image} * @return the resulting decorated {@link Image}
*/ */
public static Image decorateImage(final Image baseImage, final Image decorator, final int corner) { public static Image decorateImage(final Image baseImage, final Image decorator, final int corner) {
if (corner <= 0 || corner >= LAST_CORNER_KEY) { if (corner <= 0 || corner >= LAST_CORNER_KEY) {
throw new IllegalArgumentException("Wrong decorate corner"); throw new IllegalArgumentException("Wrong decorate corner");
} }
Map<Image, Map<Image, Image>> cornerDecoratedImageMap = decoratedImageMap[corner]; Map<Image, Map<Image, Image>> cornerDecoratedImageMap = m_decoratedImageMap[corner];
if (cornerDecoratedImageMap == null) { if (cornerDecoratedImageMap == null) {
cornerDecoratedImageMap = new HashMap<>(); cornerDecoratedImageMap = new HashMap<Image, Map<Image, Image>>();
decoratedImageMap[corner] = cornerDecoratedImageMap; m_decoratedImageMap[corner] = cornerDecoratedImageMap;
} }
Map<Image, Image> decoratedMap = cornerDecoratedImageMap.computeIfAbsent(baseImage, Map<Image, Image> decoratedMap = cornerDecoratedImageMap.get(baseImage);
k -> new HashMap<Image, Image>()); if (decoratedMap == null) {
return decoratedMap.computeIfAbsent(decorator, k -> { decoratedMap = new HashMap<Image, Image>();
cornerDecoratedImageMap.put(baseImage, decoratedMap);
}
//
Image result = decoratedMap.get(decorator);
if (result == null) {
Rectangle bib = baseImage.getBounds(); Rectangle bib = baseImage.getBounds();
Rectangle dib = decorator.getBounds(); Rectangle dib = decorator.getBounds();
Image result = new Image(Display.getCurrent(), bib.width, bib.height); //
result = new Image(Display.getCurrent(), bib.width, bib.height);
//
GC gc = new GC(result); GC gc = new GC(result);
gc.drawImage(baseImage, 0, 0); gc.drawImage(baseImage, 0, 0);
switch (corner) { if (corner == TOP_LEFT) {
case TOP_LEFT:
gc.drawImage(decorator, 0, 0); gc.drawImage(decorator, 0, 0);
break; } else if (corner == TOP_RIGHT) {
case TOP_RIGHT:
gc.drawImage(decorator, bib.width - dib.width, 0); gc.drawImage(decorator, bib.width - dib.width, 0);
break; } else if (corner == BOTTOM_LEFT) {
case BOTTOM_LEFT:
gc.drawImage(decorator, 0, bib.height - dib.height); gc.drawImage(decorator, 0, bib.height - dib.height);
break; } else if (corner == BOTTOM_RIGHT) {
case BOTTOM_RIGHT:
gc.drawImage(decorator, bib.width - dib.width, bib.height - dib.height); gc.drawImage(decorator, bib.width - dib.width, bib.height - dib.height);
break;
default:
// do nothing
} }
gc.dispose(); gc.dispose();
return result; //
}); decoratedMap.put(decorator, result);
}
return result;
} }
/** /**
* Dispose all of the cached {@link Image}'s. * Dispose all of the cached {@link Image}'s.
*/ */
public static void disposeImages() { public static void disposeImages() {
// dispose loaded images // dispose loaded images
for (Image image : imageMap.values()) { {
image.dispose(); for (Image image : m_imageMap.values()) {
image.dispose();
}
m_imageMap.clear();
} }
imageMap.clear();
// dispose decorated images // dispose decorated images
for (int i = 0; i < decoratedImageMap.length; i++) { for (int i = 0; i < m_decoratedImageMap.length; i++) {
Map<Image, Map<Image, Image>> cornerDecoratedImageMap = decoratedImageMap[i]; Map<Image, Map<Image, Image>> cornerDecoratedImageMap = m_decoratedImageMap[i];
if (cornerDecoratedImageMap != null) { if (cornerDecoratedImageMap != null) {
for (Map<Image, Image> decoratedMap : cornerDecoratedImageMap.values()) { for (Map<Image, Image> decoratedMap : cornerDecoratedImageMap.values()) {
for (Image image : decoratedMap.values()) { for (Image image : decoratedMap.values()) {
@ -294,7 +294,6 @@ public class SWTResourceManager {
} }
} }
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// //
// Font // Font
@ -303,39 +302,45 @@ public class SWTResourceManager {
/** /**
* Maps font names to fonts. * Maps font names to fonts.
*/ */
private static Map<String, Font> fontMap = new HashMap<>(); private static Map<String, Font> m_fontMap = new HashMap<String, Font>();
/** /**
* Maps fonts to their bold versions. * Maps fonts to their bold versions.
*/ */
private static Map<Font, Font> fontToBoldFontMap = new HashMap<>(); private static Map<Font, Font> m_fontToBoldFontMap = new HashMap<Font, Font>();
/** /**
* Returns a {@link Font} based on its name, height and style. * Returns a {@link Font} based on its name, height and style.
* *
* @param name the name of the font * @param name
* @param height the height of the font * the name of the font
* @param style the style of the font * @param height
* the height of the font
* @param style
* the style of the font
* @return {@link Font} The font matching the name, height and style * @return {@link Font} The font matching the name, height and style
*/ */
public static Font getFont(String name, int height, int style) { public static Font getFont(String name, int height, int style) {
return getFont(name, height, style, false, false); return getFont(name, height, style, false, false);
} }
/** /**
* Returns a {@link Font} based on its name, height and style. Windows-specific * Returns a {@link Font} based on its name, height and style. Windows-specific strikeout and underline
* strikeout and underline flags are also supported. * flags are also supported.
* *
* @param name the name of the font * @param name
* @param size the size of the font * the name of the font
* @param style the style of the font * @param size
* @param strikeout the strikeout flag (warning: Windows only) * the size of the font
* @param underline the underline flag (warning: Windows only) * @param style
* @return {@link Font} The font matching the name, height, style, strikeout and * the style of the font
* underline * @param strikeout
* the strikeout flag (warning: Windows only)
* @param underline
* the underline flag (warning: Windows only)
* @return {@link Font} The font matching the name, height, style, strikeout and underline
*/ */
public static Font getFont(String name, int size, int style, boolean strikeout, boolean underline) { public static Font getFont(String name, int size, int style, boolean strikeout, boolean underline) {
String fontName = name + '|' + size + '|' + style + '|' + strikeout + '|' + underline; String fontName = name + '|' + size + '|' + style + '|' + strikeout + '|' + underline;
return fontMap.computeIfAbsent(fontName, k -> { Font font = m_fontMap.get(fontName);
if (font == null) {
FontData fontData = new FontData(name, size, style); FontData fontData = new FontData(name, size, style);
if (strikeout || underline) { if (strikeout || underline) {
try { try {
@ -349,45 +354,47 @@ public class SWTResourceManager {
logFontClass.getField("lfUnderline").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$ logFontClass.getField("lfUnderline").set(logFont, Byte.valueOf((byte) 1)); //$NON-NLS-1$
} }
} }
} catch (Exception e) { } catch (Throwable e) {
System.err.println("Unable to set underline or strikeout" + " (probably on a non-Windows platform). " + e); //$NON-NLS-1$ //$NON-NLS-2$
} }
} }
return new Font(Display.getCurrent(), fontData); font = new Font(Display.getCurrent(), fontData);
m_fontMap.put(fontName, font);
}); }
return font;
} }
/** /**
* Returns a bold version of the given {@link Font}. * Returns a bold version of the given {@link Font}.
* *
* @param baseFont the {@link Font} for which a bold version is desired * @param baseFont
* the {@link Font} for which a bold version is desired
* @return the bold version of the given {@link Font} * @return the bold version of the given {@link Font}
*/ */
public static Font getBoldFont(Font baseFont) { public static Font getBoldFont(Font baseFont) {
return fontToBoldFontMap.computeIfAbsent(baseFont, k -> { Font font = m_fontToBoldFontMap.get(baseFont);
FontData[] fontDatas = baseFont.getFontData(); if (font == null) {
FontData fontDatas[] = baseFont.getFontData();
FontData data = fontDatas[0]; FontData data = fontDatas[0];
return new Font(Display.getCurrent(), data.getName(), data.getHeight(), SWT.BOLD); font = new Font(Display.getCurrent(), data.getName(), data.getHeight(), SWT.BOLD);
}); m_fontToBoldFontMap.put(baseFont, font);
}
return font;
} }
/** /**
* Dispose all of the cached {@link Font}'s. * Dispose all of the cached {@link Font}'s.
*/ */
public static void disposeFonts() { public static void disposeFonts() {
// clear fonts // clear fonts
for (Font font : fontMap.values()) { for (Font font : m_fontMap.values()) {
font.dispose(); font.dispose();
} }
fontMap.clear(); m_fontMap.clear();
// clear bold fonts // clear bold fonts
for (Font font : fontToBoldFontMap.values()) { for (Font font : m_fontToBoldFontMap.values()) {
font.dispose(); font.dispose();
} }
fontToBoldFontMap.clear(); m_fontToBoldFontMap.clear();
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// //
// Cursor // Cursor
@ -396,38 +403,40 @@ public class SWTResourceManager {
/** /**
* Maps IDs to cursors. * Maps IDs to cursors.
*/ */
private static Map<Integer, Cursor> idToCursorMap = new HashMap<>(); private static Map<Integer, Cursor> m_idToCursorMap = new HashMap<Integer, Cursor>();
/** /**
* Returns the system cursor matching the specific ID. * Returns the system cursor matching the specific ID.
* *
* @param id int The ID value for the cursor * @param id
* int The ID value for the cursor
* @return Cursor The system cursor matching the specific ID * @return Cursor The system cursor matching the specific ID
*/ */
public static Cursor getCursor(int id) { public static Cursor getCursor(int id) {
Integer key = Integer.valueOf(id); Integer key = Integer.valueOf(id);
return idToCursorMap.computeIfAbsent(key, k -> new Cursor(Display.getDefault(), id)); Cursor cursor = m_idToCursorMap.get(key);
if (cursor == null) {
cursor = new Cursor(Display.getDefault(), id);
m_idToCursorMap.put(key, cursor);
}
return cursor;
} }
/** /**
* Dispose all of the cached cursors. * Dispose all of the cached cursors.
*/ */
public static void disposeCursors() { public static void disposeCursors() {
for (Cursor cursor : idToCursorMap.values()) { for (Cursor cursor : m_idToCursorMap.values()) {
cursor.dispose(); cursor.dispose();
} }
idToCursorMap.clear(); m_idToCursorMap.clear();
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// //
// General // General
// //
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
/** /**
* Dispose of cached objects and their underlying OS resources. This should only * Dispose of cached objects and their underlying OS resources. This should only be called when the cached
* be called when the cached objects are no longer needed (e.g. on application * objects are no longer needed (e.g. on application shutdown).
* shutdown).
*/ */
public static void dispose() { public static void dispose() {
disposeColors(); disposeColors();

View File

@ -787,18 +787,15 @@ public class WaveformViewer implements IFileChangeListener, IPreferenceChangeLis
protected void restoreWaveformViewerState(Map<String, String> state) { protected void restoreWaveformViewerState(Map<String, String> state) {
Integer waves = state.containsKey(SHOWN_WAVEFORM+"S") ? Integer.parseInt(state.get(SHOWN_WAVEFORM + "S")):0; //$NON-NLS-1$ //$NON-NLS-2$ Integer waves = state.containsKey(SHOWN_WAVEFORM+"S") ? Integer.parseInt(state.get(SHOWN_WAVEFORM + "S")):0; //$NON-NLS-1$ //$NON-NLS-2$
List<TrackEntry> trackEntries = new LinkedList<>(); List<TrackEntry> trackEntries = new LinkedList<>();
List<TrackEntry> selectedTrackEntries = new LinkedList<>();
for (int i = 0; i < waves; i++) { for (int i = 0; i < waves; i++) {
IWaveform waveform = database.getStreamByName(state.get(SHOWN_WAVEFORM + i)); IWaveform waveform = database.getStreamByName(state.get(SHOWN_WAVEFORM + i));
if (waveform != null) { if (waveform != null) {
TrackEntry trackEntry = waveformPane.addWaveform(waveform, -1); TrackEntry trackEntry = waveformPane.addWaveform(waveform, -1);
//check if t is selected //check if t is selected
boolean isSelected = Boolean.parseBoolean(state.get(SHOWN_WAVEFORM + i + WAVEFORM_SELECTED));
if(isSelected) {
trackEntry.selected = true;
} else {
trackEntry.selected = false;
}
trackEntries.add(trackEntry); trackEntries.add(trackEntry);
if(Boolean.parseBoolean(state.get(SHOWN_WAVEFORM + i + WAVEFORM_SELECTED)))
selectedTrackEntries.add(trackEntry);
String v = state.get(SHOWN_WAVEFORM + i + VALUE_DISPLAY); String v = state.get(SHOWN_WAVEFORM + i + VALUE_DISPLAY);
if(v!=null) if(v!=null)
trackEntry.valueDisplay=ValueDisplay.valueOf(v); trackEntry.valueDisplay=ValueDisplay.valueOf(v);
@ -856,6 +853,8 @@ public class WaveformViewer implements IFileChangeListener, IPreferenceChangeLis
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
} }
} }
ISelection sel = new StructuredSelection(selectedTrackEntries);
waveformPane.setSelection(sel);
updateAll(); updateAll();
} }

View File

@ -78,7 +78,6 @@
<repositories> <repositories>
<repository location="http://https://minres.github.io/SCViewer/repository" enabled="true" /> <repository location="http://https://minres.github.io/SCViewer/repository" enabled="true" />
<repository location="http://https://minres.github.io/SCViewer/repository" enabled="true" />
</repositories> </repositories>
<preferencesInfo> <preferencesInfo>