Compare commits

...

111 Commits

Author SHA1 Message Date
7dbcffe95d fix layout for auto-hiding scrollbar (e.g. GTK) 2022-01-07 21:25:01 +01:00
20934a9f47 fix behavior of ZoomBar used by WaveformCanvas 2022-01-07 19:42:27 +01:00
24890f9bbb change behavior with a clear distinction
If no modifier key is used the zoombar acts as a scroll bar. If ctrl is
used it modifies the zoom level, shift is a speed modifier
2022-01-06 19:15:31 +01:00
bd0629301b add next increment - basically working 2022-01-06 17:48:40 +01:00
71da420d86 checkpoint development 2022-01-05 21:51:57 +01:00
f9be6758e2 implement scrollbar adapter and first version of zooming scrollbar 2022-01-03 21:46:40 +01:00
93a8c067fc add sliuder variant 2022-01-03 14:15:59 +01:00
5a372d0f90 add first elements 2022-01-02 15:51:52 +01:00
806000c4cc add missing icon 2021-12-14 21:16:11 +01:00
c32d46cdc0 Merge branch 'release/2.15.1' into develop 2021-12-02 16:55:18 +01:00
5736279e8d update version numbers 2021-12-02 16:55:07 +01:00
e3f4dc6616 fix loading of corrupted uncompressed SCV files 2021-12-02 16:47:12 +01:00
a64e06ff7a Merge branch 'master' into develop 2021-11-28 12:01:29 +01:00
a42b786835 fixe some naming 2021-11-28 11:39:51 +01:00
e76000d87b fix path for gh-pages checkout 2021-11-27 17:16:56 +01:00
60ead71029 Merge branch 'release/2.15.0' into develop 2021-11-27 16:49:01 +01:00
4c48fda5ad Merge branch 'release/2.15.0' 2021-11-27 16:49:01 +01:00
6a591f2dbc add gh-pages deployment steps 2021-11-27 16:48:09 +01:00
86c30ad948 update version numbers 2021-11-27 15:39:07 +01:00
0ae055b486 Merge branch 'feature/continous_zoom' into develop 2021-11-27 15:22:44 +01:00
efcd4f5ab8 update tool bar actions and icons 2021-11-27 15:21:17 +01:00
232d2d4275 move zomm/navigate toolbar to view 2021-11-27 14:31:39 +01:00
44f96e5383 cleanup imports 2021-11-27 14:04:25 +01:00
44812310b0 implement continous zoom and removed zoom level status control 2021-11-27 14:04:06 +01:00
6ef91bb5e7 Merge branch 'release/2.14.2' 2021-11-18 14:31:09 +01:00
aa459b0ea6 Merge branch 'release/2.14.2' into develop 2021-11-18 14:31:09 +01:00
a2adf66618 update version numbers 2021-11-18 14:30:54 +01:00
3405e90df9 fix behavior of help window 2021-11-18 14:24:08 +01:00
9a59947e67 fix handling of key short cuts 2021-11-18 12:55:23 +01:00
9384d3278c remove hierarchy in poms 2021-11-18 08:41:52 +01:00
1af3171b2e cleanup imports 2021-11-16 14:15:33 +01:00
03fd9e154b Merge branch 'master' into develop 2021-11-16 14:13:22 +01:00
3572f683e3 add zoom levels 2021-11-16 14:12:10 +01:00
524ffb189c fix MT race condition 2021-11-16 14:11:30 +01:00
7fac6c8f74 Merge branch 'release/2.14.1' into develop 2021-11-16 11:32:41 +01:00
037c645075 Merge branch 'release/2.14.1' 2021-11-16 11:32:41 +01:00
2f9bd29dc8 update version numbers 2021-11-16 11:32:34 +01:00
92662c546a fix zoom level calculation 2021-11-16 11:28:32 +01:00
efa6544623 cleanup 2021-11-16 11:27:58 +01:00
52cf9daeec unify time printing (#69) 2021-11-16 09:37:50 +01:00
4a315722b1 add help browser window instead of dialog 2021-11-16 09:33:09 +01:00
a52efd1a12 Merge branch 'master' into develop 2021-11-15 22:25:12 +01:00
535df30ada Merge branch 'release/2.14.0' into develop 2021-11-15 21:38:51 +01:00
bad34dd1d1 Merge branch 'release/2.14.0' 2021-11-15 21:38:51 +01:00
5ac7f05f57 Merge branch 'master' of https://git.minres.com/VP-Tools/SCViewer 2021-11-15 21:37:51 +01:00
8353b59a27 update version numbers for release
This release fixes the following tickets:
* #62: Instead of or in addition to the Restore Default Zoom button, add
Full Zoom Out button.
* #64: Load multiple trace files and enable the possibility to view
signals from different files in a single window.
* partial #66: key shortcuts for actions like zoom in/out, go to
next/prev event etc
* #70: add full zoom and zoom between markers (once we had markers)
* #75: marker is often hiding events underneath for highlighting and
getting transaction details
* #82: Implement help for keyboard shortcuts
2021-11-15 21:29:08 +01:00
2c6ca6c376 update version number to 2.14 2021-11-15 21:25:05 +01:00
f4b03cb0e6 extend zoom handling (#70) and hide tx property panes (#58) 2021-11-15 21:23:13 +01:00
c7858997c0 update README.md 2021-11-15 15:18:56 +01:00
bd99ab3992 corrected parent path im poms 2021-11-15 15:14:04 +01:00
66f365d38d externalize string in HelpDialog class 2021-11-15 15:14:04 +01:00
59987f262d update key bindings 2021-11-15 15:14:04 +01:00
452a28362e fix help link 2021-11-15 15:14:04 +01:00
d6805f383b add help dialog 2021-11-15 15:14:04 +01:00
076611eec7 allow multiple loading of same format 2021-11-15 15:14:04 +01:00
e0fa55e2c0 corrected parent path im poms 2021-11-15 14:32:55 +01:00
9ea1994228 update key bindings 2021-11-15 14:16:46 +01:00
36f628c365 add help dialog 2021-11-15 13:07:58 +01:00
ff87e72510 allow multiple loading of same format 2021-11-15 10:52:59 +01:00
aef1e29a53 Merge branch 'release/2.13.2' into develop 2021-07-11 14:22:41 +02:00
1ebf9ba382 update version numbers 2021-07-11 13:47:48 +02:00
929408d08c fix missing gzip txlog file filter in file dialog 2021-07-11 13:47:27 +02:00
f57fb93525 fix reload issues 2021-07-11 13:46:35 +02:00
788065e456 Merge branch 'release/2.13.1' 2021-03-25 21:09:49 +00:00
22b46e0525 fix fit zoom level calculation 2021-03-25 21:07:05 +00:00
b75018239a Merge branch 'master' into develop
Conflicts:
	features/com.minres.scviewer.database.feature/pom.xml
	features/com.minres.scviewer.e4.feature/pom.xml
	features/com.minres.scviewer.e4.platform.feature/pom.xml
	features/com.minres.scviewer.feature/pom.xml
	features/com.minres.scviewer.ui.feature/pom.xml
	plugins/com.minres.scviewer.database.sqlite/pom.xml
	plugins/com.minres.scviewer.database.text/pom.xml
	plugins/com.minres.scviewer.database.ui.swt/pom.xml
	plugins/com.minres.scviewer.database.vcd/pom.xml
	plugins/com.minres.scviewer.database/pom.xml
	plugins/com.minres.scviewer.e4.application/META-INF/MANIFEST.MF
	plugins/com.minres.scviewer.e4.application/pom.xml
	plugins/com.minres.scviewer.ui/pom.xml
	pom.xml
	products/com.minres.scviewer.e4.product/pom.xml
	products/com.minres.scviewer.e4.product/scviewer.product
	releng/com.minres.scviewer.target/pom.xml
	releng/com.minres.scviewer.updateSite/pom.xml
	tests/com.minres.scviewer.database.test/pom.xml
2021-03-25 20:46:30 +00:00
598bb8eec7 Merge remote-tracking branch 'origin/master' 2021-03-25 20:33:25 +00:00
869265fc13 add gzip files to file dialog filter 2021-03-25 20:27:55 +00:00
5ad813527a fix CLI handling 2021-03-25 20:27:55 +00:00
73f8d3d50a fix full name display of generators 2021-03-25 20:27:55 +00:00
da1701195d update version numbers 2021-03-25 20:27:55 +00:00
9b6334509e update version numbers 2021-03-25 20:27:54 +00:00
ac4acc34a4 fix warning and left-overs 2021-03-25 20:27:54 +00:00
b6963f38d6 move interface to primitive types 2021-03-25 20:27:54 +00:00
182a036ade some more refactoring 2021-03-25 20:27:53 +00:00
97f2182290 adapt painter 2021-03-25 20:27:53 +00:00
1986a8c9c3 add explicit event list 2021-03-25 20:27:53 +00:00
6905d96329 add TreeMap facade 2021-03-25 20:27:52 +00:00
818f786b1d add dispose check in case view is closed while loading db 2021-03-25 20:27:52 +00:00
2948c1bd33 update version numbers 2021-03-25 20:22:15 +00:00
78faab404c apply UI fixes
* wrong arrow target of transaction
* wrong zomm factor calculation for zoom fit
* streams and waveforms not visible if at root level
2021-03-25 20:18:50 +00:00
64b10970a8 add gzip files to file dialog filter 2021-03-02 21:55:52 +01:00
6ab8fd232e fix CLI handling 2021-03-02 21:55:52 +01:00
f337a94112 fix full name display of generators 2021-03-02 21:55:52 +01:00
0135631a3e update version numbers 2021-03-02 21:55:52 +01:00
d0e1e8801f update version numbers 2021-03-02 21:55:52 +01:00
d1b3a91979 fix warning and left-overs 2021-03-02 21:55:52 +01:00
45c1396e0e move interface to primitive types 2021-03-02 21:55:51 +01:00
b7301733f0 some more refactoring 2021-03-02 21:55:51 +01:00
5df91dbaa8 adapt painter 2021-03-02 21:55:51 +01:00
68918689e7 add explicit event list 2021-03-02 21:55:51 +01:00
c41dd646da add TreeMap facade 2021-03-02 21:55:51 +01:00
a077389b83 add dispose check in case view is closed while loading db 2021-03-02 21:55:51 +01:00
caa37375c0 add gzip files to file dialog filter 2021-03-02 21:42:54 +01:00
3daea8ab43 fix CLI handling 2021-03-02 19:32:21 +01:00
c5d77af0d0 fix full name display of generators 2021-03-02 19:32:09 +01:00
7f7fdf09f4 update version numbers 2021-02-27 19:13:28 +00:00
7aba6a2ecb update version numbers 2021-02-27 19:13:17 +00:00
012395b933 Merge branch 'feature/eclipse_collections' into develop 2021-02-27 18:47:31 +00:00
787e3accc0 fix warning and left-overs 2021-02-27 14:00:29 +00:00
b778940c83 move interface to primitive types 2021-02-27 13:59:00 +00:00
71297c4e5a some more refactoring 2021-02-27 13:47:37 +00:00
1d2395e00d adapt painter 2021-02-27 13:33:15 +00:00
b69e1886b9 add explicit event list 2021-02-27 13:27:51 +00:00
d65803a4b7 add TreeMap facade 2021-02-26 11:57:54 +00:00
0e49a68e09 add dispose check in case view is closed while loading db 2021-02-24 08:46:19 +00:00
7af5593fd4 Merge branch 'release/2.12.2' into develop 2021-02-24 07:05:00 +00:00
25545dac51 Merge branch 'release/2.12.2' 2021-02-24 07:04:59 +00:00
df77af64ca update version numbers 2021-02-24 07:04:47 +00:00
e264ab2cbe fix name display in waveform view (#6) 2021-02-24 06:56:28 +00:00
8c17ed4146 Merge branch 'release/2.12.1' into develop 2021-02-18 08:19:17 +00:00
168 changed files with 6889 additions and 1695 deletions

View File

@ -12,7 +12,8 @@
<booleanAttribute key="M2_UPDATE_SNAPSHOTS" value="false"/>
<stringAttribute key="M2_USER_SETTINGS" value=""/>
<booleanAttribute key="M2_WORKSPACE_RESOLUTION" value="true"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_ATTR_USE_ARGFILE" value="false"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_CLASSPATH_ONLY_JAR" value="false"/>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.launching.macosx.MacOSXType/AdoptOpenJDK 8 [1.8.0_232]"/>
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc:com.minres.scviewer.parent}"/>
</launchConfiguration>

View File

@ -18,14 +18,55 @@ The viewer has the following features
- sqlite based
- visualization of transaction relations
To build the plugins the Eclipse SDK or PDE can be used. In both cases the Groovy
eclipse plugin (http://groovy.codehaus.org/Eclipse+Plugin or Market) has to be
installed.
To build the plugins the Eclipse SDK or PDE can be used.
Key Shortcuts
=============
Legend:
* Left Mouse Button: LMB
* Middle Mouse Button: MMB
* Mouse Scroll wheel: MScrl
* Context any means Name List, Value List or Waveform
| 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 |
TODO
====
- add more tests
- move to feature based product to allow automatic updates
- improve graphics
- catch-up e3 plugin to functionality of e4 product
- add calculated traces

View File

@ -27,12 +27,12 @@ http://www.eclipse.org/legal/epl-v10.html
</url>
<requires>
<import plugin="org.codehaus.groovy" version="2.5.8" match="greaterOrEqual"/>
<import plugin="org.eclipse.osgi.services" version="3.4.0" match="greaterOrEqual"/>
<import plugin="com.google.guava" version="15.0.0" match="greaterOrEqual"/>
<import plugin="org.eclipse.osgi"/>
<import plugin="com.minres.scviewer.database" version="1.0.0" match="greaterOrEqual"/>
<import plugin="org.eclipse.core.runtime"/>
<import feature="org.eclipse.collections.feature" version="10.4.0.v20200820-2049"/>
</requires>
<plugin

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.minres.scviewer</groupId>
<artifactId>com.minres.scviewer.parent</artifactId>
<version>2.12.1</version>
<version>2.15.1</version>
<relativePath>../..</relativePath>
</parent>
<version>3.0.0-SNAPSHOT</version>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.minres.scviewer</groupId>
<artifactId>com.minres.scviewer.parent</artifactId>
<version>2.12.1</version>
<version>2.15.1</version>
<relativePath>../..</relativePath>
</parent>
<version>1.1.0-SNAPSHOT</version>

View File

@ -29,6 +29,10 @@
id="org.eclipse.emf.common"
version="0.0.0"/>
<includes
id="org.eclipse.collections.feature"
version="0.0.0"/>
<requires>
<import plugin="org.eclipse.core.expressions" version="3.2.0" match="compatible"/>
<import plugin="org.eclipse.core.filesystem" version="1.3.0" match="compatible"/>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.minres.scviewer</groupId>
<artifactId>com.minres.scviewer.parent</artifactId>
<version>2.12.1</version>
<version>2.15.1</version>
<relativePath>../..</relativePath>
</parent>
<version>1.0.0-SNAPSHOT</version>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.minres.scviewer</groupId>
<artifactId>com.minres.scviewer.parent</artifactId>
<version>2.12.1</version>
<version>2.15.1</version>
<relativePath>../..</relativePath>
</parent>
<version>1.1.0-SNAPSHOT</version>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.minres.scviewer</groupId>
<artifactId>com.minres.scviewer.parent</artifactId>
<version>2.12.1</version>
<version>2.15.1</version>
<relativePath>../..</relativePath>
</parent>
<version>1.1.0-SNAPSHOT</version>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>com.minres.scviewer</groupId>
<artifactId>com.minres.scviewer.parent</artifactId>
<version>2.12.1</version>
<version>2.15.1</version>
<relativePath>../..</relativePath>
</parent>
<packaging>eclipse-plugin</packaging>

View File

@ -14,12 +14,13 @@ import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.TreeMap;
import com.minres.scviewer.database.EventEntry;
import com.minres.scviewer.database.EventKind;
import com.minres.scviewer.database.HierNode;
import com.minres.scviewer.database.IEvent;
import com.minres.scviewer.database.IEventList;
import com.minres.scviewer.database.EventList;
import com.minres.scviewer.database.IWaveform;
import com.minres.scviewer.database.RelationType;
import com.minres.scviewer.database.RelationTypeFactory;
@ -35,7 +36,7 @@ abstract class AbstractTxStream extends HierNode implements IWaveform {
private Integer maxConcurrency;
private TreeMap<Long, IEvent[]> events;
private IEventList events;
private List<RelationType> usedRelationsList;
@ -71,9 +72,9 @@ abstract class AbstractTxStream extends HierNode implements IWaveform {
}
@Override
public NavigableMap<Long, IEvent[]> getEvents(){
public IEventList getEvents(){
if(events==null){
events=new TreeMap<>();
events=new EventList();
for(Entry<Integer, ITx> entry:getTransactions().entrySet()){
putEvent(new TxEvent(EventKind.BEGIN, entry.getValue()));
putEvent(new TxEvent(EventKind.END, entry.getValue()));
@ -83,22 +84,13 @@ abstract class AbstractTxStream extends HierNode implements IWaveform {
}
private void putEvent(TxEvent ev){
Long time = ev.getTime();
if(events.containsKey(time)) {
IEvent[] oldV = events.get(time);
IEvent[] newV = new IEvent[oldV.length+1];
System.arraycopy(oldV, 0, newV, 0, oldV.length);
newV[oldV.length]=ev;
events.put(time, newV);
} else {
events.put(time, new IEvent[] {ev});
}
events.put(ev.getTime(), ev);
}
protected abstract Map<Integer, ITx> getTransactions();
@Override
public IEvent[] getEventsAtTime(Long time) {
public IEvent[] getEventsAtTime(long time) {
return getEvents().get(time);
}
@ -113,12 +105,12 @@ abstract class AbstractTxStream extends HierNode implements IWaveform {
}
@Override
public IEvent[] getEventsBeforeTime(Long time) {
Entry<Long, IEvent[]> e = events.floorEntry(time);
public IEvent[] getEventsBeforeTime(long time) {
EventEntry e = events.floorEntry(time);
if(e==null)
return new IEvent[]{};
else
return events.floorEntry(time).getValue();
return events.floorEntry(time).events;
}
@Override

View File

@ -48,7 +48,7 @@ public class SQLiteDbLoader implements IWaveformDbLoader {
protected PropertyChangeSupport pcs = new PropertyChangeSupport(this);
@Override
public Long getMaxTime() {
public long getMaxTime() {
SQLiteDatabaseSelectHandler<ScvTxEvent> handler = new SQLiteDatabaseSelectHandler<>(ScvTxEvent.class,
database, "time = (SELECT MAX(time) FROM ScvTxEvent)");
try {

View File

@ -37,8 +37,8 @@ public class Tx implements ITx {
private TxGenerator trGenerator;
private ScvTx scvTx;
private List<ITxAttribute> attributes;
private Long begin;
private Long end;
private long begin=-1;
private long end=-1;
private List<ITxRelation> incoming;
private List<ITxRelation> outgoing;
@ -50,7 +50,7 @@ public class Tx implements ITx {
}
@Override
public Long getId() {
public long getId() {
return (long) scvTx.getId();
}
@ -69,8 +69,8 @@ public class Tx implements ITx {
}
@Override
public Long getBeginTime() {
if(begin==null){
public long getBeginTime() {
if(begin<0){
SQLiteDatabaseSelectHandler<ScvTxEvent> handler = new SQLiteDatabaseSelectHandler<>(ScvTxEvent.class,
database, "tx="+scvTx.getId()+" AND type="+ AssociationType.BEGIN.ordinal());
try {
@ -85,8 +85,8 @@ public class Tx implements ITx {
}
@Override
public Long getEndTime() {
if(end==null){
public long getEndTime() {
if(end<0){
SQLiteDatabaseSelectHandler<ScvTxEvent> handler = new SQLiteDatabaseSelectHandler<>(ScvTxEvent.class,
database, "tx="+scvTx.getId()+" AND type="+ AssociationType.END.ordinal());
try {
@ -178,11 +178,11 @@ public class Tx implements ITx {
@Override
public int compareTo(ITx o) {
int res = this.getBeginTime().compareTo(o.getBeginTime());
int res = Long.compare(this.getBeginTime(), o.getBeginTime());
if(res!=0)
return res;
else
return this.getId().compareTo(o.getId());
return Long.compare(this.getId(), o.getId());
}
@Override

View File

@ -28,7 +28,7 @@ public class TxEvent implements ITxEvent {
}
@Override
public Long getTime() {
public long getTime() {
return type==EventKind.BEGIN?tx.getBeginTime():tx.getEndTime();
}

View File

@ -39,7 +39,7 @@ public class TxGenerator extends AbstractTxStream {
}
@Override
public Long getId() {
public long getId() {
return (long) scvGenerator.getId();
}
@ -50,7 +50,7 @@ public class TxGenerator extends AbstractTxStream {
@Override
public boolean isSame(IWaveform other) {
return(other instanceof TxGenerator && this.getId().equals(other.getId()));
return(other instanceof TxGenerator && this.getId() == other.getId());
}
@Override

View File

@ -49,7 +49,7 @@ public class TxStream extends AbstractTxStream {
}
@Override
public Long getId() {
public long getId() {
return (long) scvStream.getId();
}
@ -89,13 +89,13 @@ public class TxStream extends AbstractTxStream {
}
@Override
public IEvent[] getEventsAtTime(Long time) {
public IEvent[] getEventsAtTime(long time) {
return getEvents().get(time);
}
@Override
public boolean isSame(IWaveform other) {
return(other instanceof TxStream && this.getId().equals(other.getId()));
return(other instanceof TxStream && this.getId() == other.getId());
}
@Override

View File

@ -2,12 +2,12 @@
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src/"/>
<classpathentry exported="true" kind="lib" path="lib/mapdb-3.0.7.jar" sourcepath="lib/mapdb-3.0.7-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/com.minres.scviewer.database.text/lib/mapdb-3.0.7-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="src"/>
<classpathentry exported="true" kind="lib" path="lib/kotlin-stdlib-1.2.42.jar"/>
<classpathentry exported="true" kind="lib" path="lib/lz4-1.3.0.jar"/>
<classpathentry exported="true" kind="lib" path="lib/elsa-3.0.0-M5.jar"/>

View File

@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Textual transaction database
Bundle-SymbolicName: com.minres.scviewer.database.text
Bundle-Version: 3.0.0.qualifier
Bundle-Version: 4.0.1.qualifier
Bundle-Vendor: MINRES Technologies GmbH
Bundle-RequiredExecutionEnvironment: JavaSE-11
Import-Package: org.osgi.framework;version="1.3.0"

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="TextDbLoader">
<implementation class="com.minres.scviewer.database.text.TextDbLoader"/>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="TextDbLoaderFactory">
<implementation class="com.minres.scviewer.database.text.TextDbLoaderFactory"/>
<service>
<provide interface="com.minres.scviewer.database.IWaveformDbLoader"/>
<provide interface="com.minres.scviewer.database.IWaveformDbLoaderFactory"/>
</service>
</scr:component>

View File

@ -2,11 +2,11 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>com.minres.scviewer.database.text</artifactId>
<version>3.0.0-SNAPSHOT</version>
<version>4.0.1-SNAPSHOT</version>
<parent>
<groupId>com.minres.scviewer</groupId>
<artifactId>com.minres.scviewer.parent</artifactId>
<version>2.12.1</version>
<version>2.15.1</version>
<relativePath>../..</relativePath>
</parent>
<packaging>eclipse-plugin</packaging>

View File

@ -12,14 +12,13 @@
package com.minres.scviewer.database.text;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.TreeMap;
import com.minres.scviewer.database.EventEntry;
import com.minres.scviewer.database.EventList;
import com.minres.scviewer.database.HierNode;
import com.minres.scviewer.database.IEvent;
import com.minres.scviewer.database.IEventList;
import com.minres.scviewer.database.IWaveform;
import com.minres.scviewer.database.WaveformType;
import com.minres.scviewer.database.tx.ITx;
@ -30,6 +29,8 @@ import com.minres.scviewer.database.tx.ITxEvent;
*/
abstract class AbstractTxStream extends HierNode implements IWaveform {
private final String fullName;
/** The id. */
private Long id;
@ -37,7 +38,7 @@ abstract class AbstractTxStream extends HierNode implements IWaveform {
protected TextDbLoader loader;
/** The events. */
TreeMap<Long, IEvent[]> events = new TreeMap<>();
IEventList events = new EventList();
/** The max concurrency. */
private int rowCount = -1;
@ -51,24 +52,27 @@ abstract class AbstractTxStream extends HierNode implements IWaveform {
*/
protected AbstractTxStream(TextDbLoader loader, Long id, String name) {
super(name);
fullName=name;
this.loader = loader;
this.id = id;
}
/**
* Gets the full hierarchical name.
*
* @return the full name
*/
@Override
public String getFullName() {
return fullName;
}
/**
* Adds the event.
*
* @param evt the evt
*/
public void addEvent(ITxEvent evt) {
if (!events.containsKey(evt.getTime()))
events.put(evt.getTime(), new IEvent[] { evt });
else {
IEvent[] evts = events.get(evt.getTime());
IEvent[] newEvts = Arrays.copyOf(evts, evts.length + 1);
newEvts[evts.length] = evt;
events.put(evt.getTime(), newEvts);
}
events.put(evt.getTime(), evt);
}
/**
@ -77,7 +81,7 @@ abstract class AbstractTxStream extends HierNode implements IWaveform {
* @return the events
*/
@Override
public NavigableMap<Long, IEvent[]> getEvents() {
public IEventList getEvents() {
return events;
}
@ -88,7 +92,7 @@ abstract class AbstractTxStream extends HierNode implements IWaveform {
* @return the events at time
*/
@Override
public IEvent[] getEventsAtTime(Long time) {
public IEvent[] getEventsAtTime(long time) {
return events.get(time);
}
@ -99,12 +103,12 @@ abstract class AbstractTxStream extends HierNode implements IWaveform {
* @return the events before time
*/
@Override
public IEvent[] getEventsBeforeTime(Long time) {
Entry<Long, IEvent[]> e = events.floorEntry(time);
public IEvent[] getEventsBeforeTime(long time) {
EventEntry e = events.floorEntry(time);
if (e == null)
return new IEvent[] {};
else
return events.floorEntry(time).getValue();
return events.floorEntry(time).events;
}
/**
@ -123,7 +127,7 @@ abstract class AbstractTxStream extends HierNode implements IWaveform {
* @return the id
*/
@Override
public Long getId() {
public long getId() {
return id;
}
@ -147,8 +151,8 @@ abstract class AbstractTxStream extends HierNode implements IWaveform {
return;
ArrayList<Long> rowEndTime = new ArrayList<>();
HashMap<Long, Integer> rowByTxId = new HashMap<>();
for(Entry<Long, IEvent[]> entry: events.entrySet()) {
for(IEvent evt:entry.getValue()) {
for(EventEntry entry: events) {
for(IEvent evt:entry.events) {
TxEvent txEvt = (TxEvent) evt;
ITx tx = txEvt.getTransaction();
int rowIdx = 0;

View File

@ -131,7 +131,7 @@ public class TextDbLoader implements IWaveformDbLoader {
* @return the max time
*/
@Override
public Long getMaxTime() {
public long getMaxTime() {
return maxTime;
}
@ -167,7 +167,9 @@ public class TextDbLoader implements IWaveformDbLoader {
*/
@Override
public Collection<IWaveform> getAllWaves() {
return new ArrayList<>(txStreams.values());
ArrayList<IWaveform> ret = new ArrayList<>(txStreams.values());
ret.addAll(txGenerators.values());
return ret;
}
/**
@ -343,10 +345,10 @@ public class TextDbLoader implements IWaveformDbLoader {
String curLine = reader.readLine();
String nextLine = null;
while ((nextLine = reader.readLine()) != null && curLine != null) {
curLine = parseLine(curLine, nextLine);
curLine = parseLine(curLine, nextLine, false);
}
if (curLine != null)
parseLine(curLine, nextLine);
parseLine(curLine, nextLine, true);
for(Entry<Long, ScvTx> e: transactionById.entrySet()) {
ScvTx scvTx = e.getValue();
scvTx.endTime=loader.maxTime;
@ -383,16 +385,16 @@ public class TextDbLoader implements IWaveformDbLoader {
* @throws IOException Signals that an I/O exception has occurred.
* @throws InputFormatException Signals that the input format is wrong
*/
private String parseLine(String curLine, String nextLine) throws IOException, InputFormatException {
private String parseLine(String curLine, String nextLine, boolean last) throws IOException, InputFormatException {
String[] tokens = curLine.split("\\s+");
if ("tx_record_attribute".equals(tokens[0])) {
if ("tx_record_attribute".equals(tokens[0]) && tokens.length>4) {
Long id = Long.parseLong(tokens[1]);
String name = tokens[2].substring(1, tokens[2].length()-1);
DataType type = DataType.valueOf(tokens[3]);
String remaining = tokens.length > 5 ? String.join(" ", Arrays.copyOfRange(tokens, 5, tokens.length)) : "";
TxAttributeType attrType = getAttrType(name, type, AssociationType.RECORD);
transactionById.get(id).attributes.add(new TxAttribute(attrType, getAttrString(attrType, remaining)));
} else if ("tx_begin".equals(tokens[0])) {
} else if ("tx_begin".equals(tokens[0]) && tokens.length>4) {
Long id = Long.parseLong(tokens[1]);
Long genId = Long.parseLong(tokens[2]);
TxGenerator gen = loader.txGenerators.get(genId);
@ -411,7 +413,7 @@ public class TextDbLoader implements IWaveformDbLoader {
}
}
transactionById.put(id, scvTx);
} else if ("tx_end".equals(tokens[0])) {
} else if ("tx_end".equals(tokens[0]) && tokens.length>4) {
Long id = Long.parseLong(tokens[1]);
ScvTx scvTx = transactionById.get(id);
assert Long.parseLong(tokens[2]) == scvTx.generatorId;
@ -441,7 +443,7 @@ public class TextDbLoader implements IWaveformDbLoader {
}
txSink.put(scvTx.getId(), scvTx);
transactionById.remove(id);
} else if ("tx_relation".equals(tokens[0])) {
} else if ("tx_relation".equals(tokens[0]) && tokens.length>3) {
Long tr2 = Long.parseLong(tokens[2]);
Long tr1 = Long.parseLong(tokens[3]);
String relType = tokens[1].substring(1, tokens[1].length() - 1);
@ -481,7 +483,7 @@ public class TextDbLoader implements IWaveformDbLoader {
}
} else if (")".equals(tokens[0])) {
generator = null;
} else
} else if(!last)
throw new InputFormatException("Don't know what to do with: '" + curLine + "'");
return nextLine;
}

View File

@ -0,0 +1,77 @@
/*******************************************************************************
* Copyright (c) 2012 IT Just working.
* Copyright (c) 2020 MINRES Technologies GmbH
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IT Just working - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.text;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;
import com.minres.scviewer.database.IWaveformDbLoader;
import com.minres.scviewer.database.IWaveformDbLoaderFactory;
/**
* The Class TextDbLoader.
*/
public class TextDbLoaderFactory implements IWaveformDbLoaderFactory {
/** The Constant x. */
static final byte[] x = "scv_tr_stream".getBytes();
/**
* Checks if f is gzipped.
*
* @param f the f
* @return true, if is gzipped
*/
private static boolean isGzipped(File f) {
try (InputStream is = new FileInputStream(f)) {
byte[] signature = new byte[2];
int nread = is.read(signature); // read the gzip signature
return nread == 2 && signature[0] == (byte) 0x1f && signature[1] == (byte) 0x8b;
} catch (IOException e) {
return false;
}
}
/**
* Can load.
*
* @param inputFile the input file
* @return true, if successful
*/
@Override
public boolean canLoad(File inputFile) {
if (!inputFile.isDirectory() && inputFile.exists()) {
boolean gzipped = isGzipped(inputFile);
try(InputStream stream = gzipped ? new GZIPInputStream(new FileInputStream(inputFile)) : new FileInputStream(inputFile)){
byte[] buffer = new byte[x.length];
int readCnt = stream.read(buffer, 0, x.length);
if (readCnt == x.length) {
for (int i = 0; i < x.length; i++)
if (buffer[i] != x[i])
return false;
}
return true;
} catch (Exception e) {
return false;
}
}
return false;
}
@Override
public IWaveformDbLoader getLoader() {
return new TextDbLoader();
}
}

View File

@ -102,11 +102,11 @@ class Tx implements ITx {
*/
@Override
public int compareTo(ITx o) {
int res = getBeginTime().compareTo(o.getBeginTime());
int res = Long.compare(getBeginTime(), o.getBeginTime());
if (res != 0)
return res;
else
return getId().compareTo(o.getId());
return Long.compare(getId(), o.getId());
}
/**
@ -150,7 +150,7 @@ class Tx implements ITx {
* @return the id
*/
@Override
public Long getId() {
public long getId() {
return getScvTx().id;
}
@ -180,7 +180,7 @@ class Tx implements ITx {
* @return the begin time
*/
@Override
public Long getBeginTime() {
public long getBeginTime() {
if (beginTime < 0) {
ScvTx tx = scvTx==null?loader.getScvTx(id):getScvTx();
beginTime = tx.beginTime;
@ -195,7 +195,7 @@ class Tx implements ITx {
* @return the end time
*/
@Override
public Long getEndTime() {
public long getEndTime() {
if (endTime < 0) {
ScvTx tx = scvTx==null?loader.getScvTx(id):getScvTx();
beginTime = tx.beginTime;

View File

@ -95,7 +95,7 @@ class TxEvent implements ITxEvent {
* @return the time
*/
@Override
public Long getTime() {
public long getTime() {
return time;
}

View File

@ -52,7 +52,7 @@ class TxGenerator extends AbstractTxStream {
*/
@Override
public boolean isSame(IWaveform other) {
return (other instanceof TxGenerator && this.getId().equals(other.getId()));
return (other instanceof TxGenerator && this.getId()==other.getId());
}
/**
@ -82,5 +82,15 @@ class TxGenerator extends AbstractTxStream {
public String getKind() {
return stream.getKind();
}
/**
* Gets the full hierarchical name.
*
* @return the full name
*/
@Override
public String getFullName() {
return ((AbstractTxStream)parent).getFullName()+"."+name;
}
}

View File

@ -42,7 +42,7 @@ class TxStream extends AbstractTxStream {
*/
@Override
public boolean isSame(IWaveform other) {
return (other instanceof TxStream && this.getId().equals(other.getId()));
return (other instanceof TxStream && this.getId() == other.getId());
}
/**

View File

@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: SWT database widget
Bundle-SymbolicName: com.minres.scviewer.database.ui.swt
Bundle-Version: 3.0.0.qualifier
Bundle-Version: 4.0.0.qualifier
Bundle-Vendor: MINRES Technologies GmbH
Bundle-RequiredExecutionEnvironment: JavaSE-11
Require-Bundle: org.eclipse.swt;bundle-version="3.103.1",

View File

@ -5,8 +5,8 @@
<parent>
<groupId>com.minres.scviewer</groupId>
<artifactId>com.minres.scviewer.parent</artifactId>
<version>2.12.1</version>
<version>2.15.1</version>
<relativePath>../..</relativePath>
</parent>
<version>3.0.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
</project>

View File

@ -27,10 +27,14 @@ import com.minres.scviewer.database.tx.ITx;
public interface IWaveformView extends PropertyChangeListener, ISelectionProvider{
String CURSOR_PROPERTY = "cursor_time";
static final String CURSOR_PROPERTY = "cursor_time";
String MARKER_PROPERTY = "marker_time";
static final String MARKER_PROPERTY = "marker_time";
static final int CURSOR_POS = 0;
static final int MARKER_POS = 1;
public static final RelationType NEXT_PREV_IN_STREAM = RelationTypeFactory.create("Prev/Next in stream");
public void addSelectionChangedListener(ISelectionChangedListener listener);
@ -75,23 +79,17 @@ public interface IWaveformView extends PropertyChangeListener, ISelectionProvide
public void setHighliteRelation(RelationType relationType);
public long getMaxTime();
public void setMaxTime(long maxTime);
public void setZoomLevel(int scale);
public int getZoomLevel();
public void setCursorTime(long time);
public void setMarkerTime(long time, int index);
public void setMarkerTime(int marker, long time);
public long getCursorTime();
public int getSelectedMarkerId();
public int getSelectedMarker();
public long getMarkerTime(int index);
public long getMarkerTime(int marker);
public void addPropertyChangeListener(PropertyChangeListener listener);
@ -101,21 +99,18 @@ public interface IWaveformView extends PropertyChangeListener, ISelectionProvide
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener);
public String getScaledTime(long time);
public String[] getZoomLevels();
public List<ICursor> getCursorList();
public long getBaselineTime();
public void setBaselineTime(Long scale);
public void scrollHorizontal(int percent);
public void scrollTo(int pos);
public void addDisposeListener( DisposeListener listener );
public void deleteSelectedTracks();
public TrackEntry addWaveform(IWaveform waveform, int pos);
public IWaveformZoom getWaveformZoom();
}

View File

@ -0,0 +1,24 @@
package com.minres.scviewer.database.ui;
public interface IWaveformZoom {
long getMaxVisibleTime();
long getMinVisibleTime();
void setMinVisibleTime(long scale);
long getMaxTime();
long getScale();
void setScale(long factor);
void setVisibleRange(long startTime, long endTime);
void centerAt(long time);
void zoom(ZoomKind kind);
String timeToString(long time);
}

View File

@ -0,0 +1,3 @@
package com.minres.scviewer.database.ui;
public enum ZoomKind {IN, OUT, FIT, FULL}

View File

@ -1,14 +1,49 @@
package com.minres.scviewer.database.ui.swt;
import java.text.DecimalFormat;
public class Constants {
public static final String[] UNIT_STRING={"fs", "ps", "ns", "us", "ms"};//, "s"};
public static final int[] UNIT_MULTIPLIER={1, 3, 10, 30, 100, 300};
public static final String[] UNIT_STRING={"fs", "ps", "ns", "us", "ms", "s"};
public static final long[] UNIT_MULTIPLIER={1l, 1000l, 1000l*1000, 1000l*1000*1000, 1000l*1000*1000*1000, 1000l*1000*1000*1000*1000 };
//public static final int[] UNIT_MULTIPLIER={1, 3, 10, 30, 100, 300};
public static final long[] SCALE_MULTIPLIER={1, 2, 5, 10, 20, 50, 100, 200, 500};
public static final String CONTENT_PROVIDER_TAG = "TOOLTIP_CONTENT_PROVIDER";
public static final String HELP_PROVIDER_TAG = "TOOLTIP_HELP_PROVIDER";
public static final DecimalFormat TIME_FORMAT_FS = new DecimalFormat("#");
public static final DecimalFormat TIME_FORMAT_PS = new DecimalFormat("#");
public static final DecimalFormat TIME_FORMAT_NS = new DecimalFormat("#.0##");
public static final DecimalFormat TIME_FORMAT_UMS = new DecimalFormat("#.0#####");
public static final long[] POWERS_OF_TEN = {
1L,
10L,
100L,
1_000L,
10_000L,
100_000L,
1_000_000L,
10_000_000L,
100_000_000L,
1_000_000_000L,
10_000_000_000L,
100_000_000_000L,
1_000_000_000_000L,
10_000_000_000_000L,
100_000_000_000_000L,
1_000_000_000_000_000L};
public static DecimalFormat getTimeFormatForLevel(int idx) {
switch(idx) {
case 0: return TIME_FORMAT_FS;
case 1: return TIME_FORMAT_PS;
case 2: return TIME_FORMAT_NS;
default:
return TIME_FORMAT_UMS;
}
}
private Constants() {}
}

View File

@ -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..."));
}
}

View File

@ -24,7 +24,6 @@ import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import com.minres.scviewer.database.IEvent;
import com.minres.scviewer.database.IHierNode;
import com.minres.scviewer.database.IWaveform;
import com.minres.scviewer.database.RelationType;
import com.minres.scviewer.database.tx.ITx;
@ -76,14 +75,14 @@ public class ArrowPainter implements IPainter {
this.tx = newTx;
iRect = new LinkedList<>();
oRect = new LinkedList<>();
scaleFactor = waveCanvas.getScaleFactor();
scaleFactor = waveCanvas.getScale();
if (tx != null) {
calculateGeometries();
}
}
private int getConcurrencyIndex(ITx tx) {
IEvent[] eventList = tx.getStream().getEvents().floorEntry(tx.getBeginTime()).getValue();
IEvent[] eventList = tx.getStream().getEventsBeforeTime(tx.getBeginTime());
Optional<Integer> res = Arrays.stream(eventList).map(e -> ((ITxEvent)e).getRowIndex()).findFirst();
return res.isPresent()? res.get():0;
}
@ -110,48 +109,23 @@ public class ArrowPainter implements IPainter {
protected void deriveGeom(Collection<ITxRelation> relations, List<LinkEntry> res, boolean useTarget) {
for (ITxRelation iTxRelation : relations) {
ITx otherTx = useTarget ? iTxRelation.getTarget() : iTxRelation.getSource();
Rectangle bb = createLinkEntry(otherTx, otherTx.getStream());
if(bb!=null){
res.add(new LinkEntry(bb, iTxRelation.getRelationType()));
return;
} else {
for(IHierNode gen:otherTx.getStream().getChildNodes()) {
if(gen instanceof IWaveform) {
bb = createLinkEntry(otherTx, (IWaveform) gen);
if(bb!=null){
res.add(new LinkEntry(bb, iTxRelation.getRelationType()));
return;
}
for(IWaveform iWaveform: new IWaveform[]{otherTx.getStream(), otherTx.getGenerator()}) {
if (waveCanvas.wave2painterMap.containsKey(iWaveform)) {
IWaveformPainter painter = waveCanvas.wave2painterMap.get(iWaveform);
if(painter!=null) {
int height = waveCanvas.styleProvider.getTrackHeight();
Rectangle bb = new Rectangle(
(int) (otherTx.getBeginTime() / scaleFactor),
waveCanvas.rulerHeight + painter.getVerticalOffset() + height * getConcurrencyIndex(otherTx),
(int) ((otherTx.getEndTime() - otherTx.getBeginTime()) / scaleFactor),
height);
res.add(new LinkEntry(bb, iTxRelation.getRelationType()));
}
}
}
}
}
private Rectangle createLinkEntry(ITx otherTx, IWaveform iWaveform) {
if (waveCanvas.wave2painterMap.containsKey(iWaveform)) {
IWaveformPainter painter = waveCanvas.wave2painterMap.get(otherTx.getStream());
if(painter==null) {
for(IHierNode gen:otherTx.getStream().getChildNodes()) {
if(gen instanceof IWaveform) {
painter = waveCanvas.wave2painterMap.get(gen);
if(painter!=null)
break;
}
}
}
if(painter!=null) {
int height = waveCanvas.styleProvider.getTrackHeight();
return new Rectangle(
(int) (otherTx.getBeginTime() / scaleFactor),
waveCanvas.rulerHeight + painter.getVerticalOffset() + height * getConcurrencyIndex(otherTx),
(int) ((otherTx.getEndTime() - otherTx.getBeginTime()) / scaleFactor),
height);
}
}
return null;
}
@Override
public void paintArea(Projection proj, Rectangle clientRect) {
yCtrlOffset = waveCanvas.styleProvider.getTrackHeight()/2;
@ -159,7 +133,7 @@ public class ArrowPainter implements IPainter {
Color highliteColor = waveCanvas.styleProvider.getColor(WaveformColors.REL_ARROW_HIGHLITE);
if(tx==null) return;
scaleFactor = waveCanvas.getScaleFactor();
scaleFactor = waveCanvas.getScale();
if(calculateGeometries())
return;
int correctionValue = (int)(selectionOffset);

View File

@ -65,7 +65,7 @@ public class CursorPainter implements IPainter, ICursor {
Rectangle area = proj.unProject(clientRect);
if(!waveCanvas.painterList.isEmpty()){
long scaleFactor=waveCanvas.getScaleFactor();
long scaleFactor=waveCanvas.getScale();
long beginPos = area.x;
maxPosX = area.x + area.width;
@ -83,8 +83,7 @@ public class CursorPainter implements IPainter, ICursor {
proj.drawLine(x, top, x, area.y+area.height);
proj.setBackground(drawColor);
proj.setForeground(textColor);
double dTime=time;
proj.drawText((dTime/waveCanvas.getScaleFactorPow10())+waveCanvas.getUnitStr(), x+1, top);
proj.drawText(waveCanvas.timeToString(time), x+1, top);
}
}
}

View File

@ -10,22 +10,21 @@
*******************************************************************************/
package com.minres.scviewer.database.ui.swt.internal;
import java.text.DecimalFormat;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.wb.swt.SWTResourceManager;
import com.minres.scviewer.database.ui.swt.Constants;
public class RulerPainter implements IPainter {
protected final WaveformCanvas waveCanvas;
static final int RULER_TICK_MINOR = 10;
static final int RULER_TICK_MAJOR = 100;
static final DecimalFormat df = new DecimalFormat("#.00####");
public RulerPainter(WaveformCanvas waveCanvas) {
this.waveCanvas=waveCanvas;
}
@ -39,26 +38,26 @@ public class RulerPainter implements IPainter {
Color headerBgColor = waveCanvas.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
if(headerBgColor.isDisposed())
headerBgColor=SWTResourceManager.getColor(255,255,255);
String unit=waveCanvas.getUnitStr();
int unitMultiplier=waveCanvas.getUnitMultiplier();
long scaleFactor=waveCanvas.getScaleFactor();
long startPos=area.x*scaleFactor;
long startVal=startPos - proj.getTranslation().x*scaleFactor;
long endPos=startPos+area.width*scaleFactor;
long rulerTickMinor = RULER_TICK_MINOR*scaleFactor;
long rulerTickMajor = RULER_TICK_MAJOR*scaleFactor;
long scaleFactor=waveCanvas.getScale();
long startTime=waveCanvas.getMinVisibleTime();
long endTime=waveCanvas.getMaxVisibleTime();
long multiplier = Constants.POWERS_OF_TEN[waveCanvas.getScaleMagnitude()];
long rulerTickMinor = RULER_TICK_MINOR*multiplier;
long rulerTickMajor = RULER_TICK_MAJOR*multiplier;
if((endTime-startTime)/rulerTickMinor>area.width/5) {
rulerTickMinor*=10;
rulerTickMajor*=10;
}
int minorTickY = waveCanvas.rulerHeight-5;
int majorTickY = waveCanvas.rulerHeight-15;
int textY=waveCanvas.rulerHeight-20;
int textY=waveCanvas.rulerHeight-30;
int baselineY=waveCanvas.rulerHeight - 1;
int bottom=waveCanvas.rulerHeight - 2;
long modulo = startVal % rulerTickMinor;
long startMinorIncrPos = startPos+rulerTickMinor-modulo;
long startMinorIncrVal = startVal+rulerTickMinor-modulo;
long startTickTime = startTime+rulerTickMinor-(startTime % rulerTickMinor);
long majorTickDist = rulerTickMajor/scaleFactor;
gc.setBackground(waveCanvas.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
gc.fillRectangle(new Rectangle(area.x, area.y, area.width, waveCanvas.rulerHeight));
@ -66,13 +65,22 @@ public class RulerPainter implements IPainter {
gc.fillRectangle(new Rectangle(area.x, area.y, area.width, baselineY));
gc.setForeground(headerFgColor);
gc.drawLine(area.x, area.y+bottom, area.x+area.width, area.y+bottom);
for (long pos = startMinorIncrPos, tick = startMinorIncrVal; pos < endPos; pos+= rulerTickMinor, tick += rulerTickMinor) {
int x0Pos = (int) (pos/scaleFactor);
long x0Val = tick/scaleFactor;
if ((tick % rulerTickMajor) == 0) {
gc.drawText(df.format(x0Val*unitMultiplier)+unit, x0Pos, area.y+textY);
int maxTextLength=0;
for (long tickTime = startTickTime; tickTime < endTime; tickTime+= rulerTickMinor) {
if ((tickTime % rulerTickMajor) == 0) {
Point textSize = gc.textExtent(waveCanvas.timeToString(tickTime));
maxTextLength=textSize.x>maxTextLength?textSize.x:maxTextLength;
}
}
boolean drawEvery = majorTickDist>maxTextLength;
boolean drawText=true;
for (long tickTime = startTickTime; tickTime < endTime; tickTime+= rulerTickMinor) {
int x0Pos = (int) (tickTime/scaleFactor) + proj.getTranslation().x;
if ((tickTime % rulerTickMajor) == 0) {
if(drawEvery || drawText)
gc.drawText(waveCanvas.timeToString(tickTime), x0Pos, area.y+textY);
gc.drawLine(x0Pos, area.y+majorTickY, x0Pos,area.y+ bottom);
drawText=!drawText;
} else {
gc.drawLine(x0Pos, area.y+minorTickY, x0Pos, area.y+bottom);
}

View File

@ -12,8 +12,6 @@ package com.minres.scviewer.database.ui.swt.internal;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map.Entry;
import java.util.NavigableMap;
import javax.swing.JPanel;
@ -26,7 +24,9 @@ import org.eclipse.swt.graphics.Rectangle;
import com.minres.scviewer.database.BitVector;
import com.minres.scviewer.database.DoubleVal;
import com.minres.scviewer.database.EventEntry;
import com.minres.scviewer.database.IEvent;
import com.minres.scviewer.database.IEventList;
import com.minres.scviewer.database.IWaveform;
import com.minres.scviewer.database.ui.TrackEntry;
import com.minres.scviewer.database.ui.WaveformColors;
@ -37,16 +37,16 @@ public class SignalPainter extends TrackPainter {
IEvent value;
boolean fromMap;
public SignalChange(Entry<Long, IEvent[]> entry) {
time = entry.getKey();
value = entry.getValue()[0];
public SignalChange(EventEntry entry) {
time = entry.timestamp;
value = entry.events[0];
fromMap = true;
}
public void set(Entry<Long, IEvent[]> entry, Long actTime) {
public void set(EventEntry entry, Long actTime) {
if (entry != null) {
time = entry.getKey();
value = entry.getValue()[0];
time = entry.timestamp;
value = entry.events[0];
fromMap = true;
} else {
time = actTime;
@ -82,7 +82,7 @@ public class SignalPainter extends TrackPainter {
}
private int getXPosEnd(long time) {
long ltmp = time / this.waveCanvas.getScaleFactor();
long ltmp = time / this.waveCanvas.getScale();
return ltmp > maxPosX ? maxPosX : (int) ltmp;
}
@ -95,31 +95,26 @@ public class SignalPainter extends TrackPainter {
proj.setFillRule(SWT.FILL_EVEN_ODD);
proj.fillRectangle(area);
long scaleFactor = this.waveCanvas.getScaleFactor();
long scaleFactor = this.waveCanvas.getScale();
long beginPos = area.x;
long beginTime = beginPos*scaleFactor;
long endTime = beginTime + area.width*scaleFactor;
Entry<Long, IEvent[]> first = signal.getEvents().floorEntry(beginTime);
Entry<Long, IEvent[]> last = signal.getEvents().floorEntry(endTime);
if (first == null) {
if (last == null)
return;
EventEntry first = signal.getEvents().floorEntry(beginTime);
if (first == null)
first = signal.getEvents().firstEntry();
} else if (last == null) {
last = signal.getEvents().lastEntry();
}
beginTime = first.timestamp;
proj.setForeground(this.waveCanvas.styleProvider.getColor(WaveformColors.LINE));
proj.setLineStyle(SWT.LINE_SOLID);
proj.setLineWidth(1);
NavigableMap<Long, IEvent[]> entries = signal.getEvents().subMap(first.getKey(), false, last.getKey(), true);
SignalChange left = new SignalChange(first);
SignalChange right = new SignalChange(entries.size() > 0 ? entries.firstEntry() : first);
IEventList entries = signal.getEvents().subMap(beginTime, true, endTime);
SignalChange left = new SignalChange(entries.firstEntry());
SignalChange right = new SignalChange(entries.size() > 1 ? entries.higherEntry(left.time) : entries.firstEntry());
maxPosX = area.x + area.width;
yOffsetT = this.waveCanvas.styleProvider.getTrackHeight() / 5 + area.y;
yOffsetM = this.waveCanvas.styleProvider.getTrackHeight() / 2 + area.y;
yOffsetB = 4 * this.waveCanvas.styleProvider.getTrackHeight() / 5 + area.y;
int xSigChangeBeginVal = Math.max(area.x, (int) (left.time / this.waveCanvas.getScaleFactor()));
int xSigChangeBeginVal = Math.max(area.x, (int) (left.time / this.waveCanvas.getScale()));
int xSigChangeBeginPos = area.x;
int xSigChangeEndPos = Math.max(area.x, getXPosEnd(right.time));
@ -132,7 +127,7 @@ public class SignalPainter extends TrackPainter {
right.time = endTime;
} else {
multiple = true;
long eTime = (xSigChangeBeginVal + 1) * this.waveCanvas.getScaleFactor();
long eTime = (xSigChangeBeginVal + 1) * this.waveCanvas.getScale();
right.set(entries.floorEntry(eTime), endTime);
right.time = eTime;
}
@ -152,16 +147,16 @@ public class SignalPainter extends TrackPainter {
multiple = false;
if (xSigChangeEndPos == xSigChangeBeginPos) {
multiple = true;
long eTime = (xSigChangeBeginPos + 1) * this.waveCanvas.getScaleFactor();
Entry<Long, IEvent[]> entry = entries.floorEntry(eTime);
if(entry!=null && entry.getKey()> right.time)
long eTime = (xSigChangeBeginPos + 1) * this.waveCanvas.getScale();
EventEntry entry = entries.floorEntry(eTime);
if(entry!=null && entry.timestamp> right.time)
right.set(entry, endTime);
xSigChangeEndPos = getXPosEnd(eTime);
}
} while (left.time < endTime);
}
private SignalStencil getStencil(GC gc, SignalChange left, NavigableMap<Long, IEvent[]> entries) {
private SignalStencil getStencil(GC gc, SignalChange left, IEventList entries) {
IEvent val = left.value;
if(val instanceof BitVector) {
BitVector bv = (BitVector) val;
@ -253,15 +248,15 @@ public class SignalPainter extends TrackPainter {
private long maxVal;
private long minVal;
double yRange = (yOffsetB-yOffsetT);
public MultiBitStencilAnalog(NavigableMap<Long, IEvent[]> entries, Object left, boolean continous, boolean signed) {
public MultiBitStencilAnalog(IEventList entries, Object left, boolean continous, boolean signed) {
this.continous=continous;
this.signed=signed;
Collection<IEvent[]> values = entries.values();
Collection<EventEntry> ievents = entries.entrySet();
minVal=signed?((BitVector)left).toSignedValue():((BitVector)left).toUnsignedValue();
if(!values.isEmpty()) {
if(!ievents.isEmpty()) {
maxVal=minVal;
for (IEvent[] tp : entries.values())
for(IEvent e: tp) {
for (EventEntry tp : ievents)
for(IEvent e: tp.events) {
long v = signed?((BitVector)e).toSignedValue():((BitVector)e).toUnsignedValue();
maxVal=Math.max(maxVal, v);
minVal=Math.min(minVal, v);
@ -358,15 +353,15 @@ public class SignalPainter extends TrackPainter {
boolean continous=true;
public RealStencil(NavigableMap<Long, IEvent[]> entries, Object left, boolean continous) {
public RealStencil(IEventList entries, Object left, boolean continous) {
this.continous=continous;
Collection<IEvent[]> values = entries.values();
Collection<EventEntry> values = entries.entrySet();
minVal=(Double) left;
range=2.0;
if(!values.isEmpty()) {
double maxVal=minVal;
for (IEvent[] val : entries.values())
for(IEvent e:val) {
for (EventEntry val : values)
for(IEvent e:val.events) {
double v = ((DoubleVal)e).value;
if(Double.isNaN(maxVal))
maxVal=v;

View File

@ -10,8 +10,6 @@
*******************************************************************************/
package com.minres.scviewer.database.ui.swt.internal;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.TreeMap;
import org.eclipse.swt.SWT;
@ -19,8 +17,10 @@ import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import com.minres.scviewer.database.EventEntry;
import com.minres.scviewer.database.EventKind;
import com.minres.scviewer.database.IEvent;
import com.minres.scviewer.database.IEventList;
import com.minres.scviewer.database.IWaveform;
import com.minres.scviewer.database.tx.ITx;
import com.minres.scviewer.database.tx.ITxEvent;
@ -36,6 +36,7 @@ public class StreamPainter extends TrackPainter{
private IWaveform stream;
private int txBase;
private int txHeight;
// TODO: remove TreeMap usage
private TreeMap<ITx, ITxEvent> seenTx;
public StreamPainter(WaveformCanvas waveCanvas, boolean even, TrackEntry trackEntry) {
@ -66,15 +67,16 @@ public class StreamPainter extends TrackPainter{
proj.setFillRule(SWT.FILL_EVEN_ODD);
proj.fillRectangle(area);
long scaleFactor = this.waveCanvas.getScaleFactor();
long scaleFactor = this.waveCanvas.getScale();
long beginPos = area.x;
long beginTime = beginPos*scaleFactor;
long endTime = beginTime + area.width*scaleFactor;
Entry<Long, IEvent[]> firstTx=stream.getEvents().floorEntry(beginTime);
Entry<Long, IEvent[]> lastTx=stream.getEvents().ceilingEntry(endTime);
if(firstTx==null) firstTx = stream.getEvents().firstEntry();
if(lastTx==null) lastTx=stream.getEvents().lastEntry();
IEventList events = stream.getEvents();
EventEntry firstTx = events.floorEntry(beginTime);
EventEntry lastTx = events.ceilingEntry(endTime);
if(firstTx==null) firstTx = events.firstEntry();
if(lastTx==null) lastTx = events.lastEntry();
proj.setFillRule(SWT.FILL_EVEN_ODD);
proj.setLineStyle(SWT.LINE_SOLID);
proj.setLineWidth(1);
@ -83,16 +85,15 @@ public class StreamPainter extends TrackPainter{
for( int y1=area.y+trackHeight/2; y1<area.y+trackEntry.height; y1+=trackHeight)
proj.drawLine(area.x, y1, area.x+area.width, y1);
if(firstTx==lastTx) {
for(IEvent txEvent: firstTx.getValue())
for(IEvent txEvent: firstTx.events)
drawTx(proj, area, ((ITxEvent)txEvent).getTransaction(), ((ITxEvent)txEvent).getRowIndex(), false);
}else{
seenTx.clear();
NavigableMap<Long, IEvent[]> entries = stream.getEvents().subMap(firstTx.getKey(), true, lastTx.getKey(), true);
ITxEvent highlighed=null;
proj.setForeground(this.waveCanvas.styleProvider.getColor(WaveformColors.LINE));
long selectedId=waveCanvas.currentSelection!=null? waveCanvas.currentSelection.getId():-1;
for(Entry<Long, IEvent[]> entry: entries.entrySet())
for(IEvent e:entry.getValue()){
for(EventEntry entry: events.subMap(firstTx.timestamp, true, lastTx.timestamp))
for(IEvent e:entry.events){
ITxEvent evt = (ITxEvent) e;
ITx tx = evt.getTransaction();
if(selectedId==tx.getId())
@ -129,8 +130,8 @@ public class StreamPainter extends TrackPainter{
int offset = concurrencyIndex*this.waveCanvas.styleProvider.getTrackHeight();
Rectangle bb = new Rectangle(
(int)(tx.getBeginTime()/this.waveCanvas.getScaleFactor()), area.y+offset+txBase,
(int)((tx.getEndTime()-tx.getBeginTime())/this.waveCanvas.getScaleFactor()), txHeight);
(int)(tx.getBeginTime()/this.waveCanvas.getScale()), area.y+offset+txBase,
(int)((tx.getEndTime()-tx.getBeginTime())/this.waveCanvas.getScale()), txHeight);
if(bb.x+bb.width<area.x || bb.x>area.x+area.width) return;
if(bb.width==0){
@ -154,12 +155,12 @@ public class StreamPainter extends TrackPainter{
public ITx getClicked(Point point) {
int lane=point.y/waveCanvas.styleProvider.getTrackHeight();
Entry<Long, IEvent[]> firstTx=stream.getEvents().floorEntry(point.x*waveCanvas.getScaleFactor());
EventEntry firstTx=stream.getEvents().floorEntry(point.x*waveCanvas.getScale());
if(firstTx!=null){
do {
ITx tx = getTxFromEntry(lane, point.x, firstTx);
ITx tx = getTxFromEntry(lane, point.x, firstTx.events);
if(tx!=null) return tx;
firstTx=stream.getEvents().lowerEntry(firstTx.getKey());
firstTx=stream.getEvents().lowerEntry(firstTx.timestamp);
}while(firstTx!=null);
}
return null;
@ -173,11 +174,11 @@ public class StreamPainter extends TrackPainter{
this.stream = stream;
}
protected ITx getTxFromEntry(int lane, int offset, Entry<Long, IEvent[]> firstTx) {
long timePoint=offset*waveCanvas.getScaleFactor();
long timePointLow=(offset-5)*waveCanvas.getScaleFactor();
long timePointHigh=(offset+5)*waveCanvas.getScaleFactor();
for(IEvent e:firstTx.getValue()){
protected ITx getTxFromEntry(int lane, int offset, IEvent[] firstTx) {
long timePoint=offset*waveCanvas.getScale();
long timePointLow=(offset-5)*waveCanvas.getScale();
long timePointHigh=(offset+5)*waveCanvas.getScale();
for(IEvent e:firstTx){
if(e instanceof ITxEvent) {
ITxEvent evt = (ITxEvent) e;
ITx tx=evt.getTransaction();

View File

@ -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();

View File

@ -34,6 +34,7 @@ import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.ScrollBar;
import com.google.common.collect.Lists;
import com.minres.scviewer.database.EventEntry;
import com.minres.scviewer.database.IEvent;
import com.minres.scviewer.database.IWaveform;
import com.minres.scviewer.database.RelationType;
@ -41,32 +42,39 @@ import com.minres.scviewer.database.tx.ITx;
import com.minres.scviewer.database.tx.ITxEvent;
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.ZoomKind;
import com.minres.scviewer.database.ui.swt.Constants;
import com.minres.scviewer.database.ui.swt.internal.slider.ZoomBar;
public class WaveformCanvas extends Canvas {
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 long scaleFactor = 1000000L; // 1ns
String unit="ns";
private int level = 12;
private long maxTime;
protected Point origin; /* original size */
protected int rulerHeight=40;
protected List<IPainter> painterList;
ITx currentSelection;
private List<SelectionAdapter> selectionListeners;
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 */
protected int rulerHeight=40;
protected List<IPainter> painterList;
ITx currentSelection;
private List<SelectionAdapter> selectionListeners;
private RulerPainter rulerPainter;
@ -76,446 +84,525 @@ public class WaveformCanvas extends Canvas {
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);
this.styleProvider=styleProvider;
addControlListener(new ControlAdapter() { /* resize listener. */
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();
}
});
addPaintListener((final PaintEvent event) -> paint(event.gc));
painterList = new LinkedList<>();
origin = new Point(0, 0);
selectionListeners = new LinkedList<>();
cursorPainters= new ArrayList<>();
wave2painterMap=new HashMap<>();
initScrollBars();
syncSb();
}
});
addPaintListener((final PaintEvent event) -> paint(event.gc));
painterList = new LinkedList<>();
origin = new Point(0, 0);
selectionListeners = new LinkedList<>();
cursorPainters= new ArrayList<>();
wave2painterMap=new HashMap<>();
horizontal = scrollbarProvider.getScrollBar();
vertical = getVerticalBar();
initScrollBars();
// order is important: it is bottom to top
trackAreaPainter=new TrackAreaPainter(this);
painterList.add(trackAreaPainter);
arrowPainter=new ArrowPainter(this, IWaveformView.NEXT_PREV_IN_STREAM);
painterList.add(arrowPainter);
rulerPainter=new RulerPainter(this);
painterList.add(rulerPainter);
CursorPainter cp = new CursorPainter(this, scaleFactor * 10, cursorPainters.size()-1);
trackAreaPainter=new TrackAreaPainter(this);
painterList.add(trackAreaPainter);
arrowPainter=new ArrowPainter(this, IWaveformView.NEXT_PREV_IN_STREAM);
painterList.add(arrowPainter);
rulerPainter=new RulerPainter(this);
painterList.add(rulerPainter);
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<>();
}
}
public void addCursoPainter(CursorPainter cursorPainter){
painterList.add(cursorPainter);
cursorPainters.add(cursorPainter);
}
public void setHighliteRelation(RelationType relationType){
if(arrowPainter!=null){
boolean redraw = arrowPainter.getHighlightType()!=relationType;
arrowPainter.setHighlightType(relationType);
if(redraw) redraw();
}
}
public Point getOrigin() {
return origin;
}
public int getWidth() {
return getClientArea().width;
}
public void setOrigin(Point origin) {
setOrigin(origin.x, origin.y);
}
public void setHighliteRelation(RelationType relationType){
if(arrowPainter!=null){
boolean redraw = arrowPainter.getHighlightType()!=relationType;
arrowPainter.setHighlightType(relationType);
if(redraw) redraw();
}
}
public void setOrigin(int x, int y) {
checkWidget();
ScrollBar hBar = getHorizontalBar();
hBar.setSelection(-x);
x = -hBar.getSelection();
ScrollBar vBar = getVerticalBar();
vBar.setSelection(-y);
y = -vBar.getSelection();
origin.x = x;
origin.y = y;
syncScrollBars();
}
public Point getOrigin() {
return origin;
}
public long getMaxTime() {
return maxTime;
}
public int getWidth() {
return getClientArea().width;
}
public void setOrigin(Point origin) {
setOrigin(origin.x, origin.y);
}
public void setMaxTime(long maxTime) {
this.maxTime = maxTime;
syncScrollBars();
}
public void setOrigin(int x, int y) {
checkWidget();
origin.x = x;
origin.y = y;
syncSb();
}
public int getZoomLevel() {
return level;
}
public int getMaxZoomLevel(){
return Constants.UNIT_MULTIPLIER.length*Constants.UNIT_STRING.length-1;
}
@Override
public long getMaxTime() {
return maxTime;
}
public void setZoomLevel(int level) {
long tc=cursorPainters.get(0).getTime(); // cursor time
setZoomLevel(level, tc);
}
public void setMaxTime(long maxTime) {
this.maxTime = maxTime;
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();
}
public void setZoomLevel(int level, long centerTime) {
//FIXME: keep center if zoom-out and cursor is not in view
if(level<0) level = 0;
long xc=centerTime/this.scaleFactor; // cursor total x-offset
if(level<Constants.UNIT_MULTIPLIER.length*Constants.UNIT_STRING.length){
this.scaleFactor = (long) Math.pow(10, level>>1);
if(level%2==1) this.scaleFactor*=3;
ITx tx = arrowPainter.getTx();
arrowPainter.setTx(null);
/*
* xc = tc/oldScaleFactor
* xoffs = xc+origin.x
* xcn = tc/newScaleFactor
* t0n = (xcn-xoffs)*scaleFactor
*/
long xoffs=xc+origin.x; // cursor offset relative to left border
long xcn=centerTime/scaleFactor; // new total x-offset
@Override
public long getScale() {
return scaleFactor;
}
@Override
public void setScale(long factor) {
setScalingFactor(factor, (getMaxVisibleTime()+getMinVisibleTime())/2);
}
@Override
public void setVisibleRange(long startTime, long endTime) {
assert(startTime<endTime);
long time_diff = endTime-startTime;
long factor = (time_diff*11/10)/getClientArea().width;
setScalingFactor(factor, startTime+time_diff/2);
}
//@Override
public void setScalingFactor(long factor, long centerTime) {
Rectangle clientArea = getClientArea();
long clientAreaWidth = clientArea.width;
long maxFactor=maxTime/clientAreaWidth;
if(factor<0) {
if(factor== ZOOM_FIT) {
long cTime = getCursorPainters().get(0).getTime();
long time_diff = centerTime>cTime?centerTime-cTime:cTime-centerTime;
centerTime = (centerTime>cTime?cTime:centerTime)+time_diff/2;
factor = (time_diff*11/10)/clientAreaWidth;
} else if(factor== ZOOM_FULL)
factor = maxFactor;
}
if(factor==0)
factor=1;
else if(factor>maxFactor)
factor=maxFactor;
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)*getScale()
*/
long xoffs = clientAreaWidth/2;
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();
arrowPainter.setTx(tx);
redraw();
this.level = level;
}
}
syncSb();
arrowPainter.setTx(tx);
redraw();
}
}
public long getScaleFactor() {
return scaleFactor;
}
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;
}
}
}
public long getScaleFactorPow10() {
int scale = level/Constants.UNIT_MULTIPLIER.length;
double res = Math.pow(1000, scale);
return (long) res;
}
@Override
public String timeToString(long time) {
int idx = scaleMagnitude/3;
double fTime = (double)time/Constants.UNIT_MULTIPLIER[idx];
return Constants.getTimeFormatForLevel(idx).format(fTime)+Constants.UNIT_STRING[idx];
}
public String getUnitStr(){
return Constants.UNIT_STRING[level/Constants.UNIT_MULTIPLIER.length];
}
public int getUnitMultiplier(){
return Constants.UNIT_MULTIPLIER[level%Constants.UNIT_MULTIPLIER.length];
}
public long getTimeForOffset(int xOffset){
return (xOffset-origin.x) * scaleFactor;
}
public void addPainter(IPainter painter) {
painterList.add(painter);
redraw();
}
public long getTimeForOffset(int xOffset){
return (xOffset-origin.x) * getScale();
}
public void removePainter(IPainter painter) {
painterList.remove(painter);
redraw();
}
public void addPainter(IPainter painter) {
painterList.add(painter);
redraw();
}
public void clearAllWaveformPainter() {
clearAllWaveformPainter(true);
}
void clearAllWaveformPainter(boolean update) {
trackAreaPainter.trackVerticalOffset.clear();
wave2painterMap.clear();
if(update) syncScrollBars();
}
public void removePainter(IPainter painter) {
painterList.remove(painter);
redraw();
}
public void addWaveformPainter(IWaveformPainter painter) {
addWaveformPainter(painter, true);
}
void addWaveformPainter(IWaveformPainter painter, boolean update) {
trackAreaPainter.addTrackPainter(painter);
wave2painterMap.put(painter.getTrackEntry().waveform, painter);
if(update) syncScrollBars();
}
public void clearAllWaveformPainter() {
clearAllWaveformPainter(true);
}
public List<CursorPainter> getCursorPainters() {
void clearAllWaveformPainter(boolean update) {
trackAreaPainter.trackVerticalOffset.clear();
wave2painterMap.clear();
if(update) syncSb();
}
public void addWaveformPainter(IWaveformPainter painter) {
addWaveformPainter(painter, true);
}
void addWaveformPainter(IWaveformPainter painter, boolean update) {
trackAreaPainter.addTrackPainter(painter);
wave2painterMap.put(painter.getTrackEntry().waveform, painter);
if(update) syncSb();
}
public List<CursorPainter> getCursorPainters() {
return cursorPainters;
}
/* Initialize the scrollbar and register listeners. */
private void initScrollBars() {
ScrollBar horizontal = getHorizontalBar();
horizontal.setEnabled(false);
horizontal.setVisible(true);
horizontal.addSelectionListener(new SelectionAdapter() {
/* Initialize the scrollbar and register listeners. */
private void initScrollBars() {
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);
}
});
ScrollBar vertical = getVerticalBar();
vertical.setEnabled(false);
vertical.setVisible(true);
vertical.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
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;
}
}
});
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());
}
});
}
/**
* Synchronize the scrollbar with the image. If the transform is out of
* 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;
}
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);
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 */
vertical.setMinimum(0);
vertical.setMaximum(height);
vertical.setEnabled(true);
if ( -origin.y > vertical.getMaximum() - clientHeighth) {
origin.y = -vertical.getMaximum() + clientHeighth;
}
} else { /* image is less higher than client area */
vertical.setMaximum(clientHeighth);
vertical.setEnabled(false);
}
vertical.setThumb(clientHeighth);
vertical.setSelection(-origin.y);
redraw();
fireSelectionEvent();
}
/* Paint function */
private void paint(GC gc) {
Point pt = getSize();
if(pt.x==0 || pt.y==0) return;
Rectangle clientRect = getClientArea(); /* Canvas' painting area */
GC thisGc = gc;
Image dBackingImg = null;
if(doubleBuffering) {
dBackingImg = new Image(getDisplay(), pt.x, pt.y);
thisGc = new GC(dBackingImg);
thisGc.setBackground(gc.getBackground());
thisGc.setForeground(gc.getForeground());
thisGc.setFont(gc.getFont());
}
Projection p = new Projection(thisGc);
p.setTranslation(origin);
if (!painterList.isEmpty() ) {
for (IPainter painter : painterList)
painter.paintArea(p, clientRect);
} else {
gc.fillRectangle(clientRect);
initScrollBars();
}
if(doubleBuffering) {
gc.drawImage(dBackingImg, 0, 0);
if(dBackingImg!=null) dBackingImg.dispose();
thisGc.dispose();
}
}
public List<Object> getElementsAt(Point point) {
LinkedList<Object> result=new LinkedList<>();
for (IPainter p : Lists.reverse(painterList)) {
if (p instanceof TrackAreaPainter) {
int y = point.y - origin.y;
int x = point.x - origin.x;
Entry<Integer, IWaveformPainter> entry = trackAreaPainter.trackVerticalOffset.floorEntry(y);
if (entry != null) {
if (entry.getValue() instanceof StreamPainter) {
ITx tx = ((StreamPainter) entry.getValue()).getClicked(new Point(x, y - entry.getKey()));
if(tx!=null)
result.add(tx);
}
result.add(entry.getValue().getTrackEntry());
}
} else if (p instanceof CursorPainter) {
if (Math.abs(point.x - origin.x - ((CursorPainter) p).getTime()/scaleFactor) < 2) {
result.add(p);
}
}
}
return result;
}
public List<Object> getEntriesAtPosition(IWaveform iWaveform, int i) {
LinkedList<Object> result=new LinkedList<>();
int x = i - origin.x;
for(IWaveformPainter p: wave2painterMap.values()){
if (p instanceof StreamPainter && ((StreamPainter)p).getStream()==iWaveform) {
result.add(((StreamPainter) p).getClicked(new Point(x, styleProvider.getTrackHeight()/2)));
}
}
return result;
}
public void setSelected(ITx currentSelection) {
this.currentSelection = currentSelection;
if (currentSelection != null)
reveal(currentSelection);
arrowPainter.setTx(currentSelection);
redraw();
}
public void reveal(ITx tx) {
int lower = (int) (tx.getBeginTime() / scaleFactor);
int higher = (int) (tx.getEndTime() / scaleFactor);
Point size = getSize();
size.x -= getVerticalBar().getSize().x + 2;
size.y -= getHorizontalBar().getSize().y;
if (lower < -origin.x) {
setOrigin(-lower, origin.y);
} else if (higher > (size.x - origin.x)) {
setOrigin(size.x - higher, origin.y);
}
for (IWaveformPainter painter : wave2painterMap.values()) {
if (painter instanceof StreamPainter && ((StreamPainter) painter).getStream() == tx.getStream()) {
Entry<Long, IEvent[]> entry = tx.getStream().getEvents().floorEntry(tx.getBeginTime());
Optional<IEvent> res = Arrays.stream(entry.getValue()).filter(e -> ((ITxEvent)e).getTransaction().equals(tx)).findFirst();
if(res.isPresent()) {
int top = painter.getVerticalOffset() + styleProvider.getTrackHeight() * ((ITxEvent)res.get()).getRowIndex();
int bottom = top + styleProvider.getTrackHeight();
if (top < -origin.y) {
setOrigin(origin.x, -(top-styleProvider.getTrackHeight()));
} else if (bottom > (size.y - origin.y)) {
setOrigin(origin.x, size.y - bottom);
}
}
}
}
}
public void reveal(IWaveform waveform) {
for (IWaveformPainter painter : wave2painterMap.values()) {
TrackEntry te = painter.getTrackEntry();
if(te.waveform == waveform) {
Point size = getSize();
size.y -=+rulerHeight;
ScrollBar sb = getHorizontalBar();
if((sb.getStyle()&SWT.SCROLLBAR_OVERLAY)!=0 && sb.isVisible())
size.y-= getHorizontalBar().getSize().y;
int top = te.vOffset;
int bottom = top + styleProvider.getTrackHeight();
if (top < -origin.y) {
setOrigin(origin.x, -(top-styleProvider.getTrackHeight()));
} else if (bottom > (size.y - origin.y)) {
setOrigin(origin.x, size.y - bottom);
}
}
}
public void widgetSelected(SelectionEvent event) {
if (!painterList.isEmpty()) {
origin.y=-vertical.getSelection();
fireSelectionEvent();
WaveformCanvas.this.getDisplay().asyncExec(() -> {redraw();});
}
}
});
}
public void reveal(long time) {
int scaledTime = (int) (time / scaleFactor);
Point size = getSize();
size.x -= getVerticalBar().getSize().x + 2;
size.y -= getHorizontalBar().getSize().y;
if (scaledTime < -origin.x) {
setOrigin(-scaledTime+10, origin.y);
} else if (scaledTime > (size.x - origin.x)) {
setOrigin(size.x - scaledTime-30, origin.y);
}
}
/**
* Synchronize the scrollbar with the image. If the transform is out of
* range, it will correct it. This function considers only following factors
* :<b> transform, image size, client area</b>.
*/
public void syncSb() {
if (!painterList.isEmpty()) {
syncHSb();
syncVSb();
fireSelectionEvent();
}
redraw();
}
public int getRulerHeight() {
return rulerHeight;
}
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() - clientHeight) {
origin.y = -vertical.getMaximum() + clientHeight;
}
} else { /* image is less higher than client area */
vertical.setMaximum(clientHeight);
vertical.setEnabled(false);
}
vertical.setThumb(clientHeight);
vertical.setSelection(-origin.y);
}
public void setRulerHeight(int rulerHeight) {
this.rulerHeight = rulerHeight;
}
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);
}
public void addSelectionListener(SelectionAdapter selectionAdapter) {
selectionListeners.add(selectionAdapter);
}
/* Paint function */
private void paint(GC gc) {
Point pt = getSize();
if(pt.x==0 || pt.y==0) return;
Rectangle clientRect = getClientArea(); /* Canvas' painting area */
GC thisGc = gc;
Image dBackingImg = null;
if(doubleBuffering) {
dBackingImg = new Image(getDisplay(), pt.x, pt.y);
thisGc = new GC(dBackingImg);
thisGc.setBackground(gc.getBackground());
thisGc.setForeground(gc.getForeground());
thisGc.setFont(gc.getFont());
public void removeSelectionListener(SelectionAdapter selectionAdapter) {
selectionListeners.remove(selectionAdapter);
}
}
Projection p = new Projection(thisGc);
p.setTranslation(origin);
if (!painterList.isEmpty() ) {
for (IPainter painter : painterList)
painter.paintArea(p, clientRect);
} else {
gc.fillRectangle(clientRect);
initScrollBars();
}
if(doubleBuffering) {
gc.drawImage(dBackingImg, 0, 0);
if(dBackingImg!=null) dBackingImg.dispose();
thisGc.dispose();
}
}
/**
*
*/
protected void fireSelectionEvent() {
Event e = new Event();
e.widget = this;
e.detail=SWT.SELECTED;
e.type=SWT.Selection;
SelectionEvent ev = new SelectionEvent(e);
ev.x = origin.x;
ev.y = origin.y;
for (SelectionAdapter a : selectionListeners) {
a.widgetSelected(ev);
}
}
public List<Object> getElementsAt(Point point) {
LinkedList<Object> result=new LinkedList<>();
for (IPainter p : Lists.reverse(painterList)) {
if (p instanceof TrackAreaPainter) {
int y = point.y - origin.y;
int x = point.x - origin.x;
Entry<Integer, IWaveformPainter> entry = trackAreaPainter.trackVerticalOffset.floorEntry(y);
if (entry != null) {
if (entry.getValue() instanceof StreamPainter) {
ITx tx = ((StreamPainter) entry.getValue()).getClicked(new Point(x, y - entry.getKey()));
if(tx!=null)
result.add(tx);
}
result.add(entry.getValue().getTrackEntry());
}
} else if (p instanceof CursorPainter) {
if (Math.abs(point.x - origin.x - ((CursorPainter) p).getTime()/getScale()) < 2) {
result.add(p);
}
}
}
return result;
}
long getMaxVisibleTime() {
return (getClientArea().width+origin.x)*scaleFactor;
}
public List<Object> getEntriesAtPosition(IWaveform iWaveform, int i) {
LinkedList<Object> result=new LinkedList<>();
int x = i - origin.x;
for(IWaveformPainter p: wave2painterMap.values()){
if (p instanceof StreamPainter && ((StreamPainter)p).getStream()==iWaveform) {
result.add(((StreamPainter) p).getClicked(new Point(x, styleProvider.getTrackHeight()/2)));
}
}
return result;
}
long getMinVisibleTime() {
return origin.x * scaleFactor;
}
public void setSelected(ITx currentSelection) {
this.currentSelection = currentSelection;
if (currentSelection != null)
reveal(currentSelection);
arrowPainter.setTx(currentSelection);
redraw();
}
public void reveal(ITx tx) {
int lower = (int) (tx.getBeginTime() / getScale());
int higher = (int) (tx.getEndTime() / getScale());
Point size = getSize();
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)) {
setOrigin(size.x - higher, origin.y);
}
for (IWaveformPainter painter : wave2painterMap.values()) {
if (painter instanceof StreamPainter && ((StreamPainter) painter).getStream() == tx.getStream()) {
EventEntry entry = tx.getStream().getEvents().floorEntry(tx.getBeginTime());
Optional<IEvent> res = Arrays.stream(entry.events).filter(e -> ((ITxEvent)e).getTransaction().equals(tx)).findFirst();
if(res.isPresent()) {
int top = painter.getVerticalOffset() + styleProvider.getTrackHeight() * ((ITxEvent)res.get()).getRowIndex();
int bottom = top + styleProvider.getTrackHeight();
if (top < -origin.y) {
setOrigin(origin.x, -(top-styleProvider.getTrackHeight()));
} else if (bottom > (size.y - origin.y)) {
setOrigin(origin.x, size.y - bottom);
}
}
}
}
}
public void reveal(IWaveform waveform) {
for (IWaveformPainter painter : wave2painterMap.values()) {
TrackEntry te = painter.getTrackEntry();
if(te.waveform == waveform) {
Point size = getSize();
size.y -=+rulerHeight;
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) {
setOrigin(origin.x, -(top-styleProvider.getTrackHeight()));
} else if (bottom > (size.y - origin.y)) {
setOrigin(origin.x, size.y - bottom);
}
}
}
}
public void reveal(long time) {
int scaledTime = (int) (time / getScale());
Point size = getSize();
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)) {
setOrigin(size.x - scaledTime-30, origin.y);
}
}
@Override
public void centerAt(long time) {
int scaledTime = (int) (time / getScale());
int newX = -scaledTime+getWidth()/2;
setOrigin(newX>0?0:newX, origin.y);
}
public int getRulerHeight() {
return rulerHeight;
}
public void setRulerHeight(int rulerHeight) {
this.rulerHeight = rulerHeight;
}
public void addSelectionListener(SelectionAdapter selectionAdapter) {
selectionListeners.add(selectionAdapter);
}
public void removeSelectionListener(SelectionAdapter selectionAdapter) {
selectionListeners.remove(selectionAdapter);
}
/**
*
*/
protected void fireSelectionEvent() {
Event e = new Event();
e.widget = this;
e.detail=SWT.SELECTED;
e.type=SWT.Selection;
SelectionEvent ev = new SelectionEvent(e);
ev.x = origin.x;
ev.y = origin.y;
for (SelectionAdapter a : selectionListeners) {
a.widgetSelected(ev);
}
}
@Override
public long getMaxVisibleTime() {
return (getClientArea().width-origin.x)*getScale();
}
@Override
public long getMinVisibleTime() {
return -origin.x * getScale();
}
@Override
public void setMinVisibleTime(long time) {
long duration = getMaxVisibleTime()-getMinVisibleTime();
if(time>0) {
if((time+duration)<getMaxTime()) {
int scaledTime = (int) (time / getScale());
setOrigin(-scaledTime, origin.y);
}
} else {
setOrigin(0, origin.y);
}
}
@Override
public void zoom(ZoomKind kind) {
switch(kind) {
case IN:
setScale(getScale()/2);
break;
case OUT:
setScale(getScale()*2);
break;
case FIT:
setScalingFactor(WaveformCanvas.ZOOM_FIT, cursorPainters.get(1).getTime());
break;
case FULL:
setScale(WaveformCanvas.ZOOM_FULL);
break;
}
}
public void setStyleProvider(IWaveformStyleProvider styleProvider) {
this.styleProvider=styleProvider;
redraw();
}
public int getScaleMagnitude() {
return scaleMagnitude;
}
}

View File

@ -13,7 +13,6 @@ package com.minres.scviewer.database.ui.swt.internal;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -21,7 +20,6 @@ import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.TreeMap;
@ -77,8 +75,10 @@ import org.eclipse.wb.swt.SWTResourceManager;
import com.google.common.collect.Lists;
import com.minres.scviewer.database.BitVector;
import com.minres.scviewer.database.DoubleVal;
import com.minres.scviewer.database.EventEntry;
import com.minres.scviewer.database.EventKind;
import com.minres.scviewer.database.IEvent;
import com.minres.scviewer.database.IEventList;
import com.minres.scviewer.database.IWaveform;
import com.minres.scviewer.database.RelationType;
import com.minres.scviewer.database.WaveformType;
@ -89,8 +89,9 @@ import com.minres.scviewer.database.ui.GotoDirection;
import com.minres.scviewer.database.ui.ICursor;
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.Constants;
import com.minres.scviewer.database.ui.swt.internal.slider.ZoomBar;
public class WaveformView implements IWaveformView {
@ -98,8 +99,6 @@ public class WaveformView implements IWaveformView {
private PropertyChangeSupport pcs;
static final DecimalFormat df = new DecimalFormat("#0.00####");
private ITx currentTxSelection;
private ArrayList<TrackEntry> currentWaveformSelection = new ArrayList<>();
@ -114,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;
@ -162,8 +165,7 @@ public class WaveformView implements IWaveformView {
: streams.subList(firstIdx, lastIdx + 1);
setSelection(new StructuredSelection(res), (e.stateMask & SWT.CTRL) != 0, false);
} else
setSelection(new StructuredSelection(entry.getValue()), (e.stateMask & SWT.CTRL) != 0,
false);
setSelection(new StructuredSelection(entry.getValue()), (e.stateMask & SWT.CTRL) != 0, false);
} else {
setSelection(new StructuredSelection(entry.getValue()), (e.stateMask & SWT.CTRL) != 0, false);
}
@ -196,88 +198,69 @@ public class WaveformView implements IWaveformView {
down = false;
if (start == null)
return;
if ((e.stateMask & SWT.MODIFIER_MASK & ~SWT.SHIFT) != 0)
return; // don't react on modifier except shift
if (e.button == 1 && Math.abs(e.x - start.x) > 3) {
asyncUpdate(e.widget);
long startTime = waveformCanvas.getTimeForOffset(start.x);
long endTime = waveformCanvas.getTimeForOffset(end.x);
long targetTimeRange = endTime - startTime;
long currentTimeRange = waveformCanvas.getMaxVisibleTime() - waveformCanvas.getMinVisibleTime();
if (targetTimeRange == 0)
return;
long relation = currentTimeRange / targetTimeRange;
long i = 1;
int level = 0;
do {
if (relation < 0) {
if (-relation < i) {
break;
}
level--;
if (-relation < i * 3) {
break;
}
level--;
} else {
if (relation < i) {
break;
}
level++;
if (relation < i * 3) {
break;
}
level++;
}
i = i * 10;
} while (i < 10000);
if (i < 10000) {
int curLevel = waveformCanvas.getZoomLevel();
waveformCanvas.setZoomLevel(curLevel - level, (startTime + endTime) / 2);
}
} else if (e.button == 1 && ((e.stateMask & SWT.SHIFT) == 0)) {
// set cursor (button 1 and no shift)
if (Math.abs(e.x - start.x) < 3 && Math.abs(e.y - start.y) < 3) {
// first set cursor time
setCursorTime(snapOffsetToEvent(start));
// then set selection and reveal
setSelection(new StructuredSelection(initialSelected));
if ((e.stateMask & SWT.MODIFIER_MASK & ~(SWT.SHIFT | SWT.CTRL)) != 0)
return; // don't react on modifier except shift and control
boolean isCtrl = (e.stateMask & SWT.CTRL)!=0;
boolean isShift = (e.stateMask & SWT.SHIFT)!=0;
if (e.button == 1) {
if (Math.abs(e.x - start.x) > 3) { // was drag event
asyncUpdate(e.widget);
long startTime = waveformCanvas.getTimeForOffset(start.x);
long endTime = waveformCanvas.getTimeForOffset(end.x);
if(startTime<endTime) {
waveformCanvas.setVisibleRange(startTime, endTime);
} else if(start.x!=end.x){
long targetTimeRange = startTime-endTime;
long currentTimeRange = waveformCanvas.getMaxVisibleTime() - waveformCanvas.getMinVisibleTime();
long factor = currentTimeRange/targetTimeRange *waveformCanvas.getScale();
waveformCanvas.setScalingFactor(factor, (startTime+endTime)/2);
}
} else if( isShift) { // set marker (button 1 and shift)
setMarkerTime(selectedMarker, snapOffsetToEvent(start));
} else if(isCtrl) { // set cursor (button 1 and ctrl)
setCursorTime(snapOffsetToEvent(start));
} else { // set cursor (button 1 only)
if (Math.abs(e.y - start.y) < 3) {
// first set cursor time
setCursorTime(snapOffsetToEvent(start));
// then set selection and reveal
setSelection(new StructuredSelection(initialSelected));
}
}
} else if (e.button == 2 || (e.button == 1 && (e.stateMask & SWT.SHIFT) != 0)) {
// set marker (button 1 and shift)
setMarkerTime(snapOffsetToEvent(start), selectedMarker);
asyncUpdate(e.widget);
} else if (e.button == 2) { // set marker (button 2)
setMarkerTime(selectedMarker, snapOffsetToEvent(start));
}
asyncUpdate(e.widget);
}
protected long snapOffsetToEvent(Point p) {
long time = waveformCanvas.getTimeForOffset(p.x);
long scaling = 5 * waveformCanvas.getScaleFactor();
long scaling = 5 * waveformCanvas.getScale();
for (Object o : waveformCanvas.getElementsAt(p)) {
Entry<Long, IEvent[]> floorEntry = null;
Entry<Long, IEvent[]> ceilEntry = null;
EventEntry floorEntry = null;
EventEntry ceilEntry = null;
if (o instanceof TrackEntry) {
TrackEntry entry = (TrackEntry) o;
NavigableMap<Long, IEvent[]> map = entry.waveform.getEvents();
IEventList map = entry.waveform.getEvents();
floorEntry = map.floorEntry(time);
ceilEntry = map.ceilingEntry(time);
} else if (o instanceof ITx) {
NavigableMap<Long, IEvent[]> map = ((ITx) o).getStream().getEvents();
IEventList map = ((ITx) o).getStream().getEvents();
floorEntry = map.floorEntry(time);
ceilEntry = map.ceilingEntry(time);
}
if (floorEntry != null && time - floorEntry.getKey() > scaling)
if (floorEntry != null && time - floorEntry.timestamp > scaling)
floorEntry = null;
if (ceilEntry != null && ceilEntry.getKey() - time > scaling)
if (ceilEntry != null && ceilEntry.timestamp - time > scaling)
ceilEntry = null;
if (ceilEntry == null && floorEntry != null) {
time = floorEntry.getKey();
time = floorEntry.timestamp;
} else if (ceilEntry != null && floorEntry == null) {
time = ceilEntry.getKey();
time = ceilEntry.timestamp;
} else if (ceilEntry != null && floorEntry != null) {
time = time - floorEntry.getKey() < ceilEntry.getKey() - time ? floorEntry.getKey()
: ceilEntry.getKey();
time = time - floorEntry.timestamp < ceilEntry.timestamp - time ? floorEntry.timestamp
: ceilEntry.timestamp;
}
}
return time;
@ -287,6 +270,25 @@ public class WaveformView implements IWaveformView {
public void handleEvent(Event e) {
switch (e.type) {
case SWT.MouseWheel:
if((e.stateMask & SWT.CTRL) != 0) {
if(e.count<0) // up scroll
waveformCanvas.setScale(waveformCanvas.getScale()*11/10);
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);
@ -346,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");
@ -363,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) {
@ -382,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));
@ -395,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) {
@ -414,6 +441,7 @@ public class WaveformView implements IWaveformView {
});
valueList.addMouseListener(nameValueMouseListener);
valueListScrolled.setContent(valueList);
valueFill = createFill(valuePane);
waveformCanvas.setMaxTime(1);
waveformCanvas.addPaintListener(waveformMouseListener);
@ -460,6 +488,26 @@ public class WaveformView implements IWaveformView {
toolTipHandler = new ToolTipHandler(parent.getShell());
toolTipHandler.activateHoverHelp(waveformCanvas);
// This is the filter that prevents the default handling of mouse wheel in waveformCanvas
getControl().getDisplay().addFilter(SWT.MouseWheel, new Listener() {
@Override
public void handleEvent(Event e) {
// Check if it's the correct widget
if(e.widget.equals(waveformCanvas) && (e.stateMask & SWT.CTRL) != 0) {
waveformMouseListener.handleEvent(e);
e.doit = false;
}
}
});
}
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) {
@ -527,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();
@ -580,10 +628,10 @@ public class WaveformView implements IWaveformView {
}
} else if (entry.waveform.getType() == WaveformType.TRANSACTION) {
ITx[] resultsList = new ITx[entry.waveform.getRowCount()];
Entry<Long, IEvent[]> firstTx = entry.waveform.getEvents().floorEntry(time);
EventEntry firstTx = entry.waveform.getEvents().floorEntry(time);
if (firstTx != null) {
do {
for (IEvent e : firstTx.getValue()) {
for (IEvent e : firstTx.events) {
if (e instanceof ITxEvent) {
ITxEvent evt = ((ITxEvent) e);
ITx tx = evt.getTransaction();
@ -593,7 +641,7 @@ public class WaveformView implements IWaveformView {
resultsList[evt.getRowIndex()] = evt.getTransaction();
}
}
firstTx = entry.waveform.getEvents().lowerEntry(firstTx.getKey());
firstTx = entry.waveform.getEvents().lowerEntry(firstTx.timestamp);
} while (firstTx != null && !isArrayFull(resultsList));
boolean separator = false;
StringBuilder sb = new StringBuilder();
@ -751,27 +799,30 @@ public class WaveformView implements IWaveformView {
if (!add)
currentWaveformSelection.clear();
List<?> selList = sel.toList();
if (selList.get(0) instanceof ITx) {
ITx txSel = (ITx) selList.get(0);
TrackEntry trackEntry = selList.size() == 2 && selList.get(1) instanceof TrackEntry
? (TrackEntry) selList.get(1)
: null;
if (trackEntry == null) {
trackEntry = getEntryFor(txSel);
if (trackEntry == null && addIfNeeded) {
trackEntry = new TrackEntry(txSel.getStream(), styleProvider);
streams.add(trackEntry);
for(Object o: selList) {
if (o instanceof ITx) {
ITx txSel = (ITx) o;
TrackEntry trackEntry = selList.size() == 2 && selList.get(1) instanceof TrackEntry
? (TrackEntry) selList.get(1)
: null;
if (trackEntry == null) {
trackEntry = getEntryFor(txSel);
if (trackEntry == null && addIfNeeded) {
trackEntry = new TrackEntry(txSel.getStream(), styleProvider);
streams.add(trackEntry);
}
}
currentTxSelection = txSel;
currentWaveformSelection.clear();
currentWaveformSelection.add(trackEntry);
selectionChanged = true;
} else if (o instanceof TrackEntry) {
TrackEntry e = (TrackEntry)o;
if(!currentWaveformSelection.contains(e))
currentWaveformSelection.add(e);
selectionChanged = true;
}
currentTxSelection = txSel;
currentWaveformSelection.clear();
currentWaveformSelection.add(trackEntry);
selectionChanged = true;
} else if (selList.size() == 1 && selList.get(0) instanceof TrackEntry) {
currentWaveformSelection.add((TrackEntry) selList.get(0));
if (currentTxSelection != null)
currentTxSelection = null;
selectionChanged = true;
}
}
} else {
@ -854,11 +905,11 @@ public class WaveformView implements IWaveformView {
}
}
if (transaction == null) {
Entry<Long, IEvent[]> entry = selectedWaveform.waveform.getEvents()
EventEntry entry = selectedWaveform.waveform.getEvents()
.higherEntry(currentTxSelection.getBeginTime());
if (entry != null)
do {
for (IEvent evt : entry.getValue()) {
for (IEvent evt : entry.events) {
if (evt instanceof ITxEvent && (evt.getKind() == EventKind.BEGIN
|| evt.getKind() == EventKind.SINGLE)) {
transaction = ((ITxEvent) evt).getTransaction();
@ -866,7 +917,7 @@ public class WaveformView implements IWaveformView {
}
}
if (transaction == null)
entry = selectedWaveform.waveform.getEvents().higherEntry(entry.getKey());
entry = selectedWaveform.waveform.getEvents().higherEntry(entry.timestamp);
} while (entry != null && transaction == null);
}
} else if (direction == GotoDirection.PREV) {
@ -883,11 +934,11 @@ public class WaveformView implements IWaveformView {
}
}
if (transaction == null) {
Entry<Long, IEvent[]> entry = selectedWaveform.waveform.getEvents()
EventEntry entry = selectedWaveform.waveform.getEvents()
.lowerEntry(currentTxSelection.getBeginTime());
if (entry != null)
do {
for (IEvent evt : Lists.reverse(Arrays.asList(entry.getValue()))) {
for (IEvent evt : Lists.reverse(Arrays.asList(entry.events))) {
if (evt instanceof ITxEvent && (evt.getKind() == EventKind.BEGIN
|| evt.getKind() == EventKind.SINGLE)) {
transaction = ((ITxEvent) evt).getTransaction();
@ -895,7 +946,7 @@ public class WaveformView implements IWaveformView {
}
}
if (transaction == null)
entry = selectedWaveform.waveform.getEvents().lowerEntry(entry.getKey());
entry = selectedWaveform.waveform.getEvents().lowerEntry(entry.timestamp);
} while (entry != null && transaction == null);
}
}
@ -929,7 +980,7 @@ public class WaveformView implements IWaveformView {
return candidates.get(0);
default:
ArrayList<ITxRelation> visibleCandidates = candidates.stream().filter(this::streamsVisible)
.collect(Collectors.toCollection(ArrayList::new));
.collect(Collectors.toCollection(ArrayList::new));
if (visibleCandidates.isEmpty()) {
return new RelSelectionDialog(waveformCanvas.getShell(), candidates, target).open();
} else if (visibleCandidates.size() == 1) {
@ -958,14 +1009,14 @@ public class WaveformView implements IWaveformView {
return;
TrackEntry sel = currentWaveformSelection.get(0);
long time = getCursorTime();
NavigableMap<Long, ?> map = null;
IEventList map = null;
if (sel.waveform.getType() == WaveformType.TRANSACTION || sel.waveform.getType() == WaveformType.SIGNAL) {
map = sel.waveform.getEvents();
}
if (map != null) {
Entry<Long, ?> entry = direction == GotoDirection.PREV ? map.lowerEntry(time) : map.higherEntry(time);
EventEntry entry = direction == GotoDirection.PREV ? map.lowerEntry(time) : map.higherEntry(time);
if (entry != null) {
time = entry.getKey();
time = entry.timestamp;
setCursorTime(time);
waveformCanvas.reveal(time);
waveformCanvas.redraw();
@ -1114,16 +1165,6 @@ public class WaveformView implements IWaveformView {
this.waveformCanvas.setHighliteRelation(relationType);
}
/*
* (non-Javadoc)
*
* @see com.minres.scviewer.database.swt.IWaveformPanel#getMaxTime()
*/
@Override
public long getMaxTime() {
return waveformCanvas.getMaxTime();
}
/*
* (non-Javadoc)
*
@ -1134,27 +1175,6 @@ public class WaveformView implements IWaveformView {
this.waveformCanvas.setMaxTime(maxTime);
}
/*
* (non-Javadoc)
*
* @see com.minres.scviewer.database.swt.IWaveformPanel#setZoomLevel(int)
*/
@Override
public void setZoomLevel(int scale) {
waveformCanvas.setZoomLevel(scale);
waveformCanvas.reveal(getCursorTime());
}
/*
* (non-Javadoc)
*
* @see com.minres.scviewer.database.swt.IWaveformPanel#getZoomLevel()
*/
@Override
public int getZoomLevel() {
return waveformCanvas.getZoomLevel();
}
/*
* (non-Javadoc)
*
@ -1173,10 +1193,10 @@ public class WaveformView implements IWaveformView {
* @see com.minres.scviewer.database.swt.IWaveformPanel#setMarkerTime(long, int)
*/
@Override
public void setMarkerTime(long time, int index) {
if (waveformCanvas.getCursorPainters().size() > index + 1) {
final Long oldVal = waveformCanvas.getCursorPainters().get(1 + index).getTime();
waveformCanvas.getCursorPainters().get(1 + index).setTime(time);
public void setMarkerTime(int marker, long time) {
if (waveformCanvas.getCursorPainters().size() > marker + 1) {
final Long oldVal = waveformCanvas.getCursorPainters().get(1 + marker).getTime();
waveformCanvas.getCursorPainters().get(1 + marker).setTime(time);
pcs.firePropertyChange(MARKER_PROPERTY, oldVal, time);
}
}
@ -1197,7 +1217,7 @@ public class WaveformView implements IWaveformView {
* @see com.minres.scviewer.database.swt.IWaveformPanel#getActMarkerTime()
*/
@Override
public int getSelectedMarkerId() {
public int getSelectedMarker() {
return selectedMarker;
}
@ -1229,7 +1249,7 @@ public class WaveformView implements IWaveformView {
if (event.y < tracksVerticalHeight) {
event.doit = true;
LocalSelectionTransfer.getTransfer()
.setSelection(new StructuredSelection(currentWaveformSelection));
.setSelection(new StructuredSelection(currentWaveformSelection));
}
}
@ -1449,58 +1469,32 @@ public class WaveformView implements IWaveformView {
return this.pcs.hasListeners(propertyName);
}
/*
* (non-Javadoc)
*
* @see com.minres.scviewer.database.swt.IWaveformPanel#getScaledTime(long)
*/
@Override
public String getScaledTime(long time) {
StringBuilder sb = new StringBuilder();
double dTime = time;
double scaledTime = dTime / waveformCanvas.getScaleFactorPow10();
return sb.append(df.format(scaledTime)).append(waveformCanvas.getUnitStr()).toString();
}
/*
* (non-Javadoc)
*
* @see com.minres.scviewer.database.swt.IWaveformPanel#getZoomLevels()
*/
@Override
public String[] getZoomLevels() {
String[] res = new String[Constants.UNIT_MULTIPLIER.length * Constants.UNIT_STRING.length];
int index = 0;
for (String unit : Constants.UNIT_STRING) {
for (int factor : Constants.UNIT_MULTIPLIER) {
res[index++] = Integer.toString(factor) + unit;
}
}
return res;
}
@Override
public long getBaselineTime() {
return -waveformCanvas.getScaleFactorPow10() * waveformCanvas.getOrigin().x;
}
@Override
public void setBaselineTime(Long time) {
Point origin = waveformCanvas.getOrigin();
origin.x = (int) (-time / waveformCanvas.getScaleFactorPow10());
waveformCanvas.setOrigin(origin);
}
@Override
public void scrollHorizontal(int percent) {
if (percent < -100)
percent = -100;
if (percent > 100)
percent = 100;
int diff = (waveformCanvas.getWidth() * percent) / 100;
Point o = waveformCanvas.getOrigin();
waveformCanvas.setOrigin(o.x - diff, o.y);
waveformCanvas.redraw();
long minTime = waveformCanvas.getMinVisibleTime();
long duration = waveformCanvas.getMaxVisibleTime()-minTime;
long diff = (duration * percent) / 100;
waveformCanvas.setMinVisibleTime(minTime+diff);
}
@Override
public void scrollTo(int pos) {
long time = 0;
switch(pos) {
case IWaveformView.CURSOR_POS:
time = getCursorTime();
break;
case IWaveformView.MARKER_POS:
time = getMarkerTime(selectedMarker);
break;
default:
break;
}
waveformCanvas.centerAt(time);
}
public void asyncUpdate(Widget widget) {
@ -1532,5 +1526,13 @@ public class WaveformView implements IWaveformView {
getStreamList().add(idx, e);
return e;
}
public boolean waveformsContainsTx() {
return streams.stream().filter(e -> e.waveform.getType() == WaveformType.TRANSACTION).findFirst().isPresent();
}
@Override
public IWaveformZoom getWaveformZoom() {
return waveformCanvas;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -29,17 +29,14 @@ import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
/**
* 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();

View File

@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: VCD signal database
Bundle-SymbolicName: com.minres.scviewer.database.vcd
Bundle-Version: 2.2.0.qualifier
Bundle-Version: 4.0.0.qualifier
Bundle-Vendor: MINRES Technologies GmbH
Bundle-RequiredExecutionEnvironment: JavaSE-11
Require-Bundle: com.minres.scviewer.database;bundle-version="1.0.0",

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="VCDDbLoader">
<implementation class="com.minres.scviewer.database.vcd.VCDDbLoader"/>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="VCDDbLoaderFactory">
<implementation class="com.minres.scviewer.database.vcd.VCDDbLoaderFactory"/>
<service>
<provide interface="com.minres.scviewer.database.IWaveformDbLoader"/>
<provide interface="com.minres.scviewer.database.IWaveformDbLoaderFactory"/>
</service>
</scr:component>

View File

@ -1,11 +1,11 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>com.minres.scviewer.database.vcd</artifactId>
<version>2.2.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<parent>
<groupId>com.minres.scviewer</groupId>
<artifactId>com.minres.scviewer.parent</artifactId>
<version>2.12.1</version>
<version>2.15.1</version>
<relativePath>../..</relativePath>
</parent>
<packaging>eclipse-plugin</packaging>

View File

@ -20,15 +20,13 @@ import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.Vector;
import java.util.zip.GZIPInputStream;
import com.google.common.collect.Iterables;
import com.minres.scviewer.database.BitVector;
import com.minres.scviewer.database.DoubleVal;
import com.minres.scviewer.database.IEvent;
import com.minres.scviewer.database.IEventList;
import com.minres.scviewer.database.IWaveform;
import com.minres.scviewer.database.IWaveformDb;
import com.minres.scviewer.database.IWaveformDbLoader;
@ -115,19 +113,20 @@ public class VCDDbLoader implements IWaveformDbLoader, IVCDDatabaseBuilder {
moduleStack=null;
throw new InputFormatException(e.toString());
}
if(!res) throw new InputFormatException("Could not parse VCD file");
if(!res)
throw new InputFormatException("Could not parse VCD file");
// calculate max time of this database
for(IWaveform waveform:signals) {
NavigableMap<Long, IEvent[]> events =waveform.getEvents();
IEventList events =waveform.getEvents();
if(!events.isEmpty())
maxTime= Math.max(maxTime, events.lastKey());
}
// extend signals to have a last value set at max time
for(IWaveform s:signals){
if(s instanceof VCDSignal<?>) {
TreeMap<Long,?> events = (TreeMap<Long, ?>) ((VCDSignal<?>)s).getEvents();
IEventList events = ((VCDSignal<?>)s).getEvents();
if(events.size()>0 && events.lastKey()<maxTime){
Object val = events.lastEntry().getValue();
Object val = events.lastEntry().events[0];
if(val instanceof BitVector) {
((VCDSignal<BitVector>)s).addSignalChange(maxTime, (BitVector) val);
} else if(val instanceof DoubleVal)
@ -147,7 +146,7 @@ public class VCDDbLoader implements IWaveformDbLoader, IVCDDatabaseBuilder {
* @see com.minres.scviewer.database.ITrDb#getMaxTime()
*/
@Override
public Long getMaxTime() {
public long getMaxTime() {
return maxTime;
}
@ -165,7 +164,8 @@ public class VCDDbLoader implements IWaveformDbLoader, IVCDDatabaseBuilder {
@Override
public void enterModule(String tokenString) {
if(moduleStack.isEmpty()) {
if("SystemC".compareTo(tokenString)!=0) moduleStack.push(tokenString);
if("SystemC".compareTo(tokenString)!=0)
moduleStack.push(tokenString);
} else
moduleStack.push(moduleStack.peek()+"."+tokenString);

View File

@ -0,0 +1,77 @@
/*******************************************************************************
* Copyright (c) 2015-2021 MINRES Technologies GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* MINRES Technologies GmbH - initial API and implementation
*******************************************************************************/
package com.minres.scviewer.database.vcd;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;
import com.minres.scviewer.database.IWaveformDbLoader;
import com.minres.scviewer.database.IWaveformDbLoaderFactory;
/**
* The Class VCDDb.
*/
public class VCDDbLoaderFactory implements IWaveformDbLoaderFactory {
/**
* Checks if is gzipped.
*
* @param f the f
* @return true, if is gzipped
*/
private static boolean isGzipped(File f) {
try (InputStream is = new FileInputStream(f)) {
byte [] signature = new byte[2];
int nread = is.read( signature ); //read the gzip signature
return nread == 2 && signature[ 0 ] == (byte) 0x1f && signature[ 1 ] == (byte) 0x8b;
}
catch (IOException e) {
return false;
}
}
/**
* Can load.
*
* @param inputFile the input file
* @return true, if successful
*/
@Override
public boolean canLoad(File inputFile) {
if(!inputFile.isDirectory() || inputFile.exists()) {
String name = inputFile.getName();
if(!(name.endsWith(".vcd") ||
name.endsWith(".vcdz") ||
name.endsWith(".vcdgz") ||
name.endsWith(".vcd.gz")) )
return false;
boolean gzipped = isGzipped(inputFile);
try(InputStream stream = gzipped ? new GZIPInputStream(new FileInputStream(inputFile)) : new FileInputStream(inputFile)){
byte[] buffer = new byte[8];
if (stream.read(buffer, 0, buffer.length) == buffer.length) {
return buffer[0]=='$';
}
} catch (Exception e) {
return false;
}
}
return false;
}
@Override
public IWaveformDbLoader getLoader() {
return new VCDDbLoader();
}
}

View File

@ -10,12 +10,11 @@
*******************************************************************************/
package com.minres.scviewer.database.vcd;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.TreeMap;
import com.minres.scviewer.database.EventEntry;
import com.minres.scviewer.database.EventList;
import com.minres.scviewer.database.HierNode;
import com.minres.scviewer.database.IEvent;
import com.minres.scviewer.database.IEventList;
import com.minres.scviewer.database.IWaveform;
import com.minres.scviewer.database.WaveformType;
@ -27,7 +26,7 @@ public class VCDSignal<T extends IEvent> extends HierNode implements IWaveform {
private final int width;
private NavigableMap<Long, IEvent[]> values;
private IEventList values;
public VCDSignal(String name) {
this(0, name, 1);
@ -42,7 +41,7 @@ public class VCDSignal<T extends IEvent> extends HierNode implements IWaveform {
fullName=name;
this.id=id;
this.width=width;
this.values=new TreeMap<>();
this.values=new EventList();
}
public VCDSignal(VCDSignal<T> o, int id, String name) {
@ -63,44 +62,36 @@ public class VCDSignal<T extends IEvent> extends HierNode implements IWaveform {
}
@Override
public Long getId() {
public long getId() {
return id;
}
public void addSignalChange(Long time, T value){
if(values.containsKey(time)) {
IEvent[] oldV = values.get(time);
IEvent[] newV = new IEvent[oldV.length+1];
System.arraycopy(oldV, 0, newV, 0, oldV.length);
newV[oldV.length]=value;
values.put(time, newV);
} else {
values.put(time, new IEvent[] {value});
}
values.put(time, value);
}
@Override
public NavigableMap<Long, IEvent[]> getEvents() {
public IEventList getEvents() {
return values;
}
@Override
public IEvent[] getEventsAtTime(Long time) {
public IEvent[] getEventsAtTime(long time) {
return values.get(time);
}
@Override
public IEvent[] getEventsBeforeTime(Long time) {
Entry<Long, IEvent[]> e = values.floorEntry(time);
public IEvent[] getEventsBeforeTime(long time) {
EventEntry e = values.floorEntry(time);
if(e==null)
return new IEvent[] {};
else
return values.floorEntry(time).getValue();
return values.floorEntry(time).events;
}
@Override
public boolean isSame(IWaveform other) {
return( other instanceof VCDSignal<?> && this.getId().equals(other.getId()));
return( other instanceof VCDSignal<?> && this.getId() == other.getId());
}
@Override

View File

@ -12,5 +12,5 @@ org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=11

View File

@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Waveform database
Bundle-SymbolicName: com.minres.scviewer.database
Bundle-Version: 3.0.0.qualifier
Bundle-Version: 4.0.0.qualifier
Bundle-Vendor: MINRES Technologies GmbH
Bundle-RequiredExecutionEnvironment: JavaSE-11
Export-Package: com.minres.scviewer.database,

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="com.minres.scviewer.database.loader">
<implementation class="com.minres.scviewer.database.internal.WaveformDb"/>
<reference bind="bind" cardinality="1..n" interface="com.minres.scviewer.database.IWaveformDbLoader" name="IWaveformDbLoader" policy="dynamic" unbind="unbind"/>
<reference bind="bind" cardinality="1..n" interface="com.minres.scviewer.database.IWaveformDbLoaderFactory" name="IWaveformDbLoaderFactory" policy="dynamic" unbind="unbind"/>
</scr:component>

View File

@ -4,9 +4,9 @@
<parent>
<groupId>com.minres.scviewer</groupId>
<artifactId>com.minres.scviewer.parent</artifactId>
<version>2.12.1</version>
<version>2.15.1</version>
<relativePath>../..</relativePath>
</parent>
<packaging>eclipse-plugin</packaging>
<version>3.0.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
</project>

View File

@ -0,0 +1,41 @@
package com.minres.scviewer.database;
import java.util.Arrays;
public class EventEntry implements Comparable<EventEntry>{
public long timestamp; // unsigned
public IEvent[] events = null;
public EventEntry(long timestamp) {
this.timestamp = timestamp;
}
public EventEntry(long timestamp, IEvent[] events) {
this.timestamp = timestamp;
this.events = events;
}
@Override
public int compareTo(EventEntry o) {
return Long.compareUnsigned(timestamp, o.timestamp);
}
@Override
public String toString() {
return String.format("e.%d@%d", events.length,timestamp);
}
public void append(IEvent value) {
if(events.length==0)
events = new IEvent[] {value};
else {
int idx = events.length;
events = Arrays.copyOf(events, idx+1);
events[idx]=value;
}
}
}

View File

@ -0,0 +1,195 @@
package com.minres.scviewer.database;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
public class EventList implements IEventList {
ArrayList<EventEntry> store = new ArrayList<>();
int start = 0;
int end = store.size();
boolean unmodifiable = false;
public class Iterator implements java.util.Iterator<EventEntry> {
EventList list;
private int pos;
public Iterator(EventList list) {
this.list=list;
this.pos=-1;
}
@Override
public boolean hasNext() {
return pos<(list.end-1);
}
@Override
public EventEntry next() {
if(hasNext()) {
pos++;
return list.store.get(pos);
} else
return null;
}
@Override
public void remove() {
list.store.remove(pos);
}
}
public EventList() {
}
private EventList(ArrayList<EventEntry> store , int start, int end) {
this.store=store;
this.start=start;
this.end=end;
this.unmodifiable=true;
}
@Override
public IEventList subMap(long from, boolean includeFrom, long to) {
//the index of the first element greater than the key, or list.size()
int beginIndex = Collections.binarySearch(store, new EventEntry(from));
int startIndex = beginIndex < 0? -(beginIndex + 1): beginIndex;
int endIndex = Collections.binarySearch(store, new EventEntry(to));
endIndex = endIndex < 0? -(endIndex + 1): endIndex+1;
if(beginIndex>0 && !includeFrom)
return new EventList(store, startIndex+1, endIndex);
else
return new EventList(store, startIndex, endIndex);
}
@Override
public int size() {
return end-start;
}
@Override
public boolean containsKey(long key) {
int index = Collections.binarySearch(store, new EventEntry(key));
return index>=0 && index>=start && index<end;
}
@Override
public IEvent[] get(long key) {
int index = Collections.binarySearch(store, new EventEntry(key));
if(index<0) return null;
return index>=start && index<end? store.get(index).events: null;
}
@Override
public void put(long key, IEvent value) {
if(unmodifiable) throw new UnsupportedOperationException();
if(store.size()==0 || store.get(store.size()-1).timestamp < key) {
store.add(new EventEntry(key, new IEvent[] {value}));
} else {
int index = Collections.binarySearch(store, new EventEntry(key));
// < 0 if element is not in the list, see Collections.binarySearch
if (index < 0) {
EventEntry e = new EventEntry(key, new IEvent[] {value});
index = -(index + 1);
store.add(index, e);
} else {
// Insertion index is index of existing element, to add new element behind it increase index
store.get(index).append(value);
}
}
end=store.size();
}
@Override
public Collection<EventEntry> entrySet() {
if(start != 0 || end != store.size())
return Collections.unmodifiableList(store.subList(start, end));
else
return Collections.unmodifiableList(store);
}
@Override
public boolean isEmpty() {
return start==end || store.isEmpty();
}
@Override
public long firstKey() {
return store.get(start).timestamp;
}
@Override
public long lastKey() {
return store.get(end-1).timestamp;
}
// Navigable map functions
@Override
public EventEntry firstEntry() {
return store.get(start);
}
@Override
public EventEntry lastEntry() {
return store.get(end-1);
}
@Override
public EventEntry floorEntry(long key) {
int index = Collections.binarySearch(store, new EventEntry(key));
if(index==-1) return null;
// < 0 if element is not in the list, see Collections.binarySearch
if (index < 0) {
index = -(index + 2);
}
if(index>=end)
return store.get(end-1);
return index<start? null: store.get(index);
}
@Override
public EventEntry ceilingEntry(long key) {
int index = Collections.binarySearch(store, new EventEntry(key));
// < 0 if element is not in the list, see Collections.binarySearch
if (index < 0)
index = -(index + 1);
if(index<start)
return store.get(start);
return index>=end? null: store.get(index);
}
@Override
public EventEntry lowerEntry(long key) {
int index = Collections.binarySearch(store, new EventEntry(key));
// < 0 if element is not in the list, see Collections.binarySearch
if (index < 0)
index = -(index + 1);
index--;
if(index>=end)
return store.get(end-1);
return index>=end || index<start ?null:store.get(index);
}
@Override
public EventEntry higherEntry(long key) {
int index = Collections.binarySearch(store, new EventEntry(key));
// < 0 if element is not in the list, see Collections.binarySearch
if (index < 0)
index = -(index + 1);
else
index++;
if(index<start)
return store.get(start);
return index>=end || index<start ?null:store.get(index);
}
@Override
public java.util.Iterator<EventEntry> iterator() {
return new Iterator(this);
}
}

View File

@ -80,7 +80,7 @@ public class HierNode implements IHierNode {
}
/**
* Gets the full name.
* Gets the full hierarchical name.
*
* @return the full name
*/

View File

@ -0,0 +1,84 @@
package com.minres.scviewer.database;
import java.util.Collection;
public interface IEventList extends Iterable<EventEntry> {
int size();
Collection<EventEntry> entrySet();
boolean containsKey(long key);
IEvent[] get(long key);
void put(long key, IEvent value);
long firstKey();
long lastKey();
boolean isEmpty();
/**
* Returns a key-value mapping associated with the greatest key less
* than or equal to the given key, or null if there is no such key.
*
* @param key
* @return
*/
EventEntry floorEntry(long key);
/**
* Returns a key-value mapping associated with the least key greater
* than or equal to the given key, or null if there is no such key.
*
* @param key
* @return
*/
EventEntry ceilingEntry(long key);
/**
* Returns a key-value mapping associated with the least key in this map,
* or null if the map is empty.
*
* @return
*/
EventEntry firstEntry();
/**
* Returns a key-value mapping associated with the least key in this map,
* or null if the map is empty.
*
* @return
*/
EventEntry lastEntry();
/**
* Returns a key-value mapping associated with the least key strictly greater
* than the given key, or null if there is no such key.
*
* @param key
* @return
*/
EventEntry higherEntry(long key);
/**
* Returns a key-value mapping associated with the greatest key strictly less
* than the given key, or null if there is no such key.
*
* @param key
* @return
*/
EventEntry lowerEntry(long key);
/**
* Returns a view of the portion of this map whose keys range from
* {@code fromKey} to {@code toKey}. If {@code fromKey} and
* {@code toKey} are equal, the returned map is empty unless
* {@code fromInclusive} is true. The
* returned map is backed by this map, so changes in the returned map are
* reflected in this map, and vice-versa. The returned map supports all
* optional map operations that this map supports.
*
* @param fromKey low endpoint of the keys in the returned map
* @param fromInclusive {@code true} if the low endpoint
* is to be included in the returned view
* @param toKey high endpoint of the keys in the returned map (inclusive)
*/
IEventList subMap(long key, boolean b, long key2);
}

View File

@ -10,7 +10,6 @@
*******************************************************************************/
package com.minres.scviewer.database;
import java.util.NavigableMap;
// TODO: Auto-generated Javadoc
/**
@ -25,7 +24,7 @@ public interface IWaveform extends IHierNode {
*
* @return the id
*/
public Long getId();
public long getId();
/**
* Checks if is same.
@ -40,7 +39,7 @@ public interface IWaveform extends IHierNode {
*
* @return the events
*/
public NavigableMap<Long, IEvent[]> getEvents();
public IEventList getEvents();
/**
* Gets the events at time.
@ -48,7 +47,7 @@ public interface IWaveform extends IHierNode {
* @param time the time
* @return the events at time
*/
public IEvent[] getEventsAtTime(Long time);
public IEvent[] getEventsAtTime(long time);
/**
* Gets the events before time.
@ -56,7 +55,7 @@ public interface IWaveform extends IHierNode {
* @param time the time
* @return the events before time
*/
public IEvent[] getEventsBeforeTime(Long time);
public IEvent[] getEventsBeforeTime(long time);
/**
* Gets the type.

Some files were not shown because too many files have changed in this diff Show More