1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package org.utgenome.gwt.utgb.client.track.lib;
26
27 import org.utgenome.gwt.utgb.client.track.Design;
28 import org.utgenome.gwt.utgb.client.track.RangeSelectable;
29 import org.utgenome.gwt.utgb.client.track.Track;
30 import org.utgenome.gwt.utgb.client.track.TrackBase;
31 import org.utgenome.gwt.utgb.client.track.TrackFrame;
32 import org.utgenome.gwt.utgb.client.track.TrackGroup;
33 import org.utgenome.gwt.utgb.client.track.TrackRangeSelector;
34 import org.utgenome.gwt.utgb.client.track.TrackWindow;
35 import org.utgenome.gwt.utgb.client.ui.AbsoluteFocusPanel;
36 import org.utgenome.gwt.utgb.client.util.CanonicalProperties;
37 import org.utgenome.gwt.widget.client.Style;
38
39 import com.google.gwt.event.dom.client.MouseDownEvent;
40 import com.google.gwt.event.dom.client.MouseDownHandler;
41 import com.google.gwt.user.client.DOM;
42 import com.google.gwt.user.client.ui.Grid;
43 import com.google.gwt.user.client.ui.Image;
44 import com.google.gwt.user.client.ui.Label;
45 import com.google.gwt.user.client.ui.Widget;
46
47 class MouseListenerOnRulerWidget implements MouseDownHandler {
48 private final TrackRangeSelector _rangeSelector;
49
50 public MouseListenerOnRulerWidget(TrackRangeSelector rangeSelector) {
51 this._rangeSelector = rangeSelector;
52 }
53
54 public void onMouseDown(MouseDownEvent e) {
55 _rangeSelector.onMouseDownFromChild(e);
56 }
57 }
58
59
60
61
62
63
64
65 class Ruler {
66 public static String[] _unitSuffix = { "", "K", "M", "G", "T", "P" };
67 public static int RULER_HEIGHT = 14;
68 private final static String TICK_IMAGE = Design.IMAGE_RULER_TICK;
69 static {
70 Image.prefetch(TICK_IMAGE);
71 }
72 private int tickUnit = 1;
73 private int tickUnitIndex = 0;
74 private int tickRangeOnGenome = 1;
75 private final TrackRangeSelector _rangeSelector;
76 private String rulerLabelStyle = "ruler-tick";
77 private final MouseListenerOnRulerWidget _commonMouseListener;
78
79 public Ruler(TrackRangeSelector rangeSelector) {
80 this._rangeSelector = rangeSelector;
81 _commonMouseListener = new MouseListenerOnRulerWidget(_rangeSelector);
82 }
83
84 public Ruler(TrackRangeSelector rangeSelector, String labelStyle) {
85 this._rangeSelector = rangeSelector;
86 _commonMouseListener = new MouseListenerOnRulerWidget(_rangeSelector);
87 rulerLabelStyle = labelStyle;
88 }
89
90 class RulerLabel extends Label {
91 public RulerLabel(String label) {
92 super(label);
93 setStyleName(rulerLabelStyle);
94 addMouseDownHandler(_commonMouseListener);
95 }
96 }
97
98 public void updateTickUnit(int windowWidth, int s, int e) {
99
100 e++;
101 int range = (e > s) ? e - s : s - e;
102 if (range <= 0)
103 range = 1;
104 tickRangeOnGenome = calcTickWidthForRange(range);
105 long div = tickRangeOnGenome;
106 int unitCount = 0;
107 tickUnit = 1;
108 while (((div = div / 1000) > 0) && unitCount < _unitSuffix.length) {
109 tickUnit *= 1000;
110 unitCount++;
111 }
112 tickUnitIndex = unitCount;
113 }
114
115 public void draw(AbsoluteFocusPanel panel, int windowWidth, int s, int e, boolean isReverseStrand) {
116 assert (s <= e);
117 panel.setSize(windowWidth + "px", RULER_HEIGHT + "px");
118 e++;
119 int range = (e > s) ? e - s : s - e;
120 if (range <= 0)
121 range = 1;
122
123 double pixelLengthPerRange = (double) windowWidth / (double) range;
124
125 int initTickCursor = s;
126 if (((s - 1) % tickRangeOnGenome) != 0)
127 initTickCursor = ((s + tickRangeOnGenome) / tickRangeOnGenome) * tickRangeOnGenome;
128
129 for (int tickCursor = initTickCursor; tickCursor <= e; tickCursor += tickRangeOnGenome) {
130 int pos = tickCursor - s;
131 int tickX = (int) (pos * pixelLengthPerRange);
132 if (tickX >= 0) {
133 Image tick = new Image(TICK_IMAGE);
134 RulerLabel label = new RulerLabel(indexOnRuler(tickCursor));
135 if (!isReverseStrand) {
136 panel.add(tick, tickX, 0);
137 panel.add(label, tickX + 2, 0);
138 }
139 else {
140 panel.add(tick, windowWidth - tickX, 0);
141 panel.add(label, windowWidth - tickX + 2, 0);
142 }
143 }
144
145 }
146
147 }
148
149 private String indexOnRuler(int genomePos) {
150 return (genomePos / tickUnit) + _unitSuffix[tickUnitIndex];
151 }
152
153 private int calcTickWidthForRange(int range) {
154 if (range <= 1)
155 return 1;
156 int[] availableTickUnit = { 1, 2, 5, 10, 20, 25 };
157
158 int numTicksMax = 13;
159 for (int i = numTicksMax; i > 0; i--) {
160 int tickRange = range / i;
161 if (tickRange <= 0)
162 continue;
163
164 int tickFraction = range % i;
165
166 int factor = (int) (Math.log(tickRange) / Math.log(10));
167 int factor10 = (int) Math.pow(10, factor);
168 int tickUnit = tickRange / factor10;
169 int tickFractionUnit = tickFraction / factor10;
170 if (tickUnit < tickFractionUnit)
171 continue;
172 for (int m = 0; m < availableTickUnit.length; m++) {
173 if (tickUnit == availableTickUnit[m])
174 return tickRange;
175 }
176 }
177 return range;
178 }
179 }
180
181
182
183
184
185
186
187 public class RulerTrack extends TrackBase implements RangeSelectable {
188 private TrackRangeSelector _rangeSelector;
189 private Grid _layoutPanel = new Grid(1, 2);
190 private AbsoluteFocusPanel _basePanel = new AbsoluteFocusPanel();
191 private Ruler ruler;
192 private int _windowLeftMargin = 0;
193
194 public static TrackFactory factory() {
195 return new TrackFactory() {
196 @Override
197 public Track newInstance() {
198 return new RulerTrack();
199 }
200 };
201 }
202
203 public RulerTrack() {
204 super("Track Ruler");
205 init();
206 }
207
208 private void init() {
209 _basePanel.setStyleName("ruler");
210 DOM.setStyleAttribute(_basePanel.getElement(), "cursor", "pointer");
211 _basePanel.setTitle("click twice to zoom the specified range");
212 _rangeSelector = new TrackRangeSelector(this);
213 ruler = new Ruler(_rangeSelector);
214 _layoutPanel.setCellPadding(0);
215 _layoutPanel.setCellSpacing(0);
216 Style.fontSize(_layoutPanel, 0);
217 }
218
219 public RulerTrack(int windowLeftMargin) {
220 super("Track Ruler");
221 _windowLeftMargin = windowLeftMargin;
222 init();
223 }
224
225 public void clear() {
226 _basePanel.clear();
227 }
228
229 public Widget getWidget() {
230 return _layoutPanel;
231 }
232
233 @Override
234 public void draw() {
235 _basePanel.clear();
236 if (_windowLeftMargin > 0)
237 _layoutPanel.getCellFormatter().setWidth(0, 0, _windowLeftMargin + "px");
238 _layoutPanel.setWidget(0, 1, _basePanel);
239 TrackWindow w = getTrackGroup().getTrackWindow();
240 int windowWidth = w.getPixelWidth() - _windowLeftMargin;
241 int s = w.getStartOnGenome();
242 int e = w.getEndOnGenome();
243
244 if (s <= e) {
245 ruler.updateTickUnit(windowWidth, s, e);
246 ruler.draw(_basePanel, windowWidth, s, e, false);
247 }
248 else {
249 ruler.updateTickUnit(windowWidth, e, s);
250 ruler.draw(_basePanel, windowWidth, e, s, true);
251 }
252 }
253
254 @Override
255 public int getDefaultWindowHeight() {
256 return Ruler.RULER_HEIGHT;
257 }
258
259 @Override
260 public void onChangeTrackWindow(TrackWindow newWindow) {
261 _rangeSelector.setWindowWidth(newWindow.getPixelWidth());
262 refresh();
263 }
264
265 public AbsoluteFocusPanel getAbsoluteFocusPanel() {
266 return _basePanel;
267 }
268
269 public void onRangeSelect(int x1OnTrackWindow, int x2OnTrackWindow) {
270 TrackWindow w = getTrackGroup().getTrackWindow();
271 double factor = w.getPixelWidth() / (double) (w.getPixelWidth() - _windowLeftMargin);
272
273 if (x1OnTrackWindow > x2OnTrackWindow) {
274 int tmp = x1OnTrackWindow;
275 x1OnTrackWindow = x2OnTrackWindow;
276 x2OnTrackWindow = tmp;
277 }
278
279 int startOnGenome = w.convertToGenomePosition((int) (x1OnTrackWindow * factor));
280 int endOnGenome = w.convertToGenomePosition((int) (x2OnTrackWindow * factor));
281
282 getTrackGroup().getPropertyWriter().setTrackWindow(startOnGenome, endOnGenome);
283 }
284
285 @Override
286 public void setUp(TrackFrame trackFrame, TrackGroup group) {
287 trackFrame.disablePack();
288 trackFrame.disableResize();
289 }
290
291 @Override
292 public void restoreProperties(CanonicalProperties properties) {
293 super.restoreProperties(properties);
294
295 _windowLeftMargin = properties.getInt("leftMargin", _windowLeftMargin);
296 }
297
298 }