View Javadoc

1   /*--------------------------------------------------------------------------
2    *  Copyright 2007 utgenome.org
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   *--------------------------------------------------------------------------*/
16  //--------------------------------------
17  // GenomeBrowser Project
18  //
19  // RulerTrack.java
20  // Since: Jun 6, 2007
21  //
22  // $URL$ 
23  // $Author$
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   * Ruler computes a tick range within ruler tracks
61   * 
62   * @author leo
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  		// update tick unit
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  * RulerTrack
183  * 
184  * @author leo
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 }