Compare commits
	
		
			10 Commits
		
	
	
		
			2.15.1
			...
			feature/cu
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7dbcffe95d | |||
| 20934a9f47 | |||
| 24890f9bbb | |||
| bd0629301b | |||
| 71da420d86 | |||
| f9be6758e2 | |||
| 93a8c067fc | |||
| 5a372d0f90 | |||
| 806000c4cc | |||
| c32d46cdc0 | 
							
								
								
									
										62
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @@ -30,25 +30,43 @@ Legend: | ||||
| * Mouse Scroll wheel: MScrl | ||||
| * Context any means Name List, Value List or Waveform | ||||
|  | ||||
| | Input     | Modifier | Context  | Action                            | | ||||
| |-----------|----------|----------|-----------------------------------| | ||||
| | LMB klick |          | any      | select                            | | ||||
| | LMB klick | Shift    | Waveform | move selected marker to position  | | ||||
| | LMB klick | Control  | Waveform | move cursor to position           | | ||||
| | LMB drag  |          | Waveform | zoom to range                     | | ||||
| | MMB klick |          | Waveform | move selected marker to position  | | ||||
| | MScrl     |          | any      | scroll window up/down             | | ||||
| | MScrl     | Shift    | any      | scroll window left/right          | | ||||
| | Key left  |          | Waveform | scroll window to the left (slow)  | | ||||
| | Key right |          | Waveform | scroll window to the right (slow) | | ||||
| | Key left  | Shift    | Waveform | scroll window to the left (fast)  | | ||||
| | Key right | Shift    | Waveform | scroll window to the right (fast) | | ||||
| | Key up    |          | Waveform | move selection up                 | | ||||
| | Key down  |          | Waveform | move selection down               | | ||||
| | Key up    | Control  | Waveform | move selected track up            | | ||||
| | Key down  | Control  | Waveform | move selected track down          | | ||||
| | Key +     | Control  | Waveform | zoom in                           | | ||||
| | Key -     | Control  | Waveform | zoom out                          | | ||||
| | Key Pos1  |          | Waveform | jump to selected marker           | | ||||
| | Key End   |          | Waveform | jump to cursor                    | | ||||
| | Key Del   |          | any      | delete selected entries           | | ||||
| | Input      | Modifier | Context  | Action                            | | ||||
| |------------|----------|----------|-----------------------------------| | ||||
| | LMB click  |          | any      | select                            | | ||||
| | LMB click  | Shift    | Waveform | move selected marker to position  | | ||||
| | LMB click  | Control  | Waveform | move cursor to position           | | ||||
| | LMB drag   |          | Waveform | zoom to range                     | | ||||
| | MMB click  |          | Waveform | move selected marker to position  | | ||||
| | MScrl      |          | any      | scroll window up/down             | | ||||
| | MScrl      | Shift    | any      | scroll window left/right          | | ||||
| | MScrl      | Control  | Waveform | zoom in/out                       | | ||||
| | Key left   |          | Waveform | scroll window to the left (slow)  | | ||||
| | Key right  |          | Waveform | scroll window to the right (slow) | | ||||
| | Key left   | Shift    | Waveform | scroll window to the left (fast)  | | ||||
| | Key right  | Shift    | Waveform | scroll window to the right (fast) | | ||||
| | Key up     |          | Waveform | move selection up                 | | ||||
| | Key down   |          | Waveform | move selection down               | | ||||
| | Key up     | Control  | Waveform | move selected track up            | | ||||
| | Key down   | Control  | Waveform | move selected track down          | | ||||
| | Key +      | Control  | Waveform | zoom in                           | | ||||
| | Key -      | Control  | Waveform | zoom out                          | | ||||
| | Key Pos1   |          | Waveform | jump to selected marker           | | ||||
| | 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                     | | ||||
|  | ||||
|   | ||||
| @@ -13,6 +13,7 @@ package com.minres.scviewer.database.ui.swt; | ||||
| import org.eclipse.core.runtime.IStatus; | ||||
| import org.eclipse.core.runtime.Plugin; | ||||
| import org.eclipse.core.runtime.Status; | ||||
| import org.eclipse.wb.swt.SWTResourceManager; | ||||
| import org.osgi.framework.BundleContext; | ||||
|  | ||||
| public class DatabaseUiPlugin extends Plugin { | ||||
| @@ -24,6 +25,7 @@ public class DatabaseUiPlugin extends Plugin { | ||||
|  | ||||
| 	@Override | ||||
| 	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...")); | ||||
| 	} | ||||
| } | ||||
| @@ -19,7 +19,7 @@ import org.eclipse.swt.graphics.Rectangle; | ||||
| import com.minres.scviewer.database.ui.WaveformColors; | ||||
|  | ||||
| public class TrackAreaPainter implements IPainter { | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 *  | ||||
| 	 */ | ||||
| @@ -35,14 +35,15 @@ public class TrackAreaPainter implements IPainter { | ||||
| 	} | ||||
|  | ||||
| 	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)); | ||||
| 	    proj.setBackground(this.waveCanvas.styleProvider.getColor(WaveformColors.TRACK_BG_EVEN)); | ||||
| 	    proj.setFillRule(SWT.FILL_EVEN_ODD); | ||||
| 	    proj.fillRectangle(area); | ||||
| 		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.setFillRule(SWT.FILL_EVEN_ODD); | ||||
| 		proj.fillRectangle(area); | ||||
| 		if(trackVerticalOffset.size()>0){ | ||||
| 			Integer firstKey=trackVerticalOffset.floorKey(area.y); | ||||
| 			if(firstKey==null) firstKey=trackVerticalOffset.firstKey(); | ||||
| 			Integer lastKey = trackVerticalOffset.floorKey(area.y+area.height); | ||||
| 			//if(lastKey==null) lastKey= trackVerticalOffset.lastKey(); | ||||
| 			Rectangle subArea = new Rectangle(area.x, 0, area.width, 0); | ||||
| 			if(lastKey.equals(firstKey)){ | ||||
| 				subArea.y=firstKey; | ||||
| @@ -61,9 +62,9 @@ public class TrackAreaPainter implements IPainter { | ||||
|  | ||||
| 	public void addTrackPainter(IWaveformPainter trackPainter){ | ||||
| 		trackVerticalOffset.put(trackPainter.getVerticalOffset()+waveCanvas.rulerHeight, trackPainter); | ||||
| 		 | ||||
|  | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	public int getHeight(){ | ||||
| 		if(trackVerticalOffset.size()==0) return 1; | ||||
| 		return trackVerticalOffset.lastKey() + trackVerticalOffset.lastEntry().getValue().getHeight(); | ||||
|   | ||||
| @@ -46,21 +46,24 @@ import com.minres.scviewer.database.ui.IWaveformZoom; | ||||
| import com.minres.scviewer.database.ui.TrackEntry; | ||||
| import com.minres.scviewer.database.ui.ZoomKind; | ||||
| 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 static final long ZOOM_FIT = -2; | ||||
|  | ||||
| 	public static final long ZOOM_FULL = -1; | ||||
|  | ||||
| 	private static final int INITIAL_ZOOM_BAR_MAX = 1000; | ||||
| 	 | ||||
| 	private boolean doubleBuffering = true; | ||||
|  | ||||
| 	IWaveformStyleProvider styleProvider; | ||||
|  | ||||
| 	private int scaleMagnitude = 6; | ||||
| 	 | ||||
|  | ||||
| 	private long scaleFactor = Constants.POWERS_OF_TEN[scaleMagnitude]; | ||||
| 	 | ||||
|  | ||||
| 	private long maxTime; | ||||
|  | ||||
| 	protected Point origin; /* original size */ | ||||
| @@ -81,22 +84,23 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{ | ||||
|  | ||||
| 	private List<CursorPainter> cursorPainters; | ||||
|  | ||||
| 	private ZoomBar horizontal; | ||||
|  | ||||
| 	private int[] lastHorSelection; | ||||
| 	 | ||||
| 	private long sliderScaleFactor = 1; | ||||
| 	 | ||||
| 	private ScrollBar vertical; | ||||
|  | ||||
| 	HashMap<IWaveform, IWaveformPainter> wave2painterMap; | ||||
| 	/** | ||||
| 	 * Constructor for ScrollableCanvas. | ||||
| 	 *  | ||||
| 	 * @param parent | ||||
| 	 *            the parent of this control.super(parent, style | SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.NO_REDRAW_RESIZE | SWT.V_SCROLL | SWT.H_SCROLL); | ||||
| 	 * @param style | ||||
| 	 *            the style of this control. | ||||
| 	 */ | ||||
| 	public WaveformCanvas(final Composite parent, int style, IWaveformStyleProvider styleProvider) { | ||||
| 		super(parent, style | SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.V_SCROLL | SWT.H_SCROLL); | ||||
|  | ||||
| 	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 ); | ||||
| 		this.styleProvider=styleProvider; | ||||
| 		addControlListener(new ControlAdapter() { /* resize listener. */ | ||||
| 			@Override | ||||
| 			public void controlResized(ControlEvent event) { | ||||
| 				syncScrollBars(); | ||||
| 				syncSb(); | ||||
| 			} | ||||
| 		}); | ||||
| 		addPaintListener((final PaintEvent event) -> paint(event.gc)); | ||||
| @@ -106,6 +110,8 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{ | ||||
| 		cursorPainters= new ArrayList<>(); | ||||
| 		wave2painterMap=new HashMap<>(); | ||||
|  | ||||
| 		horizontal = scrollbarProvider.getScrollBar(); | ||||
| 		vertical = getVerticalBar(); | ||||
| 		initScrollBars(); | ||||
| 		// order is important: it is bottom to top | ||||
| 		trackAreaPainter=new TrackAreaPainter(this); | ||||
| @@ -114,10 +120,10 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{ | ||||
| 		painterList.add(arrowPainter); | ||||
| 		rulerPainter=new RulerPainter(this); | ||||
| 		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); | ||||
| 		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); | ||||
| 		cursorPainters.add(marker); | ||||
| 		wave2painterMap=new HashMap<>(); | ||||
| @@ -149,15 +155,9 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{ | ||||
|  | ||||
| 	public void setOrigin(int x, int y) { | ||||
| 		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.y = y; | ||||
| 		syncScrollBars(); | ||||
| 		syncSb(); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| @@ -167,14 +167,20 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{ | ||||
|  | ||||
| 	public void setMaxTime(long 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 | ||||
| 	public long getScale() { | ||||
| 		return scaleFactor; | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	@Override | ||||
| 	public void setScale(long factor) { | ||||
| 		setScalingFactor(factor, (getMaxVisibleTime()+getMinVisibleTime())/2); | ||||
| @@ -206,37 +212,41 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{ | ||||
| 			factor=1; | ||||
| 		else if(factor>maxFactor) | ||||
| 			factor=maxFactor; | ||||
| 		if(factor!=scaleFactor || (getMaxVisibleTime()+getMinVisibleTime()/2) != centerTime) { | ||||
| 			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; | ||||
| 				} | ||||
| 			} | ||||
| 		if(factor!=getScale() || (getMaxVisibleTime()+getMinVisibleTime()/2) != centerTime) { | ||||
| 			updateScaleFactor(factor); | ||||
| 			ITx tx = arrowPainter.getTx(); | ||||
| 			arrowPainter.setTx(null); | ||||
| 			/* | ||||
| 			 * xc = tc/oldScaleFactor | ||||
| 			 * xoffs = xc+origin.x | ||||
| 			 * xcn = tc/newScaleFactor | ||||
| 			 * t0n = (xcn-xoffs)*scaleFactor | ||||
| 			 * t0n = (xcn-xoffs)*getScale() | ||||
| 			 */ | ||||
| 			long xoffs = clientAreaWidth/2; | ||||
| 			long xcn=centerTime/scaleFactor; // new total x-offset | ||||
| 			long xcn=centerTime/getScale(); // new total x-offset | ||||
| 			long originX=xcn-xoffs; | ||||
| 			if(originX>0) { | ||||
| 				origin.x=(int) -originX; // new cursor time offset relative to left border | ||||
| 			}else { | ||||
| 				origin.x=0; | ||||
| 			} | ||||
| 			syncScrollBars(); | ||||
| 			syncSb(); | ||||
| 			arrowPainter.setTx(tx);    		 | ||||
| 			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 | ||||
| 	public String timeToString(long time) { | ||||
| 		int idx = scaleMagnitude/3; | ||||
| @@ -245,7 +255,7 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{ | ||||
| 	} | ||||
|  | ||||
| 	public long getTimeForOffset(int xOffset){ | ||||
| 		return (xOffset-origin.x) * scaleFactor; | ||||
| 		return (xOffset-origin.x) * getScale(); | ||||
| 	} | ||||
|  | ||||
| 	public void addPainter(IPainter painter) { | ||||
| @@ -265,7 +275,7 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{ | ||||
| 	void clearAllWaveformPainter(boolean update) { | ||||
| 		trackAreaPainter.trackVerticalOffset.clear(); | ||||
| 		wave2painterMap.clear(); | ||||
| 		if(update) syncScrollBars(); | ||||
| 		if(update) syncSb(); | ||||
| 	} | ||||
|  | ||||
| 	public void addWaveformPainter(IWaveformPainter painter) { | ||||
| @@ -275,7 +285,7 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{ | ||||
| 	void addWaveformPainter(IWaveformPainter painter, boolean update) { | ||||
| 		trackAreaPainter.addTrackPainter(painter); | ||||
| 		wave2painterMap.put(painter.getTrackEntry().waveform, painter); | ||||
| 		if(update) syncScrollBars(); | ||||
| 		if(update) syncSb(); | ||||
| 	} | ||||
|  | ||||
| 	public List<CursorPainter> getCursorPainters() { | ||||
| @@ -284,26 +294,41 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{ | ||||
|  | ||||
| 	/* Initialize the scrollbar and register listeners. */ | ||||
| 	private void initScrollBars() { | ||||
| 		ScrollBar horizontal = getHorizontalBar(); | ||||
| 		horizontal.setEnabled(false); | ||||
| 		horizontal.setVisible(true); | ||||
| 		horizontal.addSelectionListener(new SelectionAdapter() { | ||||
| 			@Override | ||||
| 			public void widgetSelected(SelectionEvent event) { | ||||
| 				if (painterList.isEmpty()) | ||||
| 					return; | ||||
| 				setOrigin(-((ScrollBar) event.widget).getSelection(), origin.y); | ||||
| 				if (!painterList.isEmpty()) { | ||||
| 					int[] sel = horizontal.getSelection(); | ||||
| 					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.setVisible(true); | ||||
| 		vertical.addSelectionListener(new SelectionAdapter() { | ||||
| 			@Override | ||||
| 			public void widgetSelected(SelectionEvent event) { | ||||
| 				if (painterList.isEmpty()) | ||||
| 					return; | ||||
| 				setOrigin(origin.x, -((ScrollBar) event.widget).getSelection()); | ||||
| 				if (!painterList.isEmpty()) { | ||||
| 					origin.y=-vertical.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 | ||||
| 	 * :<b> transform, image size, client area</b>. | ||||
| 	 */ | ||||
| 	public void syncScrollBars() { | ||||
| 		if (painterList.isEmpty()) { | ||||
| 			redraw(); | ||||
| 			return; | ||||
| 	public void syncSb() { | ||||
| 		if (!painterList.isEmpty()) { | ||||
| 			syncHSb(); | ||||
| 			syncVSb(); | ||||
| 			fireSelectionEvent(); | ||||
| 		} | ||||
| 		int height = trackAreaPainter.getHeight(); // incl. Ruler | ||||
| 		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); | ||||
| 		redraw(); | ||||
| 	} | ||||
|  | ||||
| 		ScrollBar vertical = getVerticalBar(); | ||||
| 		vertical.setIncrement(getClientArea().height / 100); | ||||
| 		vertical.setPageIncrement(getClientArea().height); | ||||
| 		int clientHeighth = clientArea.height; | ||||
| 		if (height > clientHeighth) { /* image is higher than client area */ | ||||
| 	private void syncVSb() { | ||||
| 		Rectangle clientArea=getClientArea(); | ||||
| 		int height = trackAreaPainter.getHeight(); // incl. Ruler | ||||
| 		int clientHeight = clientArea.height; | ||||
| 		vertical.setIncrement(clientHeight / 100); | ||||
| 		vertical.setPageIncrement(clientHeight/2); | ||||
| 		if (height > clientHeight) { /* image is higher than client area */ | ||||
| 			vertical.setMinimum(0); | ||||
| 			vertical.setMaximum(height); | ||||
| 			vertical.setEnabled(true); | ||||
| 			if ( -origin.y > vertical.getMaximum() - clientHeighth) { | ||||
| 				origin.y = -vertical.getMaximum() + clientHeighth; | ||||
| 			if ( -origin.y > vertical.getMaximum() - clientHeight) { | ||||
| 				origin.y = -vertical.getMaximum() + clientHeight; | ||||
| 			} | ||||
| 		} else { /* image is less higher than client area */ | ||||
| 			vertical.setMaximum(clientHeighth); | ||||
| 			vertical.setMaximum(clientHeight); | ||||
| 			vertical.setEnabled(false); | ||||
| 		} | ||||
| 		vertical.setThumb(clientHeighth); | ||||
| 		vertical.setThumb(clientHeight); | ||||
| 		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 */ | ||||
| @@ -406,7 +429,7 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{ | ||||
| 					result.add(entry.getValue().getTrackEntry()); | ||||
| 				} | ||||
| 			} 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); | ||||
| 				} | ||||
| 			} | ||||
| @@ -434,11 +457,11 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{ | ||||
| 	} | ||||
|  | ||||
| 	public void reveal(ITx tx) { | ||||
| 		int lower = (int) (tx.getBeginTime() / scaleFactor); | ||||
| 		int higher = (int) (tx.getEndTime() / scaleFactor); | ||||
| 		int lower = (int) (tx.getBeginTime() / getScale()); | ||||
| 		int higher = (int) (tx.getEndTime() / getScale()); | ||||
| 		Point size = getSize(); | ||||
| 		size.x -= getVerticalBar().getSize().x + 2; | ||||
| 		size.y -= getHorizontalBar().getSize().y; | ||||
| 		size.x -= vertical.getSize().x + 2; | ||||
| 		size.y -= horizontal.getSize().y; | ||||
| 		if (lower < -origin.x) { | ||||
| 			setOrigin(-lower, origin.y); | ||||
| 		} else if (higher > (size.x - origin.x)) { | ||||
| @@ -467,9 +490,8 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{ | ||||
| 			if(te.waveform == waveform) { | ||||
| 				Point size = getSize(); | ||||
| 				size.y -=+rulerHeight; | ||||
| 				ScrollBar sb = getHorizontalBar(); | ||||
| 				if((sb.getStyle()&SWT.SCROLLBAR_OVERLAY)!=0 && sb.isVisible()) | ||||
| 					size.y-=  getHorizontalBar().getSize().y; | ||||
| 				if((horizontal.getStyle()&SWT.SCROLLBAR_OVERLAY)!=0 && horizontal.isVisible()) | ||||
| 					size.y-=  horizontal.getSize().y; | ||||
| 				int top = te.vOffset; | ||||
| 				int bottom = top + styleProvider.getTrackHeight(); | ||||
| 				if (top < -origin.y) { | ||||
| @@ -482,10 +504,10 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{ | ||||
| 	} | ||||
|  | ||||
| 	public void reveal(long time) { | ||||
| 		int scaledTime = (int) (time / scaleFactor); | ||||
| 		int scaledTime = (int) (time / getScale()); | ||||
| 		Point size = getSize(); | ||||
| 		size.x -= getVerticalBar().getSize().x + 2; | ||||
| 		size.y -= getHorizontalBar().getSize().y; | ||||
| 		size.x -= vertical.getSize().x + 2; | ||||
| 		size.y -= horizontal.getSize().y; | ||||
| 		if (scaledTime < -origin.x) { | ||||
| 			setOrigin(-scaledTime+10, origin.y); | ||||
| 		} else if (scaledTime > (size.x - origin.x)) { | ||||
| @@ -495,7 +517,7 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{ | ||||
|  | ||||
| 	@Override | ||||
| 	public void centerAt(long time) { | ||||
| 		int scaledTime = (int) (time / scaleFactor); | ||||
| 		int scaledTime = (int) (time / getScale()); | ||||
| 		int newX = -scaledTime+getWidth()/2; | ||||
| 		setOrigin(newX>0?0:newX, origin.y); | ||||
| 	} | ||||
| @@ -532,15 +554,15 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{ | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	 | ||||
|  | ||||
| 	@Override | ||||
| 	public long getMaxVisibleTime() { | ||||
| 		return (getClientArea().width-origin.x)*scaleFactor; | ||||
| 		return (getClientArea().width-origin.x)*getScale(); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| 	public long getMinVisibleTime() { | ||||
| 		return -origin.x * scaleFactor; | ||||
| 		return -origin.x * getScale(); | ||||
| 	} | ||||
|  | ||||
| 	@Override | ||||
| @@ -548,7 +570,7 @@ public class WaveformCanvas extends Canvas implements IWaveformZoom{ | ||||
| 		long duration = getMaxVisibleTime()-getMinVisibleTime(); | ||||
| 		if(time>0) { | ||||
| 			if((time+duration)<getMaxTime()) { | ||||
| 				int scaledTime = (int) (time / scaleFactor); | ||||
| 				int scaledTime = (int) (time / getScale()); | ||||
| 				setOrigin(-scaledTime, origin.y); | ||||
| 			} | ||||
| 		} else { | ||||
|   | ||||
| @@ -91,6 +91,7 @@ import com.minres.scviewer.database.ui.IWaveformStyleProvider; | ||||
| import com.minres.scviewer.database.ui.IWaveformView; | ||||
| import com.minres.scviewer.database.ui.IWaveformZoom; | ||||
| import com.minres.scviewer.database.ui.TrackEntry; | ||||
| import com.minres.scviewer.database.ui.swt.internal.slider.ZoomBar; | ||||
|  | ||||
| public class WaveformView implements IWaveformView { | ||||
|  | ||||
| @@ -112,6 +113,10 @@ public class WaveformView implements IWaveformView { | ||||
|  | ||||
| 	private final Canvas valueList; | ||||
|  | ||||
| 	private final Control nameFill; | ||||
| 	 | ||||
| 	private final Control valueFill; | ||||
|  | ||||
| 	final WaveformCanvas waveformCanvas; | ||||
|  | ||||
| 	final ToolTipHandler toolTipHandler; | ||||
| @@ -204,7 +209,7 @@ public class WaveformView implements IWaveformView { | ||||
| 					long endTime = waveformCanvas.getTimeForOffset(end.x); | ||||
| 					if(startTime<endTime) { | ||||
| 						waveformCanvas.setVisibleRange(startTime, endTime); | ||||
| 					} else { | ||||
| 					} else if(start.x!=end.x){ | ||||
| 						long targetTimeRange = startTime-endTime; | ||||
| 						long currentTimeRange = waveformCanvas.getMaxVisibleTime() - waveformCanvas.getMinVisibleTime(); | ||||
| 						long factor = currentTimeRange/targetTimeRange *waveformCanvas.getScale(); | ||||
| @@ -266,11 +271,24 @@ public class WaveformView implements IWaveformView { | ||||
| 			switch (e.type) { | ||||
| 			case SWT.MouseWheel: | ||||
| 				if((e.stateMask & SWT.CTRL) != 0) { | ||||
| 					if(e.count<0) | ||||
| 					if(e.count<0) // up scroll | ||||
| 						waveformCanvas.setScale(waveformCanvas.getScale()*11/10); | ||||
| 					else | ||||
| 					else // down scroll | ||||
| 						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; | ||||
| 			case SWT.MouseDown: | ||||
| 				start = new Point(e.x, e.y); | ||||
| @@ -330,8 +348,26 @@ public class WaveformView implements IWaveformView { | ||||
| 		rightSash.setBackground(SWTResourceManager.getColor(SWT.COLOR_GRAY)); | ||||
|  | ||||
| 		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 | ||||
| 		createTextPane(namePane, "Name"); | ||||
|  | ||||
| @@ -347,7 +383,10 @@ public class WaveformView implements IWaveformView { | ||||
| 			@Override | ||||
| 			public void controlResized(ControlEvent e) { | ||||
| 				nameListScrolled.getVerticalBar().setVisible(false); | ||||
|  | ||||
| 				if(nameListScrolled.getSize().y == nameList.getSize().y) { | ||||
| 					((GridData)nameFill.getLayoutData()).heightHint=18; | ||||
| 					namePane.layout(); | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
| 		nameList = new Canvas(nameListScrolled, SWT.NONE) { | ||||
| @@ -366,7 +405,8 @@ public class WaveformView implements IWaveformView { | ||||
| 		}); | ||||
| 		nameList.addMouseListener(nameValueMouseListener); | ||||
| 		nameListScrolled.setContent(nameList); | ||||
|  | ||||
| 		nameFill = createFill(namePane); | ||||
| 		 | ||||
| 		createTextPane(valuePane, "Value"); | ||||
|  | ||||
| 		valuePane.setBackground(SWTResourceManager.getColor(SWT.COLOR_WIDGET_BACKGROUND)); | ||||
| @@ -379,7 +419,10 @@ public class WaveformView implements IWaveformView { | ||||
| 			@Override | ||||
| 			public void controlResized(ControlEvent e) { | ||||
| 				valueListScrolled.getVerticalBar().setVisible(false); | ||||
|  | ||||
| 				if(valueListScrolled.getSize().y == valueList.getSize().y) { | ||||
| 					((GridData)valueFill.getLayoutData()).heightHint=18; | ||||
| 					valuePane.layout(); | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
| 		valueList = new Canvas(valueListScrolled, SWT.NONE) { | ||||
| @@ -398,6 +441,7 @@ public class WaveformView implements IWaveformView { | ||||
| 		}); | ||||
| 		valueList.addMouseListener(nameValueMouseListener); | ||||
| 		valueListScrolled.setContent(valueList); | ||||
| 		valueFill = createFill(valuePane); | ||||
|  | ||||
| 		waveformCanvas.setMaxTime(1); | ||||
| 		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) { | ||||
| 		GridLayout glNamePane = new GridLayout(1, false); | ||||
| 		glNamePane.verticalSpacing = 0; | ||||
| @@ -522,7 +575,7 @@ public class WaveformView implements IWaveformView { | ||||
| 			tracksVerticalHeight += streamEntry.height; | ||||
| 			even = !even; | ||||
| 		} | ||||
| 		waveformCanvas.syncScrollBars(); | ||||
| 		waveformCanvas.syncSb(); | ||||
| 		nameList.setSize(nameMaxWidth + 15, tracksVerticalHeight); | ||||
| 		nameListScrolled.setMinSize(nameMaxWidth + 15, tracksVerticalHeight); | ||||
| 		nameList.redraw(); | ||||
|   | ||||
| @@ -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 ); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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; | ||||
| 	} | ||||
| } | ||||
| @@ -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(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -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; | ||||
| 	} | ||||
| } | ||||
| @@ -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(); | ||||
| 	} | ||||
| } | ||||
| After Width: | Height: | Size: 352 B | 
| After Width: | Height: | Size: 326 B | 
| After Width: | Height: | Size: 352 B | 
| After Width: | Height: | Size: 305 B | 
| After Width: | Height: | Size: 302 B | 
| After Width: | Height: | Size: 339 B | 
| After Width: | Height: | Size: 350 B | 
| After Width: | Height: | Size: 336 B | 
| After Width: | Height: | Size: 352 B | 
| After Width: | Height: | Size: 329 B | 
| After Width: | Height: | Size: 326 B | 
| After Width: | Height: | Size: 349 B | 
| @@ -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 ); | ||||
|   } | ||||
| } | ||||
| @@ -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; | ||||
|   } | ||||
| } | ||||
| @@ -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 ) {} | ||||
| } | ||||
| @@ -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; | ||||
|   } | ||||
| } | ||||
| @@ -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 ) { | ||||
|   } | ||||
| } | ||||
| @@ -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 ); | ||||
|   } | ||||
| } | ||||
| @@ -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 ) {} | ||||
| } | ||||
| @@ -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; | ||||
|   } | ||||
| } | ||||
| @@ -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(); | ||||
|   } | ||||
| } | ||||
| @@ -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(); | ||||
|   } | ||||
| } | ||||
| @@ -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 ); | ||||
|   } | ||||
| } | ||||
| @@ -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() ); | ||||
|   } | ||||
| } | ||||
| @@ -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 ); | ||||
|   } | ||||
| } | ||||
| @@ -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(); | ||||
|   } | ||||
| } | ||||
| @@ -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 ) ); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -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 ) { | ||||
|   } | ||||
| } | ||||
| @@ -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 ); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -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; | ||||
|   } | ||||
| } | ||||
| @@ -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; | ||||
|   } | ||||
| } | ||||
| @@ -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 ); | ||||
|   } | ||||
| } | ||||
| @@ -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; | ||||
|   } | ||||
| } | ||||
| @@ -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; | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,7 @@ | ||||
| package com.minres.scviewer.database.ui.swt.sb; | ||||
|  | ||||
| import org.eclipse.swt.widgets.Control; | ||||
|  | ||||
| public interface ViewComponent { | ||||
|   Control getControl(); | ||||
| } | ||||
| @@ -29,17 +29,14 @@ import org.eclipse.swt.graphics.Rectangle; | ||||
| import org.eclipse.swt.widgets.Display; | ||||
|  | ||||
| /** | ||||
|  * Utility class for managing OS resources associated with SWT controls such as | ||||
|  * colors, fonts, images, etc. | ||||
|  * Utility class for managing OS resources associated with SWT controls such as colors, fonts, images, etc. | ||||
|  * <p> | ||||
|  * !!! IMPORTANT !!! Application code must explicitly invoke the | ||||
|  * <code>dispose()</code> method to release the operating system resources | ||||
|  * managed by cached objects when those objects and OS resources are no longer | ||||
|  * !!! IMPORTANT !!! Application code must explicitly invoke the <code>dispose()</code> method to release the | ||||
|  * operating system resources managed by cached objects when those objects and OS resources are no longer | ||||
|  * needed (e.g. on application shutdown) | ||||
|  * <p> | ||||
|  * This class may be freely distributed as part of any application or plugin. | ||||
|  * <p> | ||||
|  *  | ||||
|  * @author scheglov_ke | ||||
|  * @author Dan Rubel | ||||
|  */ | ||||
| @@ -49,54 +46,57 @@ public class SWTResourceManager { | ||||
| 	// Color | ||||
| 	// | ||||
| 	//////////////////////////////////////////////////////////////////////////// | ||||
| 	private static Map<RGB, Color> colorMap = new HashMap<>(); | ||||
|  | ||||
| 	private SWTResourceManager() {} | ||||
| 	 | ||||
| 	private static Map<RGB, Color> m_colorMap = new HashMap<RGB, Color>(); | ||||
| 	/** | ||||
| 	 * 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 | ||||
| 	 */ | ||||
| 	public static Color getColor(int systemColorID) { | ||||
| 		Display display = Display.getCurrent(); | ||||
| 		return display.getSystemColor(systemColorID); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Returns a {@link Color} given its red, green and blue component values. | ||||
| 	 *  | ||||
| 	 * @param r the red component of the color | ||||
| 	 * @param g the green component of the color | ||||
| 	 * @param b the blue component of the color | ||||
| 	 * @return the {@link Color} matching the given red, green and blue component | ||||
| 	 *         values | ||||
| 	 * @param r | ||||
| 	 *            the red component of the color | ||||
| 	 * @param g | ||||
| 	 *            the green component of the color | ||||
| 	 * @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) { | ||||
| 		return getColor(new RGB(r, g, b)); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * 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 | ||||
| 	 */ | ||||
| 	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. | ||||
| 	 */ | ||||
| 	public static void disposeColors() { | ||||
| 		for (Color color : colorMap.values()) { | ||||
| 		for (Color color : m_colorMap.values()) { | ||||
| 			color.dispose(); | ||||
| 		} | ||||
| 		colorMap.clear(); | ||||
| 		m_colorMap.clear(); | ||||
| 	} | ||||
|  | ||||
| 	//////////////////////////////////////////////////////////////////////////// | ||||
| 	// | ||||
| 	// Image | ||||
| @@ -105,12 +105,12 @@ public class SWTResourceManager { | ||||
| 	/** | ||||
| 	 * 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}. | ||||
| 	 *  | ||||
| 	 * @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 | ||||
| 	 */ | ||||
| 	protected static Image getImage(InputStream stream) throws IOException { | ||||
| @@ -125,55 +125,52 @@ public class SWTResourceManager { | ||||
| 			stream.close(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * 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 | ||||
| 	 */ | ||||
| 	public static Image getImage(String path) { | ||||
| 		Image image = imageMap.get(path); | ||||
| 		Image image = m_imageMap.get(path); | ||||
| 		if (image == null) { | ||||
| 			try { | ||||
| 				image = getImage(new FileInputStream(path)); | ||||
| 				imageMap.put(path, image); | ||||
| 				m_imageMap.put(path, image); | ||||
| 			} catch (Exception e) { | ||||
| 				image = getMissingImage(); | ||||
| 				imageMap.put(path, image); | ||||
| 				m_imageMap.put(path, image); | ||||
| 			} | ||||
| 		} | ||||
| 		return image; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Returns an {@link Image} stored in the file at the specified path relative to | ||||
| 	 * the specified class. | ||||
| 	 * Returns an {@link Image} stored in the file at the specified path relative to the specified class. | ||||
| 	 *  | ||||
| 	 * @param clazz the {@link Class} relative to which to find the image | ||||
| 	 * @param path  the path to the image file, if starts with <code>'/'</code> | ||||
| 	 * @param clazz | ||||
| 	 *            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 | ||||
| 	 */ | ||||
| 	public static Image getImage(Class<?> clazz, String path) { | ||||
| 		String key = clazz.getName() + '|' + path; | ||||
| 		Image image = imageMap.get(key); | ||||
| 		Image image = m_imageMap.get(key); | ||||
| 		if (image == null) { | ||||
| 			try { | ||||
| 				image = getImage(clazz.getResourceAsStream(path)); | ||||
| 				imageMap.put(key, image); | ||||
| 				m_imageMap.put(key, image); | ||||
| 			} catch (Exception e) { | ||||
| 				image = getMissingImage(); | ||||
| 				imageMap.put(key, image); | ||||
| 				m_imageMap.put(key, image); | ||||
| 			} | ||||
| 		} | ||||
| 		return image; | ||||
| 	} | ||||
|  | ||||
| 	private static final int MISSING_IMAGE_SIZE = 10; | ||||
|  | ||||
| 	/** | ||||
| 	 * @return the small {@link Image} that can be used as placeholder for missing | ||||
| 	 *         image. | ||||
| 	 * @return the small {@link Image} that can be used as placeholder for missing image. | ||||
| 	 */ | ||||
| 	private static Image getMissingImage() { | ||||
| 		Image image = new Image(Display.getCurrent(), MISSING_IMAGE_SIZE, MISSING_IMAGE_SIZE); | ||||
| @@ -185,7 +182,6 @@ public class SWTResourceManager { | ||||
| 		// | ||||
| 		return 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; | ||||
| 	/** | ||||
| 	 * Style constant for placing decorator image in bottom left corner of base | ||||
| 	 * image. | ||||
| 	 * Style constant for placing decorator image in bottom left corner of base image. | ||||
| 	 */ | ||||
| 	public static final int BOTTOM_LEFT = 3; | ||||
| 	/** | ||||
| 	 * Style constant for placing decorator image in bottom right corner of base | ||||
| 	 * image. | ||||
| 	 * Style constant for placing decorator image in bottom right corner of base image. | ||||
| 	 */ | ||||
| 	public static final int BOTTOM_RIGHT = 4; | ||||
| 	/** | ||||
| @@ -212,77 +206,83 @@ public class SWTResourceManager { | ||||
| 	 * Maps images to decorated images. | ||||
| 	 */ | ||||
| 	@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. | ||||
| 	 *  | ||||
| 	 * @param baseImage the base {@link Image} that should be decorated | ||||
| 	 * @param decorator the {@link Image} to decorate the base image | ||||
| 	 * @param baseImage | ||||
| 	 *            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 | ||||
| 	 */ | ||||
| 	public static Image decorateImage(Image baseImage, Image decorator) { | ||||
| 		return decorateImage(baseImage, decorator, BOTTOM_RIGHT); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Returns an {@link Image} composed of a base image decorated by another image. | ||||
| 	 *  | ||||
| 	 * @param baseImage the base {@link Image} that should be decorated | ||||
| 	 * @param decorator the {@link Image} to decorate the base image | ||||
| 	 * @param corner    the corner to place decorator image | ||||
| 	 * @param baseImage | ||||
| 	 *            the base {@link Image} that should be decorated | ||||
| 	 * @param decorator | ||||
| 	 *            the {@link Image} to decorate the base image | ||||
| 	 * @param corner | ||||
| 	 *            the corner to place decorator image | ||||
| 	 * @return the resulting decorated {@link Image} | ||||
| 	 */ | ||||
| 	public static Image decorateImage(final Image baseImage, final Image decorator, final int corner) { | ||||
| 		if (corner <= 0 || corner >= LAST_CORNER_KEY) { | ||||
| 			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) { | ||||
| 			cornerDecoratedImageMap = new HashMap<>(); | ||||
| 			decoratedImageMap[corner] = cornerDecoratedImageMap; | ||||
| 			cornerDecoratedImageMap = new HashMap<Image, Map<Image, Image>>(); | ||||
| 			m_decoratedImageMap[corner] = cornerDecoratedImageMap; | ||||
| 		} | ||||
| 		Map<Image, Image> decoratedMap = cornerDecoratedImageMap.computeIfAbsent(baseImage, | ||||
| 				k -> new HashMap<Image, Image>()); | ||||
| 		return decoratedMap.computeIfAbsent(decorator, k -> { | ||||
| 		Map<Image, Image> decoratedMap = cornerDecoratedImageMap.get(baseImage); | ||||
| 		if (decoratedMap == null) { | ||||
| 			decoratedMap = new HashMap<Image, Image>(); | ||||
| 			cornerDecoratedImageMap.put(baseImage, decoratedMap); | ||||
| 		} | ||||
| 		// | ||||
| 		Image result = decoratedMap.get(decorator); | ||||
| 		if (result == null) { | ||||
| 			Rectangle bib = baseImage.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.drawImage(baseImage, 0, 0); | ||||
| 			switch (corner) { | ||||
| 			case TOP_LEFT: | ||||
| 			if (corner == TOP_LEFT) { | ||||
| 				gc.drawImage(decorator, 0, 0); | ||||
| 				break; | ||||
| 			case TOP_RIGHT: | ||||
| 			} else if (corner == TOP_RIGHT) { | ||||
| 				gc.drawImage(decorator, bib.width - dib.width, 0); | ||||
| 				break; | ||||
| 			case BOTTOM_LEFT: | ||||
| 			} else if (corner == BOTTOM_LEFT) { | ||||
| 				gc.drawImage(decorator, 0, bib.height - dib.height); | ||||
| 				break; | ||||
| 			case BOTTOM_RIGHT: | ||||
| 			} else if (corner == BOTTOM_RIGHT) { | ||||
| 				gc.drawImage(decorator, bib.width - dib.width, bib.height - dib.height); | ||||
| 				break; | ||||
| 			default: | ||||
| 				// do nothing | ||||
| 			} | ||||
| 			gc.dispose(); | ||||
| 			return result; | ||||
| 		}); | ||||
| 			// | ||||
| 			decoratedMap.put(decorator, result); | ||||
| 		} | ||||
| 		return result; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Dispose all of the cached {@link Image}'s. | ||||
| 	 */ | ||||
| 	public static void disposeImages() { | ||||
| 		// 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 | ||||
| 		for (int i = 0; i < decoratedImageMap.length; i++) { | ||||
| 			Map<Image, Map<Image, Image>> cornerDecoratedImageMap = decoratedImageMap[i]; | ||||
| 		for (int i = 0; i < m_decoratedImageMap.length; i++) { | ||||
| 			Map<Image, Map<Image, Image>> cornerDecoratedImageMap = m_decoratedImageMap[i]; | ||||
| 			if (cornerDecoratedImageMap != null) { | ||||
| 				for (Map<Image, Image> decoratedMap : cornerDecoratedImageMap.values()) { | ||||
| 					for (Image image : decoratedMap.values()) { | ||||
| @@ -294,7 +294,6 @@ public class SWTResourceManager { | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	//////////////////////////////////////////////////////////////////////////// | ||||
| 	// | ||||
| 	// Font | ||||
| @@ -303,39 +302,45 @@ public class SWTResourceManager { | ||||
| 	/** | ||||
| 	 * 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. | ||||
| 	 */ | ||||
| 	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. | ||||
| 	 *  | ||||
| 	 * @param name   the name of the font | ||||
| 	 * @param height the height of the font | ||||
| 	 * @param style  the style of the font | ||||
| 	 * @param name | ||||
| 	 *            the name 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 | ||||
| 	 */ | ||||
| 	public static Font getFont(String name, int height, int style) { | ||||
| 		return getFont(name, height, style, false, false); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Returns a {@link Font} based on its name, height and style. Windows-specific | ||||
| 	 * strikeout and underline flags are also supported. | ||||
| 	 * Returns a {@link Font} based on its name, height and style. Windows-specific strikeout and underline | ||||
| 	 * flags are also supported. | ||||
| 	 *  | ||||
| 	 * @param name      the name of the font | ||||
| 	 * @param size      the size of the font | ||||
| 	 * @param style     the style of the font | ||||
| 	 * @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 | ||||
| 	 * @param name | ||||
| 	 *            the name of the font | ||||
| 	 * @param size | ||||
| 	 *            the size of the font | ||||
| 	 * @param style | ||||
| 	 *            the style of the font | ||||
| 	 * @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) { | ||||
| 		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); | ||||
| 			if (strikeout || underline) { | ||||
| 				try { | ||||
| @@ -349,45 +354,47 @@ public class SWTResourceManager { | ||||
| 							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}. | ||||
| 	 *  | ||||
| 	 * @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} | ||||
| 	 */ | ||||
| 	public static Font getBoldFont(Font baseFont) { | ||||
| 		return fontToBoldFontMap.computeIfAbsent(baseFont, k -> { | ||||
| 			FontData[] fontDatas = baseFont.getFontData(); | ||||
| 		Font font = m_fontToBoldFontMap.get(baseFont); | ||||
| 		if (font == null) { | ||||
| 			FontData fontDatas[] = baseFont.getFontData(); | ||||
| 			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. | ||||
| 	 */ | ||||
| 	public static void disposeFonts() { | ||||
| 		// clear fonts | ||||
| 		for (Font font : fontMap.values()) { | ||||
| 		for (Font font : m_fontMap.values()) { | ||||
| 			font.dispose(); | ||||
| 		} | ||||
| 		fontMap.clear(); | ||||
| 		m_fontMap.clear(); | ||||
| 		// clear bold fonts | ||||
| 		for (Font font : fontToBoldFontMap.values()) { | ||||
| 		for (Font font : m_fontToBoldFontMap.values()) { | ||||
| 			font.dispose(); | ||||
| 		} | ||||
| 		fontToBoldFontMap.clear(); | ||||
| 		m_fontToBoldFontMap.clear(); | ||||
| 	} | ||||
|  | ||||
| 	//////////////////////////////////////////////////////////////////////////// | ||||
| 	// | ||||
| 	// Cursor | ||||
| @@ -396,38 +403,40 @@ public class SWTResourceManager { | ||||
| 	/** | ||||
| 	 * 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. | ||||
| 	 *  | ||||
| 	 * @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 | ||||
| 	 */ | ||||
| 	public static Cursor getCursor(int 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. | ||||
| 	 */ | ||||
| 	public static void disposeCursors() { | ||||
| 		for (Cursor cursor : idToCursorMap.values()) { | ||||
| 		for (Cursor cursor : m_idToCursorMap.values()) { | ||||
| 			cursor.dispose(); | ||||
| 		} | ||||
| 		idToCursorMap.clear(); | ||||
| 		m_idToCursorMap.clear(); | ||||
| 	} | ||||
|  | ||||
| 	//////////////////////////////////////////////////////////////////////////// | ||||
| 	// | ||||
| 	// General | ||||
| 	// | ||||
| 	//////////////////////////////////////////////////////////////////////////// | ||||
| 	/** | ||||
| 	 * Dispose of cached objects and their underlying OS resources. This should only | ||||
| 	 * be called when the cached objects are no longer needed (e.g. on application | ||||
| 	 * shutdown). | ||||
| 	 * Dispose of cached objects and their underlying OS resources. This should only be called when the cached | ||||
| 	 * objects are no longer needed (e.g. on application shutdown). | ||||
| 	 */ | ||||
| 	public static void dispose() { | ||||
| 		disposeColors(); | ||||
|   | ||||
| @@ -787,18 +787,15 @@ public class WaveformViewer implements IFileChangeListener, IPreferenceChangeLis | ||||
| 	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$ | ||||
| 		List<TrackEntry> trackEntries = new LinkedList<>(); | ||||
| 		List<TrackEntry> selectedTrackEntries = new LinkedList<>(); | ||||
| 		for (int i = 0; i < waves; i++) { | ||||
| 			IWaveform waveform = database.getStreamByName(state.get(SHOWN_WAVEFORM + i)); | ||||
| 			if (waveform != null) { | ||||
| 				TrackEntry trackEntry = waveformPane.addWaveform(waveform, -1); | ||||
| 				//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); | ||||
| 				if(Boolean.parseBoolean(state.get(SHOWN_WAVEFORM + i + WAVEFORM_SELECTED))) | ||||
| 					selectedTrackEntries.add(trackEntry); | ||||
| 				String v = state.get(SHOWN_WAVEFORM + i + VALUE_DISPLAY); | ||||
| 				if(v!=null) | ||||
| 					trackEntry.valueDisplay=ValueDisplay.valueOf(v); | ||||
| @@ -856,6 +853,8 @@ public class WaveformViewer implements IFileChangeListener, IPreferenceChangeLis | ||||
| 			} catch (NumberFormatException e) { | ||||
| 			} | ||||
| 		} | ||||
| 		ISelection sel = new StructuredSelection(selectedTrackEntries); | ||||
| 		waveformPane.setSelection(sel); | ||||
| 		updateAll(); | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
|    <extension | ||||
|          point="org.eclipse.ui.views"> | ||||
|       <category | ||||
|             name="TxViewer" | ||||
|             name="SCViewer" | ||||
|             id="com.minres.scviewer.ui"> | ||||
|       </category> | ||||
|    </extension> | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								plugins/com.minres.scviewer.ui/res/images/wave.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.2 KiB | 
| @@ -80,7 +80,6 @@ | ||||
|  | ||||
|    <repositories> | ||||
|       <repository location="http://https://minres.github.io/SCViewer/repository" enabled="true" /> | ||||
|       <repository location="http://https://minres.github.io/SCViewer/repository" enabled="true" /> | ||||
|    </repositories> | ||||
|  | ||||
|    <preferencesInfo> | ||||
|   | ||||