View Javadoc

1   /*--------------------------------------------------------------------------
2    *  Copyright 2008 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  // utgb-core Project
18  //
19  // UTGBEntryPointBase.java
20  // Since: Jun 2, 2008
21  //
22  // $URL$ 
23  // $Author$
24  //--------------------------------------
25  package org.utgenome.gwt.utgb.client;
26  
27  import java.util.ArrayList;
28  import java.util.HashMap;
29  
30  import org.utgenome.gwt.utgb.client.track.TrackFrame;
31  import org.utgenome.gwt.utgb.client.track.TrackGroup;
32  import org.utgenome.gwt.utgb.client.track.TrackGroupProperty;
33  import org.utgenome.gwt.utgb.client.track.TrackGroupPropertyChange;
34  import org.utgenome.gwt.utgb.client.track.TrackGroupPropertyChangeListener;
35  import org.utgenome.gwt.utgb.client.track.TrackQueue;
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.ui.RoundCornerFrame;
39  import org.utgenome.gwt.utgb.client.util.BrowserInfo;
40  import org.utgenome.gwt.utgb.client.util.Properties;
41  import org.utgenome.gwt.utgb.client.util.StringUtil;
42  import org.utgenome.gwt.utgb.client.view.TrackView;
43  import org.utgenome.gwt.widget.client.Style;
44  
45  import com.google.gwt.core.client.EntryPoint;
46  import com.google.gwt.core.client.GWT;
47  import com.google.gwt.dom.client.Element;
48  import com.google.gwt.dom.client.EventTarget;
49  import com.google.gwt.event.dom.client.KeyCodes;
50  import com.google.gwt.event.logical.shared.ResizeEvent;
51  import com.google.gwt.event.logical.shared.ResizeHandler;
52  import com.google.gwt.event.logical.shared.ValueChangeEvent;
53  import com.google.gwt.event.logical.shared.ValueChangeHandler;
54  import com.google.gwt.user.client.Command;
55  import com.google.gwt.user.client.DOM;
56  import com.google.gwt.user.client.DeferredCommand;
57  import com.google.gwt.user.client.Event;
58  import com.google.gwt.user.client.History;
59  import com.google.gwt.user.client.Window;
60  import com.google.gwt.user.client.Event.NativePreviewEvent;
61  import com.google.gwt.user.client.rpc.AsyncCallback;
62  import com.google.gwt.user.client.ui.DockPanel;
63  import com.google.gwt.user.client.ui.Label;
64  import com.google.gwt.user.client.ui.PopupPanel;
65  import com.google.gwt.user.client.ui.RootPanel;
66  
67  public class UTGBEntryPointBase implements EntryPoint {
68  	// widgets
69  	private final DockPanel basePanel = new DockPanel();
70  	private final TrackGroup trackGroup = new TrackGroup("root");
71  	private TrackGroup mainGroup;
72  	private final TrackQueue trackQueue = new TrackQueue(trackGroup);
73  
74  	private HashMap<String, String> queryParam = new HashMap<String, String>();
75  
76  	public TrackGroup getTrackGroup() {
77  		return trackGroup;
78  	}
79  
80  	public TrackQueue getTrackQueue() {
81  		return trackQueue;
82  	}
83  
84  	public DockPanel getBasePanel() {
85  		return basePanel;
86  	}
87  
88  	/**
89  	 * Defines keyboard shortcuts
90  	 * 
91  	 * @author leo
92  	 * 
93  	 */
94  	public class KeyboardShortcut implements Event.NativePreviewHandler {
95  		public void onPreviewNativeEvent(NativePreviewEvent event) {
96  
97  			// handle shortcut keys
98  			int type = event.getTypeInt();
99  			int keyCode = event.getNativeEvent().getKeyCode();
100 
101 			switch (type) {
102 			case Event.ONKEYDOWN:
103 				EventTarget eventTarget = event.getNativeEvent().getEventTarget();
104 				if (Element.is(eventTarget)) {
105 					Element e = eventTarget.cast();
106 					String tagName = e.getTagName();
107 					// disable keyboard short cuts on the input form (text area, etc.)
108 					if (tagName.equalsIgnoreCase("input"))
109 						break;
110 
111 					// Also ignore keyboard input, ALT+(key)
112 					if (event.getNativeEvent().getAltKey())
113 						break;
114 
115 					double scrollPercentage = 20.0;
116 					if (event.getNativeEvent().getShiftKey())
117 						scrollPercentage = 25.0;
118 
119 					switch (keyCode) {
120 					case KeyCodes.KEY_RIGHT:
121 						trackGroup.getPropertyWriter().scrollTrackWindow(scrollPercentage);
122 						event.getNativeEvent().preventDefault();
123 						break;
124 					case KeyCodes.KEY_LEFT:
125 						trackGroup.getPropertyWriter().scrollTrackWindow(-scrollPercentage);
126 						event.getNativeEvent().preventDefault();
127 						break;
128 					case KeyCodes.KEY_UP:
129 						trackGroup.getPropertyWriter().scaleUpTrackWindow();
130 						event.getNativeEvent().preventDefault();
131 						break;
132 					case KeyCodes.KEY_DOWN:
133 						trackGroup.getPropertyWriter().scaleDownTrackWindow();
134 						event.getNativeEvent().preventDefault();
135 						break;
136 					}
137 
138 				}
139 				break;
140 			}
141 
142 		}
143 
144 	}
145 
146 	public void onModuleLoad() {
147 		RPCServiceManager.initServices();
148 		queryParam = BrowserInfo.getURLQueryRequestParameters();
149 		RootPanel.get().setStyleName("utgb");
150 
151 		basePanel.add(trackQueue, DockPanel.CENTER);
152 
153 		History.addValueChangeHandler(new HistoryChangeHandler());
154 		Event.addNativePreviewHandler(new KeyboardShortcut());
155 
156 		// add window size change listener
157 		Window.addResizeHandler(new ResizeHandler() {
158 
159 			public void onResize(ResizeEvent e) {
160 				adjustTrackWidth();
161 
162 			}
163 		});
164 
165 		// invoke main method
166 		main();
167 
168 		if (BrowserInfo.isIE()) {
169 			showErrorMessage("IE does not support canvas feature in HTML5 for drawing grpahics in the browser, so we strongly recommend you to use another browser supporting HTML5, e.g., Google Chrome, Firefox, Safari, Opera, etc.");
170 		}
171 
172 	}
173 
174 	public static int computeTrackWidth() {
175 		int newBrowserWidth = Window.getClientWidth();
176 		return Math.max((int) (newBrowserWidth * 0.95) - TrackFrame.INFOPANEL_WIDTH, 150);
177 	}
178 
179 	private void adjustTrackWidth() {
180 		int newTrackWidth = computeTrackWidth();
181 		for (TrackGroup g : trackGroup.getTrackGroupList()) {
182 			g.setTrackWindowWidth(newTrackWidth);
183 		}
184 
185 	}
186 
187 	public void displayTrackView() {
188 		// load a view
189 		if (queryParam.containsKey("view")) {
190 			loadView(queryParam.get("view"));
191 		}
192 		else {
193 			loadView("default-view");
194 		}
195 
196 		RootPanel rootPanel = RootPanel.get("utgb-main");
197 		if (rootPanel != null) {
198 			rootPanel.add(basePanel);
199 		}
200 		else {
201 			RootPanel.get().add(new Label("Error: <div id=\"utgb-main\"></div> tag is not found in this HTML file."));
202 		}
203 	}
204 
205 	/**
206 	 * load the view XML file from the public/view folder.
207 	 * 
208 	 * @param viewXMLPath
209 	 */
210 	public void loadView(String viewName) {
211 
212 		RPCServiceManager.getRPCService().getTrackView(viewName, new AsyncCallback<TrackView>() {
213 			public void onFailure(Throwable e) {
214 				showErrorMessage("failed to load view: " + e.getMessage());
215 			}
216 
217 			public void onSuccess(TrackView v) {
218 				try {
219 					mainGroup = TrackGroup.createTrackGroup(v);
220 
221 					// apply the URL query parameters
222 					String hash = BrowserInfo.getHash();
223 					if (hash != null && hash.length() > 0)
224 						hash = hash.substring(1);
225 					setQueryParam(mainGroup, hash);
226 					trackGroup.addTrackGroup(mainGroup);
227 					mainGroup.addTrackGroupPropertyChangeListener(new URLRewriter(mainGroup));
228 				}
229 				catch (UTGBClientException e) {
230 					showErrorMessage("failed to load view: " + e.getMessage());
231 					GWT.log(e.getMessage(), e);
232 				}
233 			}
234 		});
235 
236 	}
237 
238 	private static class URLRewriter implements TrackGroupPropertyChangeListener {
239 		public final TrackGroup group;
240 
241 		public URLRewriter(TrackGroup group) {
242 			this.group = group;
243 		}
244 
245 		public void onChange(TrackGroupPropertyChange change, TrackWindow newWindow) {
246 			if (newWindow != null || (change != null && change.containsOneOf(UTGBProperty.coordinateParameters)))
247 				setBrowserURL();
248 		}
249 
250 		public void setBrowserURL() {
251 			ArrayList<String> prop = new ArrayList<String>();
252 
253 			TrackGroupProperty propertyReader = group.getPropertyReader();
254 			TrackWindow w = group.getTrackWindow();
255 			prop.add("start=" + w.getStartOnGenome());
256 			prop.add("end=" + w.getEndOnGenome());
257 			//prop.add("width=" + w.getWindowWidth());
258 			for (String key : propertyReader.keySet()) {
259 				prop.add(key + "=" + propertyReader.getProperty(key));
260 			}
261 
262 			String n = StringUtil.join(prop, ";");
263 			String prev = History.getToken();
264 
265 			if (prev != null && prev.equals(n))
266 				return;
267 			else {
268 				History.newItem(n, false);
269 				String s = propertyReader.getProperty(UTGBProperty.TARGET) + ":" + w.getStartOnGenome() + "-" + w.getEndOnGenome();
270 				Window.setTitle(s + " - UTGB");
271 			}
272 
273 		}
274 
275 	}
276 
277 	private class HistoryChangeHandler implements ValueChangeHandler<String> {
278 
279 		public HistoryChangeHandler() {
280 		}
281 
282 		public void onValueChange(ValueChangeEvent<String> e) {
283 			if (mainGroup != null)
284 				setQueryParam(mainGroup, e.getValue());
285 		}
286 	}
287 
288 	private static void setQueryParam(TrackGroup group, String queryParam) {
289 		TrackWindow w = group.getTrackWindow();
290 
291 		Properties p = getProperties(queryParam);
292 		if (p.containsKey("start")) {
293 			int start = Integer.parseInt(p.get("start"));
294 			int end = p.containsKey("end") ? Integer.parseInt(p.get("end")) : start + 1000;
295 			w = w.newWindow(start, end);
296 		}
297 
298 		p.remove("start");
299 		p.remove("end");
300 
301 		group.getPropertyWriter().setProperty(p, w);
302 	}
303 
304 	private static Properties getProperties(String query) {
305 		Properties properties = new Properties();
306 		if (query == null || query.length() < 1)
307 			return properties;
308 
309 		String[] keyAndValue = query.split(";");
310 		for (int i = 0; i < keyAndValue.length; i++) {
311 			String[] kv = keyAndValue[i].split("=");
312 			if (kv.length > 1)
313 				properties.put(kv[0], BrowserInfo.unescape(kv[1]));
314 			else
315 				properties.put(kv[0], "");
316 		}
317 
318 		return properties;
319 
320 	}
321 
322 	public void main() {
323 		displayTrackView();
324 	}
325 
326 	private static RoundCornerFrame errorFrame;
327 	private static Label errorLabel = new Label();
328 	private static PopupPanel errorPopup = new PopupPanel(true);
329 	{
330 		errorFrame = new RoundCornerFrame("FF6699", 0.7f, 2);
331 		errorFrame.setWidth("400px");
332 		errorFrame.setWidget(errorLabel);
333 		Style.fontColor(errorLabel, "white");
334 		errorPopup.setWidget(errorFrame);
335 	}
336 
337 	public static void showErrorMessage(final String message) {
338 
339 		DeferredCommand.addCommand(new Command() {
340 
341 			public void execute() {
342 				errorLabel.setText(message);
343 				int x = Window.getClientWidth() / 2 - 200;
344 				int y = 10;
345 				errorPopup.setPopupPosition(x, y);
346 				errorPopup.show();
347 			}
348 		});
349 	}
350 
351 	public static void hideLoadingMessage() {
352 		Element _loadingMessage = DOM.getElementById("loading");
353 		if (_loadingMessage != null) {
354 			RootPanel.setVisible(_loadingMessage, false);
355 		}
356 	}
357 
358 	public static void showLoadingMessage() {
359 		Element _loadingMessage = DOM.getElementById("loading");
360 		if (_loadingMessage != null) {
361 			RootPanel.setVisible(_loadingMessage, true);
362 		}
363 	}
364 
365 }