001: /*******************************************************************************
002: * Copyright (c) 2004, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: * Chris Gross chris.gross@us.ibm.com Bug 107443
011: *******************************************************************************/package org.eclipse.ui.internal;
012:
013: import org.eclipse.jface.action.ContributionItem;
014: import org.eclipse.jface.action.IMenuManager;
015: import org.eclipse.jface.util.Geometry;
016: import org.eclipse.swt.SWT;
017: import org.eclipse.swt.events.FocusAdapter;
018: import org.eclipse.swt.events.FocusEvent;
019: import org.eclipse.swt.events.KeyAdapter;
020: import org.eclipse.swt.events.KeyEvent;
021: import org.eclipse.swt.events.KeyListener;
022: import org.eclipse.swt.events.SelectionAdapter;
023: import org.eclipse.swt.events.SelectionEvent;
024: import org.eclipse.swt.graphics.Point;
025: import org.eclipse.swt.graphics.Rectangle;
026: import org.eclipse.swt.widgets.Composite;
027: import org.eclipse.swt.widgets.Control;
028: import org.eclipse.swt.widgets.Display;
029: import org.eclipse.swt.widgets.Event;
030: import org.eclipse.swt.widgets.Listener;
031: import org.eclipse.swt.widgets.Sash;
032: import org.eclipse.swt.widgets.ToolBar;
033: import org.eclipse.ui.ISizeProvider;
034: import org.eclipse.ui.IViewReference;
035: import org.eclipse.ui.IWorkbenchPartReference;
036: import org.eclipse.ui.internal.dnd.DragUtil;
037: import org.eclipse.ui.internal.presentations.PresentablePart;
038: import org.eclipse.ui.internal.presentations.SystemMenuFastView;
039: import org.eclipse.ui.internal.presentations.SystemMenuFastViewOrientation;
040: import org.eclipse.ui.internal.presentations.SystemMenuSizeFastView;
041: import org.eclipse.ui.internal.presentations.UpdatingActionContributionItem;
042: import org.eclipse.ui.presentations.AbstractPresentationFactory;
043: import org.eclipse.ui.presentations.IPresentablePart;
044: import org.eclipse.ui.presentations.IStackPresentationSite;
045: import org.eclipse.ui.presentations.StackPresentation;
046:
047: /**
048: * Handles the presentation of an active fastview. A fast view pane docks to one side of a
049: * parent composite, and is capable of displaying a single view. The view may be resized.
050: * Displaying a new view will hide any view currently being displayed in the pane.
051: *
052: * Currently, the fast view pane does not own or contain the view. It only controls the view's
053: * position and visibility.
054: *
055: * @see org.eclipse.ui.internal.FastViewBar
056: */
057: public class FastViewPane {
058: private int side = SWT.LEFT;
059:
060: private PresentablePart currentPane;
061:
062: private Composite clientComposite;
063:
064: private static final int SASH_SIZE = 3;
065:
066: private int minSize = 10;
067:
068: private int size;
069:
070: private Sash sash;
071:
072: // Traverse listener -- listens to ESC and closes the active fastview
073: private Listener escapeListener = new Listener() {
074: public void handleEvent(Event event) {
075: if (event.character == SWT.ESC) {
076: if (currentPane != null) {
077: currentPane.getPane().getPage().hideFastView();
078: }
079: }
080: }
081: };
082:
083: private DefaultStackPresentationSite site = new DefaultStackPresentationSite() {
084: /* (non-Javadoc)
085: * @see org.eclipse.ui.internal.skins.IPresentationSite#setState(int)
086: */
087: public void setState(int newState) {
088: super .setState(newState);
089: PartPane pane = currentPane.getPane();
090: switch (newState) {
091: case IStackPresentationSite.STATE_MINIMIZED:
092:
093: pane.getPage().hideFastView();
094: break;
095: case IStackPresentationSite.STATE_MAXIMIZED:
096: pane.setZoomed(true);
097: sash.setVisible(false);
098: this .getPresentation().setBounds(getBounds());
099: break;
100: case IStackPresentationSite.STATE_RESTORED:
101: pane.setZoomed(false);
102: sash.setVisible(true);
103: this .getPresentation().setBounds(getBounds());
104: break;
105: default:
106: }
107: }
108:
109: public void flushLayout() {
110:
111: }
112:
113: public void close(IPresentablePart part) {
114: if (!isCloseable(part)) {
115: return;
116: }
117: IWorkbenchPartReference ref = currentPane.getPane()
118: .getPartReference();
119: if (ref instanceof IViewReference) {
120: currentPane.getPane().getPage().hideView(
121: (IViewReference) ref);
122: }
123: }
124:
125: public void close(IPresentablePart[] parts) {
126: for (int idx = 0; idx < parts.length; idx++) {
127: close(parts[idx]);
128: }
129: }
130:
131: /* (non-Javadoc)
132: * @see org.eclipse.ui.internal.skins.IPresentationSite#dragStart(org.eclipse.ui.internal.skins.IPresentablePart, boolean)
133: */
134: public void dragStart(IPresentablePart beingDragged,
135: Point initialPosition, boolean keyboard) {
136: dragStart(initialPosition, keyboard);
137: }
138:
139: /* (non-Javadoc)
140: * @see org.eclipse.ui.internal.skins.IPresentationSite#dragStart(boolean)
141: */
142: public void dragStart(Point initialPosition, boolean keyboard) {
143: if (!isPartMoveable()) {
144: return;
145: }
146:
147: PartPane pane = currentPane.getPane();
148:
149: Control control = this .getPresentation().getControl();
150:
151: Rectangle bounds = Geometry.toDisplay(clientComposite,
152: control.getBounds());
153:
154: WorkbenchPage page = pane.getPage();
155:
156: page.hideFastView();
157: if (page.isZoomed()) {
158: page.zoomOut();
159: }
160:
161: DragUtil.performDrag(pane, bounds, initialPosition,
162: !keyboard);
163: }
164:
165: public IPresentablePart getSelectedPart() {
166: return currentPane;
167: }
168:
169: public void addSystemActions(IMenuManager menuManager) {
170: ViewStackTrimToolBar vstt = getTrim();
171:
172: appendToGroupIfPossible(
173: menuManager,
174: "misc", new SystemMenuFastViewOrientation(currentPane.getPane(), vstt)); //$NON-NLS-1$
175:
176: // Only add the 'Fast View' menu entry if the
177: // pane is showing a 'legacy' fast view
178: if (vstt == null) {
179: appendToGroupIfPossible(
180: menuManager,
181: "misc", new UpdatingActionContributionItem(fastViewAction)); //$NON-NLS-1$
182: }
183:
184: appendToGroupIfPossible(
185: menuManager,
186: "size", new SystemMenuSizeFastView(FastViewPane.this )); //$NON-NLS-1$
187: }
188:
189: /**
190: * Returns the ViewStackTrimToolBar which has caused the FV to be shown. If
191: * <code>null</code> then we can assume it was the legacy FastViewBar.
192: */
193: private ViewStackTrimToolBar getTrim() {
194: if (currentPane == null || currentPane.getPane() == null)
195: return null;
196:
197: ViewStackTrimToolBar trim = null;
198:
199: PartPane pane = currentPane.getPane();
200: if (pane instanceof ViewPane) {
201: ViewPane vp = (ViewPane) pane;
202: Perspective persp = vp.getPage().getActivePerspective();
203: IViewReference viewRef = vp.getViewReference();
204: FastViewManager fvm = persp.getFastViewManager();
205:
206: String trimId = null;
207: if (fvm != null)
208: trimId = fvm.getIdForRef(viewRef);
209:
210: if (trimId != null
211: && !trimId.equals(FastViewBar.FASTVIEWBAR_ID))
212: trim = fvm.getViewStackTrimToolbar(trimId);
213: }
214:
215: return trim;
216: }
217:
218: public boolean isPartMoveable(IPresentablePart toMove) {
219: return isPartMoveable();
220: }
221:
222: public boolean isStackMoveable() {
223: // a fast view stack is moveable iff its part is moveable
224: return isPartMoveable();
225: }
226:
227: private boolean isPartMoveable() {
228: if (currentPane == null) {
229: return false;
230: }
231: Perspective perspective = currentPane.getPane().getPage()
232: .getActivePerspective();
233: if (perspective == null) {
234: // Shouldn't happen -- can't have a FastViewPane without a perspective
235: return false;
236: }
237:
238: IWorkbenchPartReference ref = currentPane.getPane()
239: .getPartReference();
240:
241: if (ref instanceof IViewReference) {
242: return perspective.isMoveable((IViewReference) ref);
243: }
244: return true;
245: }
246:
247: public boolean supportsState(int newState) {
248: if (currentPane == null) {
249: return false;
250: }
251: if (currentPane.getPane().getPage().isFixedLayout()) {
252: return false;
253: }
254: return true;
255: }
256:
257: public IPresentablePart[] getPartList() {
258: return new IPresentablePart[] { getSelectedPart() };
259: }
260:
261: /* (non-Javadoc)
262: * @see org.eclipse.ui.presentations.IStackPresentationSite#getProperty(java.lang.String)
263: */
264: public String getProperty(String id) {
265: // fast views stacks do not get arbitrary user properties.
266: return null;
267: }
268: };
269:
270: private SystemMenuFastView fastViewAction = new SystemMenuFastView(
271: site);
272:
273: private static void appendToGroupIfPossible(IMenuManager m,
274: String groupId, ContributionItem item) {
275: try {
276: m.appendToGroup(groupId, item);
277: } catch (IllegalArgumentException e) {
278: m.add(item);
279: }
280: }
281:
282: private Listener mouseDownListener = new Listener() {
283: public void handleEvent(Event event) {
284: if (event.widget instanceof Control) {
285: Control control = (Control) event.widget;
286:
287: if (control.getShell() != clientComposite.getShell()) {
288: return;
289: }
290:
291: if (event.widget instanceof ToolBar) {
292: // Ignore mouse down on actual tool bar buttons
293: Point pt = new Point(event.x, event.y);
294: ToolBar toolBar = (ToolBar) event.widget;
295: if (toolBar.getItem(pt) != null) {
296: return;
297: }
298: }
299:
300: Point loc = DragUtil.getEventLoc(event);
301:
302: // 'Extrude' the rect -before- converting to Display coords
303: // to avoid Right-to-Left issues
304: Rectangle bounds = clientComposite.getBounds();
305: if (site.getState() != IStackPresentationSite.STATE_MAXIMIZED) {
306: bounds = Geometry.getExtrudedEdge(bounds, size
307: + SASH_SIZE, side);
308: }
309:
310: // Now map the bounds to display coords
311: bounds = clientComposite.getDisplay().map(
312: clientComposite, null, bounds);
313:
314: if (!bounds.contains(loc)) {
315: site
316: .setState(IStackPresentationSite.STATE_MINIMIZED);
317: }
318: }
319: }
320: };
321:
322: public void moveSash() {
323: final KeyListener listener = new KeyAdapter() {
324: public void keyPressed(KeyEvent e) {
325: if (e.character == SWT.ESC || e.character == '\r') {
326: currentPane.setFocus();
327: }
328: }
329: };
330: sash.addFocusListener(new FocusAdapter() {
331: public void focusGained(FocusEvent e) {
332: sash.setBackground(sash.getDisplay().getSystemColor(
333: SWT.COLOR_LIST_SELECTION));
334: sash.addKeyListener(listener);
335: }
336:
337: public void focusLost(FocusEvent e) {
338: sash.setBackground(null);
339: sash.removeKeyListener(listener);
340: }
341: });
342: sash.setFocus();
343: }
344:
345: private Listener resizeListener = new Listener() {
346: public void handleEvent(Event event) {
347: if (event.type == SWT.Resize && currentPane != null) {
348: setSize(size);
349: }
350: }
351: };
352:
353: private SelectionAdapter selectionListener = new SelectionAdapter() {
354: public void widgetSelected(SelectionEvent e) {
355:
356: if (currentPane != null) {
357: Rectangle bounds = clientComposite.getClientArea();
358: Point location = new Point(e.x, e.y);
359: int distanceFromEdge = Geometry.getDistanceFromEdge(
360: bounds, location, side);
361:
362: if (!(side == SWT.TOP || side == SWT.LEFT)) {
363: distanceFromEdge -= SASH_SIZE;
364: }
365:
366: setSize(distanceFromEdge);
367:
368: if (e.detail != SWT.DRAG) {
369: updateFastViewSashBounds();
370: // getPresentation().getControl().moveAbove(null);
371: // currentPane.moveAbove(null);
372: // sash.moveAbove(null);
373: //currentPane.getControl().redraw();
374: sash.redraw();
375: }
376: }
377: }
378: };
379:
380: private void setSize(int size) {
381:
382: if (size < minSize) {
383: size = minSize;
384: }
385: this .size = size;
386:
387: StackPresentation presentation = getPresentation();
388: if (presentation == null
389: || presentation.getControl().isDisposed()) {
390: return;
391: }
392: getPresentation().setBounds(getBounds());
393: updateFastViewSashBounds();
394: }
395:
396: /**
397: * Returns the current fastview size ratio. Returns 0.0 if there is no fastview visible.
398: */
399: public float getCurrentRatio() {
400: if (currentPane == null) {
401: return 0.0f;
402: }
403:
404: boolean isVertical = !Geometry.isHorizontal(side);
405: Rectangle clientArea = clientComposite.getClientArea();
406:
407: int clientSize = Geometry.getDimension(clientArea, isVertical);
408:
409: return (float) size / (float) clientSize;
410: }
411:
412: private Rectangle getClientArea() {
413: return clientComposite.getClientArea();
414: }
415:
416: private Rectangle getBounds() {
417: Rectangle bounds = getClientArea();
418:
419: if (site.getState() == IStackPresentationSite.STATE_MAXIMIZED) {
420: return bounds;
421: }
422:
423: boolean horizontal = Geometry.isHorizontal(side);
424:
425: int available = Geometry.getDimension(bounds, !horizontal);
426:
427: return Geometry.getExtrudedEdge(bounds, Math.min(
428: FastViewPane.this .size, available), side);
429: }
430:
431: /**
432: * Displays the given view as a fastview. The view will be docked to the edge of the
433: * given composite until it is subsequently hidden by a call to hideFastView.
434: *
435: * @param newClientComposite
436: * @param pane
437: * @param newSide
438: */
439: public void showView(Composite newClientComposite, ViewPane pane,
440: int newSide, float sizeRatio) {
441: side = newSide;
442:
443: if (currentPane != null) {
444: hideView();
445: }
446:
447: currentPane = new PresentablePart(pane, newClientComposite);
448:
449: fastViewAction.setPane(currentPane);
450: clientComposite = newClientComposite;
451:
452: clientComposite.addListener(SWT.Resize, resizeListener);
453:
454: // Create the control first
455: Control ctrl = pane.getControl();
456: if (ctrl == null) {
457: pane.createControl(clientComposite);
458: ctrl = pane.getControl();
459: }
460:
461: ctrl.addListener(SWT.Traverse, escapeListener);
462:
463: // Temporarily use the same appearance as docked views .. eventually, fastviews will
464: // be independently pluggable.
465: AbstractPresentationFactory factory = ((WorkbenchWindow) pane
466: .getWorkbenchWindow()).getWindowConfigurer()
467: .getPresentationFactory();
468: StackPresentation presentation = factory
469: .createViewPresentation(newClientComposite, site);
470:
471: site.setPresentation(presentation);
472: site
473: .setPresentationState(IStackPresentationSite.STATE_RESTORED);
474: presentation.addPart(currentPane, null);
475: presentation.selectPart(currentPane);
476: presentation.setActive(StackPresentation.AS_ACTIVE_FOCUS);
477: presentation.setVisible(true);
478:
479: boolean horizontalResize = Geometry.isHorizontal(side);
480:
481: minSize = presentation.computePreferredSize(horizontalResize,
482: ISizeProvider.INFINITE, Geometry.getDimension(
483: getClientArea(), horizontalResize), 0);
484:
485: // Show pane fast.
486: ctrl.setEnabled(true); // Add focus support.
487: Composite parent = ctrl.getParent();
488:
489: boolean horizontal = Geometry.isHorizontal(side);
490: sash = new Sash(parent, Geometry
491: .getSwtHorizontalOrVerticalConstant(horizontal));
492:
493: sash.addSelectionListener(selectionListener);
494:
495: Rectangle clientArea = newClientComposite.getClientArea();
496:
497: getPresentation().getControl().moveAbove(null);
498: currentPane.getPane().moveAbove(null);
499: sash.moveAbove(null);
500:
501: setSize((int) (Geometry.getDimension(clientArea, !horizontal) * sizeRatio));
502:
503: Display display = sash.getDisplay();
504:
505: display.addFilter(SWT.MouseDown, mouseDownListener);
506:
507: pane.setFocus();
508: }
509:
510: /**
511: * Updates the position of the resize sash.
512: */
513: private void updateFastViewSashBounds() {
514: Rectangle bounds = getBounds();
515:
516: int oppositeSide = Geometry.getOppositeSide(side);
517: Rectangle newBounds = Geometry.getExtrudedEdge(bounds,
518: -SASH_SIZE, oppositeSide);
519:
520: Rectangle oldBounds = sash.getBounds();
521:
522: if (!newBounds.equals(oldBounds)) {
523: sash.setBounds(newBounds);
524: }
525: }
526:
527: /**
528: * Disposes of any active widgetry being used for the fast view pane. Does not dispose
529: * of the view itself.
530: */
531: public void dispose() {
532: hideView();
533: }
534:
535: private StackPresentation getPresentation() {
536: return site.getPresentation();
537: }
538:
539: /**
540: * Hides the sash for the fastview if it is currently visible. This method may not be
541: * required anymore, and might be removed from the public interface.
542: */
543: public void hideFastViewSash() {
544: if (sash != null) {
545: sash.setVisible(false);
546: }
547: }
548:
549: /**
550: * Hides the currently visible fastview.
551: */
552: public void hideView() {
553:
554: if (clientComposite != null) {
555: Display display = clientComposite.getDisplay();
556:
557: display.removeFilter(SWT.MouseDown, mouseDownListener);
558: }
559:
560: if (currentPane == null) {
561: return;
562: }
563:
564: fastViewAction.setPane(null);
565:
566: //unzoom before hiding
567: currentPane.getPane().setZoomed(false);
568:
569: if (sash != null) {
570: sash.dispose();
571: sash = null;
572: }
573:
574: clientComposite.removeListener(SWT.Resize, resizeListener);
575:
576: // Get pane.
577: // Hide the right side sash first
578: //hideFastViewSash();
579: Control ctrl = currentPane.getControl();
580:
581: ctrl.removeListener(SWT.Traverse, escapeListener);
582:
583: // Hide it completely.
584: getPresentation().setVisible(false);
585: site.dispose();
586: //currentPane.setFastViewSash(null);
587: ctrl.setEnabled(false); // Remove focus support.
588:
589: currentPane.dispose();
590: currentPane = null;
591: }
592:
593: /**
594: * @return Returns the currently visible fastview or null if none
595: */
596: public ViewPane getCurrentPane() {
597: if (currentPane != null
598: && currentPane.getPane() instanceof ViewPane) {
599: return (ViewPane) currentPane.getPane();
600: }
601:
602: return null;
603: }
604:
605: public void setState(int newState) {
606: site.setState(newState);
607: }
608:
609: public int getState() {
610: return site.getState();
611: }
612:
613: /**
614: *
615: */
616: public void showSystemMenu() {
617: getPresentation().showSystemMenu();
618: }
619:
620: /**
621: *
622: */
623: public void showPaneMenu() {
624: getPresentation().showPaneMenu();
625: }
626: }
|