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 java.util.ArrayList;
28 import java.util.Date;
29 import java.util.Iterator;
30
31 import org.utgenome.gwt.utgb.client.track.Track;
32 import org.utgenome.gwt.utgb.client.track.TrackBase;
33 import org.utgenome.gwt.utgb.client.track.TrackFrame;
34 import org.utgenome.gwt.utgb.client.track.TrackGroup;
35 import org.utgenome.gwt.utgb.client.track.TrackGroupPropertyChange;
36 import org.utgenome.gwt.utgb.client.track.TrackWindow;
37 import org.utgenome.gwt.utgb.client.track.UTGBProperty;
38 import org.utgenome.gwt.utgb.client.track.bean.SequenceInfo;
39 import org.utgenome.gwt.utgb.client.ui.FormLabel;
40 import org.utgenome.gwt.utgb.client.util.CanonicalProperties;
41 import org.utgenome.gwt.utgb.client.util.JSONUtil;
42 import org.utgenome.gwt.utgb.client.util.StringUtil;
43 import org.utgenome.gwt.utgb.client.util.xml.XMLWriter;
44 import org.utgenome.gwt.widget.client.Style;
45
46 import com.google.gwt.core.client.GWT;
47 import com.google.gwt.event.dom.client.ChangeEvent;
48 import com.google.gwt.event.dom.client.ChangeHandler;
49 import com.google.gwt.event.dom.client.ClickEvent;
50 import com.google.gwt.event.dom.client.ClickHandler;
51 import com.google.gwt.event.dom.client.KeyCodes;
52 import com.google.gwt.event.dom.client.KeyUpEvent;
53 import com.google.gwt.event.dom.client.KeyUpHandler;
54 import com.google.gwt.json.client.JSONArray;
55 import com.google.gwt.json.client.JSONException;
56 import com.google.gwt.json.client.JSONObject;
57 import com.google.gwt.json.client.JSONParser;
58 import com.google.gwt.json.client.JSONValue;
59 import com.google.gwt.user.client.DOM;
60 import com.google.gwt.user.client.ui.Button;
61 import com.google.gwt.user.client.ui.Composite;
62 import com.google.gwt.user.client.ui.FormPanel;
63 import com.google.gwt.user.client.ui.Hidden;
64 import com.google.gwt.user.client.ui.HorizontalPanel;
65 import com.google.gwt.user.client.ui.ListBox;
66 import com.google.gwt.user.client.ui.TextBox;
67 import com.google.gwt.user.client.ui.VerticalPanel;
68 import com.google.gwt.user.client.ui.Widget;
69
70
71
72
73
74
75
76 public class NavigatorTrack extends TrackBase {
77 public static TrackFactory factory() {
78 return new TrackFactory() {
79 @Override
80 public Track newInstance() {
81 return new NavigatorTrack();
82 }
83 };
84 }
85
86 public VerticalPanel panel = new VerticalPanel();
87 private ListBox speciesBox = new ListBox();
88 private ListBox revisionBox = new ListBox();
89 private TextBox targetBox = new TextBox();
90 private TextBox regionBox = new TextBox();
91 private ArrayList<SequenceInfo> sequenceInfoList = new ArrayList<SequenceInfo>();
92
93 private class PropertyChangeHandler implements ChangeHandler {
94 private String proeprty;
95 private ListBox listBox;
96
97 public PropertyChangeHandler(String property, ListBox listBox) {
98 this.proeprty = property;
99 this.listBox = listBox;
100 }
101
102 public void onChange(ChangeEvent e) {
103 getTrackGroup().getPropertyWriter().setProperty(proeprty, listBox.getItemText(listBox.getSelectedIndex()));
104 }
105 }
106
107 private class SequenceRangeChangeListner implements KeyUpHandler {
108 public void onKeyUp(KeyUpEvent e) {
109 int keyCode = e.getNativeKeyCode();
110 if (keyCode == KeyCodes.KEY_ENTER || keyCode == KeyCodes.KEY_TAB) {
111 try {
112
113 int prevStart = getTrackWindow().getStartOnGenome();
114 int prevEnd = getTrackWindow().getEndOnGenome();
115 int width = getTrackWindow().getSequenceLength();
116 if (width < 1) {
117 width = 1;
118 }
119
120 String[] region = regionBox.getText().split("-");
121 if (region.length != 2)
122 return;
123
124 int start = StringUtil.toInt(region[0]);
125 int end = StringUtil.toInt(region[1]);
126
127 if (start <= 0) {
128 start = 1;
129 }
130
131 if (end <= start) {
132 end = start + width;
133 }
134
135 getTrackGroup().setTrackWindowLocation(start, end);
136 }
137 catch (NumberFormatException ex) {
138 GWT.log("(" + regionBox.getText() + ") is invalid range", ex);
139 }
140 }
141 }
142 }
143
144 public static void scroll(TrackGroup group, double movePercentageOnWindow) {
145 TrackWindow window = group.getTrackWindow();
146 int genomeRange = window.getEndOnGenome() - window.getStartOnGenome() + 1;
147 boolean isPlusStrand = true;
148 if (genomeRange < 0) {
149 genomeRange = -genomeRange;
150 isPlusStrand = false;
151 }
152 int offset = (int) (genomeRange * (movePercentageOnWindow / 100.0));
153 if (!isPlusStrand)
154 offset = -offset;
155
156 if (window.getStartOnGenome() + offset < 0) {
157 offset = -window.getStartOnGenome() + 1;
158 }
159 if (window.getEndOnGenome() + offset < 0) {
160 offset = -window.getEndOnGenome() + 1;
161 }
162
163 group.getPropertyWriter().setTrackWindow(window.getStartOnGenome() + offset, window.getEndOnGenome() + offset);
164 }
165
166 public static void zoom(TrackGroup group, int scaleDiff) {
167 if (scaleDiff == 0)
168 return;
169
170 TrackWindow currentWindow = group.getTrackWindow();
171 int start = currentWindow.getStartOnGenome();
172 int end = currentWindow.getEndOnGenome();
173 int windowSize = (start < end) ? end - start + 1 : start - end + 1;
174
175
176 int windowUpperBound = (int) Math.pow(10L, Math.ceil(Math.log(windowSize) / Math.log(10)));
177 int windowLowerBound = (int) Math.pow(10L, Math.floor(Math.log(windowSize) / Math.log(10)));
178
179 if (windowUpperBound == windowLowerBound) {
180 if (scaleDiff > 0)
181 windowUpperBound *= 10;
182 else
183 windowLowerBound /= 10;
184 }
185
186 final double magnificationRatio = 0.25;
187
188 int tickWidth = (int) (windowUpperBound * magnificationRatio);
189 int currentPos = windowSize / tickWidth;
190 int nextPos = currentPos + scaleDiff;
191 int nextWindowSize;
192 if (nextPos <= 0) {
193 nextWindowSize = windowLowerBound;
194 }
195 else
196 nextWindowSize = tickWidth * nextPos;
197
198 zoomWindow(group, nextWindowSize);
199 }
200
201 public final static int MINIMUM_WINDOW_SIZE = 10;
202
203 public static void zoomWindow(TrackGroup group, int windowSize) {
204 TrackWindow currentWindow = group.getTrackWindow();
205 int start = currentWindow.getStartOnGenome();
206 int end = currentWindow.getEndOnGenome();
207
208 int middle = (start + end) / 2;
209
210 if (windowSize <= MINIMUM_WINDOW_SIZE)
211 windowSize = MINIMUM_WINDOW_SIZE;
212 if (windowSize >= 1000000000)
213 windowSize = 1000000000;
214
215 int half = windowSize / 2;
216 int remaining = windowSize % 2;
217
218 if (middle - half < 0)
219 middle = half;
220
221 if (start <= end)
222 group.setTrackWindowLocation(middle - half + 1, middle + half + remaining);
223 else
224 group.setTrackWindowLocation(middle + half + remaining, middle - half + 1);
225
226 }
227
228 class ScrollButtonSet extends Composite {
229 HorizontalPanel _panel = new HorizontalPanel();
230
231 class WindowScrollButton extends Button implements ClickHandler {
232 int movePercentageOnWindow;
233
234 public WindowScrollButton(String label, int movePercentageOnWindow) {
235 super(label);
236 setStyleName("scrollbutton");
237 addClickHandler(this);
238 this.movePercentageOnWindow = movePercentageOnWindow;
239 }
240
241 public void onClick(ClickEvent e) {
242 scroll(getTrackGroup(), movePercentageOnWindow);
243 }
244 }
245
246 class ZoomButton extends Button implements ClickHandler {
247 int windowSize;
248
249 public ZoomButton(String label, int windowSize) {
250 super(label);
251 this.windowSize = windowSize;
252 setStyleName("scrollbutton");
253 addClickHandler(this);
254 }
255
256 public void onClick(ClickEvent e) {
257 zoomWindow(getTrackGroup(), windowSize);
258 }
259 }
260
261 public ScrollButtonSet() {
262 _panel.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
263 _panel.add(new WindowScrollButton("<<< ", -95));
264 _panel.add(new WindowScrollButton("<< ", -50));
265 _panel.add(new WindowScrollButton("< ", -25));
266 _panel.add(new WindowScrollButton("> ", 25));
267 _panel.add(new WindowScrollButton(">> ", 50));
268 _panel.add(new WindowScrollButton(">>> ", 95));
269 _panel.add(new FormLabel("Window Size:"));
270 _panel.add(new ZoomButton("100B", 100));
271 _panel.add(new ZoomButton("1K", 1000));
272 _panel.add(new ZoomButton("10K", 10000));
273 _panel.add(new ZoomButton("100K", 100000));
274 _panel.add(new ZoomButton("1M", 1000000));
275 _panel.add(new ZoomButton("10M", 10000000));
276 _panel.add(new ZoomButton("100M", 100000000));
277 _panel.add(new ZoomButton("1G", 1000000000));
278 initWidget(_panel);
279 }
280 }
281
282 private final HorizontalPanel hp = new HorizontalPanel();
283 private final Track _self = this;
284 private boolean isPlusStrand = true;
285
286 public NavigatorTrack() {
287 super("NavigatorTrack");
288 panel.setStyleName("toolbox");
289 panel.setWidth("100%");
290
291 speciesBox.addChangeHandler(new PropertyChangeHandler(UTGBProperty.SPECIES, speciesBox));
292 revisionBox.addChangeHandler(new PropertyChangeHandler(UTGBProperty.REVISION, revisionBox));
293 regionBox.addKeyUpHandler(new SequenceRangeChangeListner());
294 targetBox.addKeyUpHandler(new KeyUpHandler() {
295 public void onKeyUp(KeyUpEvent e) {
296 int keyCode = e.getNativeKeyCode();
297 if (keyCode == KeyCodes.KEY_ENTER || keyCode == KeyCodes.KEY_TAB) {
298 getTrackGroup().getPropertyWriter().setProperty(UTGBProperty.TARGET, targetBox.getText());
299 }
300 }
301 });
302 targetBox.setWidth("100px");
303
304 hp.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
305 hp.add(new FormLabel("Species"));
306 hp.add(speciesBox);
307 hp.add(new FormLabel("Ref."));
308 hp.add(revisionBox);
309 hp.add(new FormLabel("Chr."));
310 hp.add(targetBox);
311
312 regionBox.setWidth("160px");
313 HorizontalPanel hp2 = new HorizontalPanel();
314 hp2.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
315 hp2.add(new FormLabel("Region"));
316 hp2.add(regionBox);
317
318 Button strandSwitch = new Button("reverse");
319 Style.margin(strandSwitch, Style.LEFT, 2);
320 Style.border(strandSwitch, 2, Style.BORDER_OUTSET, "white");
321 strandSwitch.addClickHandler(new ClickHandler() {
322 public void onClick(ClickEvent e) {
323 isPlusStrand = !isPlusStrand;
324 TrackWindow window = getTrackGroup().getTrackWindow();
325 if (isPlusStrand) {
326 getTrackGroup().setTrackWindowLocation(window.getEndOnGenome(), window.getStartOnGenome());
327 }
328 else {
329 getTrackGroup().setTrackWindowLocation(window.getEndOnGenome(), window.getStartOnGenome());
330 }
331 }
332 });
333
334
335 hp2.add(new ScrollButtonSet());
336
337 final FormPanel saveViewForm = new FormPanel();
338 saveViewForm.setAction(GWT.getModuleBaseURL() + "utgb-core/EchoBackView");
339 saveViewForm.setEncoding(FormPanel.ENCODING_URLENCODED);
340 saveViewForm.setMethod(FormPanel.METHOD_POST);
341 final Hidden viewData = new Hidden("view");
342 final Hidden time = new Hidden("time");
343 final Button saveButton = new Button("save view");
344 HorizontalPanel formLayout = new HorizontalPanel();
345 formLayout.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
346 viewData.setVisible(false);
347 formLayout.add(viewData);
348 formLayout.add(time);
349 formLayout.add(saveButton);
350 saveButton.addClickHandler(new ClickHandler() {
351 public void onClick(ClickEvent e) {
352 XMLWriter xmlWriter = new XMLWriter();
353 getTrackGroup().toXML(xmlWriter);
354 String view = xmlWriter.toString();
355 viewData.setValue(view);
356
357 Date today = new Date();
358 time.setValue(Long.toString(today.getTime()));
359 saveViewForm.submit();
360 }
361 });
362 saveViewForm.add(formLayout);
363 DOM.setStyleAttribute(saveViewForm.getElement(), "margin", "0");
364 hp.add(saveViewForm);
365 Button loadButton = new Button("load view");
366 loadButton.addClickHandler(new ClickHandler() {
367 public void onClick(ClickEvent e) {
368 getTrackGroup().insertTrack(new ViewLoaderTrack(), getTrackGroup().getTrackIndex(_self) + 1);
369 }
370 });
371 hp.add(loadButton);
372
373 panel.add(hp);
374 panel.add(hp2);
375
376 }
377
378 public Widget getWidget() {
379 return panel;
380 }
381
382 private void retrieveSpeciesList() {
383 speciesBox.clear();
384 for (Iterator<SequenceInfo> it = sequenceInfoList.iterator(); it.hasNext();) {
385 SequenceInfo sequenceInfo = it.next();
386 speciesBox.addItem(sequenceInfo.getSpecies());
387 }
388 if (!sequenceInfoList.isEmpty())
389 updateListBox();
390 }
391
392 private void updateListBox() {
393 String species = getSelectedSpecies();
394
395 ArrayList<String> revisionList = new ArrayList<String>();
396 for (Iterator<SequenceInfo> it = sequenceInfoList.iterator(); it.hasNext();) {
397 SequenceInfo sequenceInfo = it.next();
398 if (sequenceInfo.getSpecies().equals(species)) {
399 revisionBox.clear();
400 for (Iterator<String> rit = sequenceInfo.getRevisionList().iterator(); rit.hasNext();) {
401 String revision = rit.next();
402 revisionBox.addItem(revision);
403 }
404 }
405 }
406
407 boolean canSelectRevision = selectItem(revisionBox, getTrackGroup().getPropertyReader().getProperty(UTGBProperty.REVISION));
408 if (!canSelectRevision) {
409 getTrackGroup().getPropertyWriter().setProperty(UTGBProperty.REVISION, revisionBox.getItemText(0));
410 }
411
412 }
413
414 private String getSelectedSpecies() {
415 return speciesBox.getItemText(speciesBox.getSelectedIndex());
416 }
417
418 private boolean selectItem(ListBox listBox, String value) {
419 for (int i = 0; i < listBox.getItemCount(); i++) {
420 String itemText = listBox.getItemText(i);
421 if (itemText.equals(value)) {
422 listBox.setSelectedIndex(i);
423 return true;
424 }
425 }
426 return false;
427 }
428
429 public void updateRangeBox() {
430 regionBox.setText(StringUtil.formatNumber(getTrackWindow().getStartOnGenome()) + "-" + StringUtil.formatNumber(getTrackWindow().getEndOnGenome()));
431 }
432
433 @Override
434 public void onChangeTrackGroupProperty(TrackGroupPropertyChange change) {
435 final String[] relatedProperties = new String[] { UTGBProperty.SPECIES, UTGBProperty.REVISION, UTGBProperty.TARGET };
436 if (change.containsOneOf(relatedProperties)) {
437 String newSpecies = change.getProperty(UTGBProperty.SPECIES);
438 if (newSpecies != null && !newSpecies.equals(getSelectedSpecies())) {
439 selectItem(speciesBox, newSpecies);
440 }
441 updateListBox();
442
443 if (change.contains(UTGBProperty.TARGET))
444 targetBox.setText(change.getProperty(UTGBProperty.TARGET));
445
446 updateRangeBox();
447 }
448 }
449
450 @Override
451 public void onChangeTrackWindow(TrackWindow newWindow) {
452 updateRangeBox();
453 }
454
455 @Override
456 public void setUp(TrackFrame trackFrame, TrackGroup group) {
457 trackFrame.disableClose();
458 TrackWindow w = group.getTrackWindow();
459 regionBox.setText(StringUtil.formatNumber(w.getStartOnGenome()) + "-" + StringUtil.formatNumber(w.getEndOnGenome()));
460 targetBox.setText(group.getPropertyReader().getProperty("target", "chr1"));
461
462 retrieveSpeciesList();
463 }
464
465 @Override
466 public void saveProperties(CanonicalProperties saveData) {
467
468 StringBuffer buf = new StringBuffer();
469 buf.append("[");
470 int count = 0;
471 for (Iterator<SequenceInfo> it = sequenceInfoList.iterator(); it.hasNext(); count++) {
472 if (count > 0)
473 buf.append(",");
474 SequenceInfo sequenceInfo = it.next();
475 buf.append(sequenceInfo.toJSON());
476 }
477 buf.append("]");
478 saveData.add("sequenceList", buf.toString());
479 }
480
481 @Override
482 public void restoreProperties(CanonicalProperties properties) {
483
484 try {
485 JSONValue v = JSONParser.parse(properties.get("sequenceList", "[]"));
486 sequenceInfoList.clear();
487 JSONArray list = v.isArray();
488 if (list != null) {
489 for (int i = 0; i < list.size(); i++) {
490 JSONObject sequenceInfo = list.get(i).isObject();
491 if (sequenceInfo != null) {
492 JSONValue speciesValue = sequenceInfo.get("species");
493 if (speciesValue == null)
494 continue;
495
496 String species = JSONUtil.toStringValue(speciesValue);
497 SequenceInfo si = new SequenceInfo(species);
498 JSONValue arrayValue = sequenceInfo.get("revision");
499 JSONArray revisionArray = arrayValue.isArray();
500 for (int j = 0; j < revisionArray.size(); j++) {
501 String revision = JSONUtil.toStringValue(revisionArray.get(j));
502 si.addRevision(revision);
503 }
504 sequenceInfoList.add(si);
505 }
506 }
507 }
508 }
509 catch (JSONException e) {
510 GWT.log("failed to parse json : " + properties.get("sequenceList"), e);
511 }
512
513 }
514 }