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  // TrackFrame.java
20  // Since: Jun 6, 2007
21  //
22  // $URL$ 
23  // $Author$
24  //--------------------------------------
25  package org.utgenome.gwt.utgb.client.track;
26  
27  import org.utgenome.gwt.utgb.client.ui.Icon;
28  import org.utgenome.gwt.utgb.client.ui.IconImage;
29  import org.utgenome.gwt.utgb.client.util.Optional;
30  import org.utgenome.gwt.widget.client.Style;
31  
32  import com.google.gwt.event.dom.client.ClickEvent;
33  import com.google.gwt.event.dom.client.ClickHandler;
34  import com.google.gwt.event.dom.client.HasAllMouseHandlers;
35  import com.google.gwt.event.dom.client.MouseDownEvent;
36  import com.google.gwt.event.dom.client.MouseDownHandler;
37  import com.google.gwt.event.dom.client.MouseMoveEvent;
38  import com.google.gwt.event.dom.client.MouseMoveHandler;
39  import com.google.gwt.event.dom.client.MouseOutEvent;
40  import com.google.gwt.event.dom.client.MouseOutHandler;
41  import com.google.gwt.event.dom.client.MouseOverEvent;
42  import com.google.gwt.event.dom.client.MouseOverHandler;
43  import com.google.gwt.event.dom.client.MouseUpEvent;
44  import com.google.gwt.event.dom.client.MouseUpHandler;
45  import com.google.gwt.user.client.DOM;
46  import com.google.gwt.user.client.Event;
47  import com.google.gwt.user.client.ui.AbsolutePanel;
48  import com.google.gwt.user.client.ui.Composite;
49  import com.google.gwt.user.client.ui.DockPanel;
50  import com.google.gwt.user.client.ui.HTML;
51  import com.google.gwt.user.client.ui.HorizontalPanel;
52  import com.google.gwt.user.client.ui.Image;
53  import com.google.gwt.user.client.ui.Label;
54  import com.google.gwt.user.client.ui.ScrollPanel;
55  import com.google.gwt.user.client.ui.SimplePanel;
56  import com.google.gwt.user.client.ui.VerticalPanel;
57  
58  /**
59   * {@link TrackFrame} wraps your {@link Track}, then decorates the track with window manipulation buttons, a track
60   * label, drag & drop facility, etc.
61   * 
62   * @author leo
63   * 
64   */
65  public class TrackFrame extends SimplePanel {
66  	public static final int SCROLLBAR_WIDTH = 25;
67  	public static final int INFOPANEL_WIDTH = 250;
68  	private static IconImage _dragBarIcon = Design.getIconImage(Design.TRACK_BORDER_V);
69  	private static IconImage _resizeBarIcon = Design.getIconImage(Design.TRACK_BORDER_H);
70  
71  	class DragBar extends Image implements MouseOverHandler, MouseOutHandler {
72  		public DragBar() {
73  			super(_dragBarIcon.getImageURL());
74  			setStyleName("drag-bar");
75  			setSize("4px", "100%");
76  			setTitle("drag & drop to change track order");
77  		}
78  
79  		public void register(HasAllMouseHandlers w) {
80  			w.addMouseOverHandler(this);
81  			w.addMouseOutHandler(this);
82  		}
83  
84  		public void onMouseOver(MouseOverEvent e) {
85  			setUrl(_dragBarIcon.getMouseOverImageURL());
86  		}
87  
88  		public void onMouseOut(MouseOutEvent e) {
89  			setUrl(_dragBarIcon.getImageURL());
90  		}
91  
92  		@Override
93  		protected void onAttach() {
94  			super.onAttach();
95  			setUrl(_dragBarIcon.getImageURL());
96  		}
97  	}
98  
99  	class ResizeBar extends Image implements MouseMoveHandler, MouseOverHandler, MouseOutHandler, MouseDownHandler, MouseUpHandler {
100 		private TrackFrame _targetFrame;
101 		private boolean isMouseDown = false;
102 		private int initialY_Position = 0;
103 		private int initial_height = 0;
104 		private boolean enabled = true;
105 
106 		public ResizeBar(final TrackFrame targetWidget) {
107 			super(_resizeBarIcon.getImageURL());
108 			this._targetFrame = targetWidget;
109 			setStyleName("drag-bar");
110 			setSize(INFOPANEL_WIDTH + "px", "2px");
111 			enableResize();
112 			setTitle("Drag this bar to resize tracks");
113 
114 			register(this);
115 		}
116 
117 		private void register(HasAllMouseHandlers w) {
118 			w.addMouseMoveHandler(this);
119 
120 			w.addMouseUpHandler(this);
121 			w.addMouseDownHandler(this);
122 
123 			w.addMouseOverHandler(this);
124 			w.addMouseOutHandler(this);
125 		}
126 
127 		public void onMouseMove(MouseMoveEvent e) {
128 			if (!enabled)
129 				return;
130 
131 			if (isMouseDown) {
132 				final int diff = (e.getScreenY()) - initialY_Position;
133 				final int newSize = initial_height + diff;
134 				_targetFrame.resize(newSize);
135 				_frameState.setPreviousFrameHeight(newSize);
136 			}
137 		}
138 
139 		public void onMouseUp(MouseUpEvent e) {
140 			if (!enabled)
141 				return;
142 
143 			if (isMouseDown) {
144 				isMouseDown = false;
145 				unpack();
146 				_frameState.setMinimized(false);
147 				hideButton.update();
148 			}
149 			Event.releaseCapture(e.getRelativeElement());
150 		}
151 
152 		public void onMouseDown(MouseDownEvent e) {
153 			if (!enabled)
154 				return;
155 
156 			isMouseDown = true;
157 			initialY_Position = e.getScreenY();
158 			initial_height = _targetFrame.getOffsetHeight();
159 			Event.setCapture(e.getRelativeElement());
160 		}
161 
162 		public void onMouseOver(MouseOverEvent e) {
163 			if (!enabled)
164 				return;
165 
166 			setUrl(_resizeBarIcon.getMouseOverImageURL());
167 		}
168 
169 		public void onMouseOut(MouseOutEvent e) {
170 			if (!enabled)
171 				return;
172 
173 			setUrl(_resizeBarIcon.getImageURL());
174 		}
175 
176 		public void disableResize() {
177 			enabled = false;
178 			DOM.setStyleAttribute(this.getElement(), "cursor", "default");
179 		}
180 
181 		public void enableResize() {
182 			enabled = true;
183 			DOM.setStyleAttribute(this.getElement(), "cursor", "n-resize");
184 		}
185 	}
186 
187 	class WindowButton extends Icon {
188 		IconImage _icon;
189 
190 		public WindowButton(IconImage icon) {
191 			super(icon);
192 			// setStyleName("track-icon");
193 		}
194 	}
195 
196 	class ConfigButton extends WindowButton implements MouseDownHandler {
197 		public ConfigButton() {
198 			super(Design.getIconImage(Design.ICON_CONFIG));
199 			setTitle("config");
200 
201 			addMouseDownHandler(this);
202 		}
203 
204 		public void onMouseDown(MouseDownEvent e) {
205 			TrackConfig configPopup = _track.getConfig();
206 			assert (configPopup != null);
207 			configPopup.setPopupPosition(this.getAbsoluteLeft() + 2, this.getAbsoluteTop() + 13);
208 			configPopup.show();
209 		}
210 	}
211 
212 	class HideButton extends WindowButton implements ClickHandler {
213 		/**
214 		 * @param imageURL
215 		 * @param mouseOverImageURL
216 		 */
217 		public HideButton() {
218 			super(Design.getIconImage(Design.ICON_HIDE));
219 			setTitle("minimize");
220 			addClickHandler(this);
221 		}
222 
223 		public void onClick(ClickEvent e) {
224 			switchMinimization();
225 			update();
226 		}
227 
228 		public void update() {
229 			if (_frameState.isMinimized()) {
230 				setIcon(Design.getIconImage(Design.ICON_SHOW));
231 				setTitle("restore");
232 			}
233 			else {
234 				setIcon(Design.getIconImage(Design.ICON_HIDE));
235 				setTitle("minimize");
236 			}
237 		}
238 	}
239 
240 	class PackButton extends WindowButton implements ClickHandler {
241 		public PackButton() {
242 			super(Design.getIconImage(Design.ICON_PACK));
243 			setTitle("adjust height");
244 			addClickHandler(this);
245 		}
246 
247 		public void onClick(ClickEvent e) {
248 			switchPackUnpack();
249 			update();
250 		}
251 
252 		public void update() {
253 			// setTitle(_frameState.isPacked() ? "free height" : "automatically adjust height");
254 			updateIcon();
255 		}
256 
257 		public void updateIcon() {
258 			if (_trackWidgetFrame.getOffsetHeight() <= _layoutPanel.getOffsetHeight())
259 				setIcon(Design.getIconImage(Design.ICON_PACK));
260 			else
261 				setIcon(Design.getIconImage(Design.ICON_UNPACK));
262 		}
263 	}
264 
265 	class CloseButton extends WindowButton implements ClickHandler {
266 		TrackFrame _frame;
267 
268 		public CloseButton(TrackFrame frame) {
269 			super(Design.getIconImage(Design.ICON_CLOSE));
270 			setTitle("close");
271 			this._frame = frame;
272 			addClickHandler(this);
273 		}
274 
275 		public void onClick(ClickEvent e) {
276 			getTrack().getTrackGroup().removeTrack(_track);
277 		}
278 	}
279 
280 	private final Track _track;
281 	private final HorizontalPanel _layoutPanel = new HorizontalPanel();
282 	private final DragBar _dragBar = new DragBar();
283 	private final ResizeBar _resizeBar = new ResizeBar(this);
284 	private final TrackInfoPanel _infoPanel;
285 	private final ScrollPanel _scrollPanel = new ScrollPanel();
286 	private final DockPanel _trackWidgetFrame = new DockPanel();
287 	private final Label _messageLabel = new Label();
288 	private final ConfigButton configButton = new ConfigButton();
289 	private final HideButton hideButton = new HideButton();
290 	private final PackButton packButton = new PackButton();
291 	private final CloseButton closeButton = new CloseButton(this);
292 	TrackFrameState _frameState = new TrackFrameState();
293 
294 	/**
295 	 * <pre>
296 	 *       150px
297 	 * -------------------
298 	 * | |         |icon |
299 	 * | |               |
300 	 * | | label         |
301 	 * | |               |
302 	 * | |---------------|
303 	 * | | resize bar    |
304 	 * -------------------
305 	 * </pre>
306 	 * 
307 	 * @author leo
308 	 * 
309 	 */
310 	class TrackInfoPanel extends Composite implements TrackInfoChangeListener {
311 		private final AbsolutePanel basePanel = new AbsolutePanel();
312 		private final HorizontalPanel _labelFrame = new HorizontalPanel();
313 		private final VerticalPanel frameWithResizeBar = new VerticalPanel();
314 		private final Image _loadingIcon = new Image(Design.IMAGE_NOW_LOADING);
315 		private final HTML _trackLabel = new HTML();
316 		private final int DRAGBAR_WIDTH = 4;
317 		private final int RESIZE_BAR_HEIGHT = 2;
318 		private boolean _disablePackButton = false;
319 		private Optional<Boolean> _disableConfigButton = new Optional<Boolean>();
320 		private boolean _disableCloseButton = false;
321 		private boolean _disableHideButton = false;
322 		private boolean _nowLoading = false;
323 
324 		public TrackInfoPanel(TrackFrame trackFrame, int height) {
325 			String h = height + "px";
326 			basePanel.setStyleName("track-info");
327 			basePanel.setSize(INFOPANEL_WIDTH + "px", h);
328 			// drag bars
329 			basePanel.add(_dragBar, 0, 0);
330 			_dragBar.register(_trackLabel);
331 			// track label
332 
333 			_trackLabel.setStyleName("track-label");
334 			_trackLabel.setWidth((INFOPANEL_WIDTH - 20) + "px");
335 			Style.trimOverflowedText(_trackLabel);
336 
337 			updateTrackLabel();
338 			_labelFrame.setSize("100%", "100%");
339 			_labelFrame.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
340 			_labelFrame.setHorizontalAlignment(HorizontalPanel.ALIGN_LEFT);
341 			_labelFrame.add(_trackLabel);
342 
343 			frameWithResizeBar.setSize((INFOPANEL_WIDTH - DRAGBAR_WIDTH) + "px", "100%");
344 			frameWithResizeBar.setVerticalAlignment(VerticalPanel.ALIGN_BOTTOM);
345 			frameWithResizeBar.add(_labelFrame);
346 			frameWithResizeBar.add(_resizeBar);
347 			basePanel.add(frameWithResizeBar, DRAGBAR_WIDTH, 0);
348 			// icon
349 			drawIcon();
350 			// listen to the track information change
351 			getTrack().getTrackInfo().addChangeListener(this);
352 			initWidget(basePanel);
353 		}
354 
355 		void disablePackButton() {
356 			_disablePackButton = true;
357 			basePanel.remove(packButton);
358 			drawIcon();
359 		}
360 
361 		void disableConfigButton() {
362 			_disableConfigButton.set(true);
363 			basePanel.remove(configButton);
364 			drawIcon();
365 		}
366 
367 		void enableConfigButton() {
368 			_disableConfigButton.set(false);
369 			drawIcon();
370 		}
371 
372 		void disableCloseButton() {
373 			_disableCloseButton = true;
374 			basePanel.remove(closeButton);
375 			drawIcon();
376 		}
377 
378 		void disableHideButton() {
379 			_disableHideButton = true;
380 			basePanel.remove(hideButton);
381 			drawIcon();
382 		}
383 
384 		static final int ICON_WIDTH = 15 + 1;
385 
386 		void drawIcon() {
387 			final int yOffset = 1;
388 			int xPos = INFOPANEL_WIDTH;
389 			if (!_disableCloseButton) {
390 				xPos -= ICON_WIDTH;
391 				basePanel.add(closeButton, xPos, yOffset);
392 			}
393 			if (!_disableHideButton) {
394 				xPos -= ICON_WIDTH;
395 				basePanel.add(hideButton, xPos, yOffset);
396 			}
397 			if (!_disablePackButton) {
398 				xPos -= ICON_WIDTH;
399 				basePanel.add(packButton, xPos, yOffset);
400 			}
401 			if (!isDisabledConfigButton()) {
402 				xPos -= ICON_WIDTH;
403 				basePanel.add(configButton, xPos, yOffset);
404 			}
405 		}
406 
407 		private boolean isDisabledConfigButton() {
408 			if (_disableConfigButton.isDefined())
409 				return !_disableConfigButton.get();
410 			else {
411 				return !(_track.getConfig() != null && _track.getConfig().hasProperties());
412 			}
413 		}
414 
415 		public void nowLoading(boolean nowLoading) {
416 			_nowLoading = nowLoading;
417 			if (_nowLoading) {
418 				int xPos = INFOPANEL_WIDTH;
419 				int numIcon = 0;
420 				if (!_disableCloseButton)
421 					numIcon++;
422 				if (!_disableHideButton)
423 					numIcon++;
424 				if (!_disablePackButton)
425 					numIcon++;
426 				if (!isDisabledConfigButton())
427 					numIcon++;
428 				xPos -= ICON_WIDTH * (numIcon + 1);
429 				basePanel.add(_loadingIcon, xPos, 1);
430 			}
431 			else {
432 				basePanel.remove(_loadingIcon);
433 			}
434 		}
435 
436 		void updateTrackLabel() {
437 			TrackInfo info = getTrack().getTrackInfo();
438 			_trackLabel.setText(info.getTrackName());
439 			_trackLabel.setTitle(info.getTrackName() + ": " + info.getDescription());
440 
441 			String linkURL = info.getLinkURL();
442 			if (linkURL.length() > 0) {
443 				_trackLabel.setHTML("<a href=\"" + linkURL + "\" target=\"_blank\">" + info.getTrackName() + "</a>");
444 			}
445 		}
446 
447 		public void onChange(TrackInfo info) {
448 			updateTrackLabel();
449 		}
450 	}
451 
452 	public TrackFrame(Track track, int width, int height) {
453 		this._track = track;
454 		_frameState.setMinFrameHeight(track.getMinimumWindowHeight());
455 		_frameState.setPreviousFrameHeight(height);
456 		_infoPanel = new TrackInfoPanel(this, height);
457 		// _layoutPanel.setStyleName("track");
458 		// DOM.setStyleAttribute(_layoutPanel.getElement(), "overflow-x", "hidden");
459 		// CSS.hideHorizontalScrollBar(_layoutPanel);
460 		// layout the track components
461 		_layoutPanel.add(_infoPanel);
462 		// CSS.hideHorizontalScrollBar(_scrollPanel);
463 		// DOM.setStyleAttribute(_scrollPanel.getElement(), "overflow-x", "hidden");
464 		_trackWidgetFrame.setWidth("100%");
465 		_trackWidgetFrame.setStyleName("track");
466 		_trackWidgetFrame.add(_track.getWidget(), DockPanel.CENTER);
467 		_messageLabel.setStyleName("trackframe-message");
468 		_messageLabel.setWidth("100%");
469 		_messageLabel.setHorizontalAlignment(Label.ALIGN_CENTER);
470 		_scrollPanel.setWidget(_trackWidgetFrame);
471 		_scrollPanel.setSize((width + SCROLLBAR_WIDTH) + "px", height + "px");
472 		_layoutPanel.add(_scrollPanel);
473 		this.add(_layoutPanel);
474 		setFrameHeight(height);
475 		_track.setFrame(this);
476 	}
477 
478 	public TrackFrame(Track track, int width) {
479 		this(track, width, track.getDefaultWindowHeight());
480 	}
481 
482 	public void setFrameHeight(int height) {
483 		String h = _frameState.resizeFrameHeight(height) + "px";
484 		_infoPanel.setHeight(h);
485 		_scrollPanel.setHeight(h);
486 		_layoutPanel.setHeight(h);
487 	}
488 
489 	public int getContentHeight() {
490 		if (_frameState.isMinimized())
491 			return _frameState.getPreviousFrameHeight();
492 		else
493 			return _scrollPanel.getOffsetHeight();
494 	}
495 
496 	public void resize(int height) {
497 		setFrameHeight(height);
498 		packButton.updateIcon();
499 		_track.getTrackGroup().notifyResize();
500 		_track.onChangeTrackHeight(height);
501 	}
502 
503 	public void adjustFrameHeight() {
504 		if (_frameState.isPacked()) {
505 			// int newHeight = _track.getWidget().getOffsetHeight();
506 			int newHeight = _trackWidgetFrame.getOffsetHeight();
507 			setFrameHeight(newHeight);
508 		}
509 		resizeTrackAreaWidth(_track.getTrackGroup().getTrackWindow().getPixelWidth());
510 	}
511 
512 	public void onUpdateTrackWidget() {
513 		adjustFrameHeight();
514 		packButton.updateIcon();
515 		_track.getTrackGroup().notifyResize();
516 	}
517 
518 	public void resizeTrackAreaWidth(int newWidth) {
519 		_scrollPanel.setWidth((newWidth + SCROLLBAR_WIDTH) + "px");
520 	}
521 
522 	public void open() {
523 		if (_frameState.isMinimized())
524 			switchMinimization();
525 	}
526 
527 	public void minimize() {
528 		if (!_frameState.isMinimized()) {
529 			switchMinimization();
530 		}
531 	}
532 
533 	private int appropriateWidgetHeight(boolean toMinimize, boolean toPack) {
534 		if (toMinimize)
535 			// return minimumFrameHeight;
536 			return TrackFrameState.DEFAULT_MIN_TRACKFRAME_HEIGHT;
537 		else if (toPack) {
538 			if (_frameState.isMinimized())
539 				return _frameState.getPreviousFrameHeight();
540 			else
541 				// return _track.getWidget().getOffsetHeight();
542 				return _trackWidgetFrame.getOffsetHeight();
543 		}
544 		else
545 			return _frameState.getPreviousFrameHeight();
546 	}
547 
548 	public void switchMinimization() {
549 		int height = appropriateWidgetHeight(!_frameState.isMinimized(), _frameState.isPacked());
550 		if (_frameState.isMinimized()) {
551 			// _layoutPanel.add(_scrollPanel);
552 		}
553 		else {
554 			_frameState.setPreviousFrameHeight(this.getOffsetHeight());
555 			// _layoutPanel.remove(_scrollPanel); // hide the track
556 		}
557 		_frameState.setMinimized(!_frameState.isMinimized());
558 		resize(height);
559 		hideButton.update();
560 	}
561 
562 	public void switchPackUnpack() {
563 		open();
564 		int height = appropriateWidgetHeight(_frameState.isMinimized(), !_frameState.isPacked());
565 		resize(height);
566 		_frameState.setPacked(!_frameState.isPacked());
567 		packButton.update();
568 	}
569 
570 	/**
571 	 * Pack (fullly display: no scroll bar) the track content
572 	 */
573 	public void pack() {
574 		if (!_frameState.isPacked())
575 			switchPackUnpack();
576 	}
577 
578 	/**
579 	 * Use scroll bar to display the track content
580 	 */
581 	public void unpack() {
582 		if (_frameState.isPacked())
583 			switchPackUnpack();
584 	}
585 
586 	public void setPacked(boolean packed) {
587 		if (packed)
588 			pack();
589 		else
590 			unpack();
591 	}
592 
593 	public void enableConfig() {
594 		_infoPanel.enableConfigButton();
595 	}
596 
597 	public void disableConfig() {
598 		_infoPanel.disableConfigButton();
599 	}
600 
601 	public boolean isPacked() {
602 		return _frameState.isPacked();
603 	}
604 
605 	public void disablePack() {
606 		_infoPanel.disablePackButton();
607 	}
608 
609 	public void disableResize() {
610 		_resizeBar.disableResize();
611 	}
612 
613 	public void enableResize() {
614 		_resizeBar.enableResize();
615 	}
616 
617 	public void disableClose() {
618 		_infoPanel.disableCloseButton();
619 	}
620 
621 	public void disableHide() {
622 		_infoPanel.disableHideButton();
623 	}
624 
625 	public Track getTrack() {
626 		return _track;
627 	}
628 
629 	public Label getFrameBar() {
630 		return _infoPanel._trackLabel;
631 	}
632 
633 	public Label getMessageLabel() {
634 		return _messageLabel;
635 	}
636 
637 	public void setVisibleMessageLabel(final boolean visible) {
638 		if (visible) {
639 			if (_trackWidgetFrame.getWidgetIndex(_messageLabel) == -1) {
640 				_trackWidgetFrame.add(_messageLabel, DockPanel.NORTH);
641 			}
642 			_messageLabel.setText("");
643 		}
644 		else {
645 			if (_trackWidgetFrame.getWidgetIndex(_messageLabel) != -1) {
646 				_trackWidgetFrame.remove(_messageLabel);
647 			}
648 		}
649 	}
650 
651 	public void writeMessage(String message) {
652 		if (_trackWidgetFrame.getWidgetIndex(_messageLabel) == -1) {
653 			_trackWidgetFrame.add(_messageLabel, DockPanel.NORTH);
654 		}
655 		_messageLabel.setText(message);
656 	}
657 
658 	public void eraseMessage(boolean eraseLabel) {
659 		if (_trackWidgetFrame.getWidgetIndex(_messageLabel) != -1) {
660 			if (eraseLabel)
661 				_trackWidgetFrame.remove(_messageLabel);
662 			else
663 				_messageLabel.setText("");
664 		}
665 	}
666 
667 	public void setFrameState(TrackFrameState frameState) {
668 		_frameState = frameState;
669 		packButton.update();
670 		hideButton.update();
671 		// setHeight(_frameState.getPreviousFrameHeight() + "px");
672 	}
673 
674 	public void setNowLoading() {
675 		_infoPanel.nowLoading(true);
676 	}
677 
678 	public void loadingDone() {
679 		_infoPanel.nowLoading(false);
680 	}
681 }