001: /*******************************************************************************
002: * Copyright (c) 2000, 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: * Stefan Xenos, IBM; Chris Torrence, ITT Visual Information Solutions - bug 51580
011: *******************************************************************************/package org.eclipse.ui.internal;
012:
013: import org.eclipse.core.runtime.ListenerList;
014: import org.eclipse.jface.action.MenuManager;
015: import org.eclipse.jface.util.IPropertyChangeListener;
016: import org.eclipse.jface.util.PropertyChangeEvent;
017: import org.eclipse.swt.SWT;
018: import org.eclipse.swt.events.FocusAdapter;
019: import org.eclipse.swt.events.FocusEvent;
020: import org.eclipse.swt.events.KeyAdapter;
021: import org.eclipse.swt.events.KeyEvent;
022: import org.eclipse.swt.events.KeyListener;
023: import org.eclipse.swt.events.SelectionAdapter;
024: import org.eclipse.swt.events.SelectionEvent;
025: import org.eclipse.swt.events.TraverseEvent;
026: import org.eclipse.swt.events.TraverseListener;
027: import org.eclipse.swt.graphics.Point;
028: import org.eclipse.swt.graphics.Rectangle;
029: import org.eclipse.swt.layout.FillLayout;
030: import org.eclipse.swt.widgets.Composite;
031: import org.eclipse.swt.widgets.Control;
032: import org.eclipse.swt.widgets.Event;
033: import org.eclipse.swt.widgets.Listener;
034: import org.eclipse.swt.widgets.Menu;
035: import org.eclipse.swt.widgets.MenuItem;
036: import org.eclipse.swt.widgets.Sash;
037: import org.eclipse.ui.IPropertyListener;
038: import org.eclipse.ui.IWorkbenchPart;
039: import org.eclipse.ui.IWorkbenchPartReference;
040: import org.eclipse.ui.internal.dnd.SwtUtil;
041: import org.eclipse.ui.part.MultiEditor;
042: import org.eclipse.ui.presentations.IPresentablePart;
043:
044: /**
045: * Provides the common behavior for both views
046: * and editor panes.
047: *
048: * TODO: Delete ViewPane and EditorPane, and make this class non-abstract.
049: *
050: * TODO: Stop subclassing LayoutPart. This class cannot be interchanged with other LayoutParts.
051: * Pointers that refer to PartPane instances should do so directly rather than referring to
052: * LayoutPart and downcasting. The getPresentablePart() method only applies to PartPanes, and
053: * should be removed from LayoutPart.
054: */
055: public abstract class PartPane extends LayoutPart implements
056: IPropertyListener, Listener, IPropertyChangeListener {
057:
058: public static final String PROP_ZOOMED = "zoomed"; //$NON-NLS-1$
059:
060: private boolean isZoomed = false;
061:
062: private MenuManager paneMenuManager;
063: private ListenerList listeners = new ListenerList();
064: private ListenerList partListeners = new ListenerList();
065:
066: protected IWorkbenchPartReference partReference;
067:
068: protected WorkbenchPage page;
069:
070: protected Composite control;
071:
072: private boolean inLayout = true;
073:
074: private TraverseListener traverseListener = new TraverseListener() {
075: /* (non-Javadoc)
076: * @see org.eclipse.swt.events.TraverseListener#keyTraversed(org.eclipse.swt.events.TraverseEvent)
077: */
078: public void keyTraversed(TraverseEvent e) {
079: // Hack: Currently, SWT sets focus whenever we call Control.traverse. This doesn't
080: // cause too much of a problem for ctrl-pgup and ctrl-pgdn, but it is seriously unexpected
081: // for other traversal events. When (and if) it becomes possible to call traverse() without
082: // forcing a focus change, this if statement should be removed and ALL events should be
083: // forwarded to the container.
084: if (e.detail == SWT.TRAVERSE_PAGE_NEXT
085: || e.detail == SWT.TRAVERSE_PAGE_PREVIOUS) {
086: ILayoutContainer container = getContainer();
087: if (container != null
088: && container instanceof LayoutPart) {
089: LayoutPart parent = (LayoutPart) container;
090: Control parentControl = parent.getControl();
091: if (parentControl != null
092: && !parentControl.isDisposed()) {
093: e.doit = parentControl.traverse(e.detail);
094: if (e.doit) {
095: e.detail = SWT.TRAVERSE_NONE;
096: }
097: }
098: }
099: }
100: }
101:
102: };
103:
104: private boolean busy;
105:
106: public static class Sashes {
107: public Sash left;
108:
109: public Sash right;
110:
111: public Sash top;
112:
113: public Sash bottom;
114: }
115:
116: /**
117: * Construct a pane for a part.
118: */
119: public PartPane(IWorkbenchPartReference partReference,
120: WorkbenchPage workbenchPage) {
121: super (partReference.getId());
122: this .partReference = partReference;
123: this .page = workbenchPage;
124: }
125:
126: public void addSizeMenuItem(Menu menu, int index) {
127: //Add size menu
128: MenuItem item = new MenuItem(menu, SWT.CASCADE, index);
129: item.setText(WorkbenchMessages.PartPane_size);
130: Menu sizeMenu = new Menu(menu);
131: item.setMenu(sizeMenu);
132: addSizeItems(sizeMenu);
133: }
134:
135: /**
136: *
137: */
138: public void createControl(Composite parent) {
139: if (getControl() != null) {
140: return;
141: }
142:
143: partReference.addPropertyListener(this );
144: partReference.addPartPropertyListener(this );
145: // Create view form.
146: control = new Composite(parent, SWT.NONE);
147: control.setLayout(new FillLayout());
148: // the part should never be visible by default. It will be made visible
149: // by activation. This allows us to have views appear in tabs without
150: // becoming active by default.
151: control.setVisible(false);
152: control.moveAbove(null);
153: // Create a title bar.
154: createTitleBar();
155:
156: // When the pane or any child gains focus, notify the workbench.
157: control.addListener(SWT.Activate, this );
158:
159: control.addTraverseListener(traverseListener);
160: }
161:
162: /**
163: * Create a title bar for the pane if required.
164: */
165: protected abstract void createTitleBar();
166:
167: /**
168: * @private
169: */
170: public void dispose() {
171: super .dispose();
172:
173: if ((control != null) && (!control.isDisposed())) {
174: control.removeListener(SWT.Activate, this );
175: control.removeTraverseListener(traverseListener);
176: control.dispose();
177: control = null;
178: }
179: if ((paneMenuManager != null)) {
180: paneMenuManager.dispose();
181: paneMenuManager = null;
182: }
183:
184: partReference.removePropertyListener(this );
185: partReference.removePartPropertyListener(this );
186: }
187:
188: /**
189: * User has requested to close the pane.
190: * Take appropriate action depending on type.
191: */
192: abstract public void doHide();
193:
194: /**
195: * Zooms in on the part contained in this pane.
196: */
197: protected void doZoom() {
198: if (isDocked()) {
199: page.toggleZoom(partReference);
200: }
201: }
202:
203: /**
204: * Gets the presentation bounds.
205: */
206: public Rectangle getBounds() {
207: return getControl().getBounds();
208: }
209:
210: /**
211: * Get the control.
212: */
213: public Control getControl() {
214: return control;
215: }
216:
217: /**
218: * Answer the part child.
219: */
220: public IWorkbenchPartReference getPartReference() {
221: return partReference;
222: }
223:
224: /**
225: * @see Listener
226: */
227: public void handleEvent(Event event) {
228: if (event.type == SWT.Activate) {
229: if (inLayout) {
230: requestActivation();
231: }
232: }
233: }
234:
235: /**
236: * Return whether the pane is zoomed or not
237: */
238: public boolean isZoomed() {
239: return isZoomed;
240: }
241:
242: /**
243: * Move the control over another one.
244: */
245: public void moveAbove(Control refControl) {
246: if (getControl() != null) {
247: getControl().moveAbove(refControl);
248: }
249: }
250:
251: /**
252: * Notify the workbook page that the part pane has
253: * been activated by the user.
254: */
255: public void requestActivation() {
256: IWorkbenchPart part = partReference.getPart(true);
257: // Cannot activate the outer bit of a MultiEditor. In previous versions of the
258: // workbench, MultiEditors had their own implementation of EditorPane for the purpose
259: // of overriding requestActivation with a NOP... however, keeping the old pattern would
260: // mean it is necessary to eagerly activate an editor's plugin in order to determine
261: // what type of pane to create.
262: if (part instanceof MultiEditor) {
263: return;
264: }
265:
266: this .page.requestActivation(part);
267: }
268:
269: /**
270: * Sets the parent for this part.
271: */
272: public void setContainer(ILayoutContainer container) {
273:
274: if (container instanceof LayoutPart) {
275: LayoutPart part = (LayoutPart) container;
276:
277: Control containerControl = part.getControl();
278:
279: if (containerControl != null) {
280: Control control = getControl();
281: Composite newParent = containerControl.getParent();
282: if (control != null && newParent != control.getParent()) {
283: reparent(newParent);
284: }
285: }
286: }
287: super .setContainer(container);
288: }
289:
290: /**
291: * Shows the receiver if <code>visible</code> is true otherwise hide it.
292: */
293: public void setVisible(boolean makeVisible) {
294: // Avoid redundant visibility changes
295: if (makeVisible == getVisible()) {
296: return;
297: }
298:
299: if (makeVisible) {
300: partReference.getPart(true);
301: }
302:
303: super .setVisible(makeVisible);
304:
305: ((WorkbenchPartReference) partReference).fireVisibilityChange();
306: }
307:
308: /**
309: * Sets focus to this part.
310: */
311: public void setFocus() {
312: requestActivation();
313:
314: IWorkbenchPart part = partReference.getPart(true);
315: if (part != null) {
316: Control control = getControl();
317: if (!SwtUtil.isFocusAncestor(control)) {
318: // First try to call part.setFocus
319: part.setFocus();
320: }
321: }
322: }
323:
324: /**
325: * Sets the workbench page of the view.
326: */
327: public void setWorkbenchPage(WorkbenchPage workbenchPage) {
328: this .page = workbenchPage;
329: }
330:
331: /**
332: * Set whether the pane is zoomed or not
333: */
334: public void setZoomed(boolean isZoomed) {
335: if (this .isZoomed == isZoomed) {
336: return; // do nothing if we're already in the right state.
337: }
338:
339: super .setZoomed(isZoomed);
340:
341: this .isZoomed = isZoomed;
342:
343: ((WorkbenchPartReference) partReference).fireZoomChange();
344: }
345:
346: /**
347: * Informs the pane that it's window shell has
348: * been activated.
349: */
350: /* package */abstract void shellActivated();
351:
352: /**
353: * Informs the pane that it's window shell has
354: * been deactivated.
355: */
356: /* package */abstract void shellDeactivated();
357:
358: /**
359: * Indicate focus in part.
360: */
361: public abstract void showFocus(boolean inFocus);
362:
363: /**
364: * @see IPartDropTarget::targetPartFor
365: */
366: public LayoutPart targetPartFor(LayoutPart dragSource) {
367: return this ;
368: }
369:
370: /**
371: * Returns the PartStack that contains this PartPane, or null if none.
372: *
373: * @return
374: */
375: public PartStack getStack() {
376: ILayoutContainer container = getContainer();
377: if (container instanceof PartStack) {
378: return (PartStack) container;
379: }
380:
381: return null;
382: }
383:
384: /**
385: * Show a title label menu for this pane.
386: */
387: public void showPaneMenu() {
388: PartStack folder = getStack();
389:
390: if (folder != null) {
391: folder.showPaneMenu();
392: }
393: }
394:
395: /**
396: * Show the context menu for this part.
397: */
398: public void showSystemMenu() {
399: PartStack folder = getStack();
400:
401: if (folder != null) {
402: folder.showSystemMenu();
403: }
404: }
405:
406: /**
407: * Finds and return the sashes around this part.
408: */
409: protected Sashes findSashes() {
410: Sashes result = new Sashes();
411:
412: ILayoutContainer container = getContainer();
413:
414: if (container == null) {
415: return result;
416: }
417:
418: container.findSashes(this , result);
419: return result;
420: }
421:
422: /**
423: * Enable the user to resize this part using
424: * the keyboard to move the specified sash
425: */
426: protected void moveSash(final Sash sash) {
427: moveSash(sash, this );
428: }
429:
430: public static void moveSash(final Sash sash,
431: final LayoutPart toGetFocusWhenDone) {
432: final KeyListener listener = new KeyAdapter() {
433: public void keyPressed(KeyEvent e) {
434: if (e.character == SWT.ESC || e.character == '\r') {
435: if (toGetFocusWhenDone != null) {
436: toGetFocusWhenDone.setFocus();
437: }
438: }
439: }
440: };
441: sash.addFocusListener(new FocusAdapter() {
442: public void focusGained(FocusEvent e) {
443: sash.setBackground(sash.getDisplay().getSystemColor(
444: SWT.COLOR_LIST_SELECTION));
445: sash.addKeyListener(listener);
446: }
447:
448: public void focusLost(FocusEvent e) {
449: sash.setBackground(null);
450: sash.removeKeyListener(listener);
451: }
452: });
453: sash.setFocus();
454:
455: }
456:
457: /**
458: * Add a menu item to the Size Menu
459: */
460: protected void addSizeItem(Menu sizeMenu, String labelMessage,
461: final Sash sash) {
462: MenuItem item = new MenuItem(sizeMenu, SWT.NONE);
463: item.setText(labelMessage);
464: item.addSelectionListener(new SelectionAdapter() {
465: public void widgetSelected(SelectionEvent e) {
466: moveSash(sash);
467: }
468: });
469: item.setEnabled(!isZoomed() && sash != null);
470: }
471:
472: /**
473: * Returns the workbench page of this pane.
474: */
475: public WorkbenchPage getPage() {
476: return page;
477: }
478:
479: /**
480: * Add the Left,Right,Up,Botton menu items to the Size menu.
481: */
482: protected void addSizeItems(Menu sizeMenu) {
483: Sashes sashes = findSashes();
484: addSizeItem(sizeMenu, WorkbenchMessages.PartPane_sizeLeft,
485: sashes.left);
486: addSizeItem(sizeMenu, WorkbenchMessages.PartPane_sizeRight,
487: sashes.right);
488: addSizeItem(sizeMenu, WorkbenchMessages.PartPane_sizeTop,
489: sashes.top);
490: addSizeItem(sizeMenu, WorkbenchMessages.PartPane_sizeBottom,
491: sashes.bottom);
492: }
493:
494: /**
495: * Pin this part.
496: */
497: protected void doDock() {
498: // do nothing
499: }
500:
501: /**
502: * Set the busy state of the pane.
503: */
504: public void setBusy(boolean isBusy) {
505: if (isBusy != busy) {
506: busy = isBusy;
507: firePropertyChange(IPresentablePart.PROP_BUSY);
508: }
509: }
510:
511: /**
512: * Show a highlight for the receiver if it is
513: * not currently the part in the front of its
514: * presentation.
515: *
516: */
517: public void showHighlight() {
518: //No nothing by default
519: }
520:
521: /**
522: * @return
523: */
524: public abstract Control getToolBar();
525:
526: /**
527: * @return
528: */
529: public boolean hasViewMenu() {
530: return false;
531: }
532:
533: /**
534: * @param location
535: */
536: public void showViewMenu(Point location) {
537:
538: }
539:
540: public boolean isBusy() {
541: return busy;
542: }
543:
544: /**
545: * Writes a description of the layout to the given string buffer.
546: * This is used for drag-drop test suites to determine if two layouts are the
547: * same. Like a hash code, the description should compare as equal iff the
548: * layouts are the same. However, it should be user-readable in order to
549: * help debug failed tests. Although these are english readable strings,
550: * they do not need to be translated.
551: *
552: * @param buf
553: */
554: public void describeLayout(StringBuffer buf) {
555:
556: IWorkbenchPartReference part = getPartReference();
557:
558: if (part != null) {
559: buf.append(part.getPartName());
560: return;
561: }
562: }
563:
564: /**
565: * @return
566: * @since 3.1
567: */
568: public abstract boolean isCloseable();
569:
570: public void setInLayout(boolean inLayout) {
571: this .inLayout = inLayout;
572: }
573:
574: public boolean getInLayout() {
575: return inLayout;
576: }
577:
578: public boolean allowsAutoFocus() {
579: if (!inLayout) {
580: return false;
581: }
582:
583: return super .allowsAutoFocus();
584: }
585:
586: /**
587: * Clears all contribution items from the contribution managers (this is done separately
588: * from dispose() since it is done after the part is disposed). This is a bit of a hack.
589: * Really, the contribution managers should be part of the site, not the PartPane. If these
590: * were moved elsewhere, then disposal of the PartPane would be atomic and this method could
591: * be removed.
592: */
593: public void removeContributions() {
594:
595: }
596:
597: public void addPropertyListener(IPropertyListener listener) {
598: listeners.add(listener);
599: }
600:
601: public void removePropertyListener(IPropertyListener listener) {
602: listeners.remove(listener);
603: }
604:
605: public void firePropertyChange(int propertyId) {
606: Object listeners[] = this .listeners.getListeners();
607: for (int i = 0; i < listeners.length; i++) {
608: ((IPropertyListener) listeners[i]).propertyChanged(this ,
609: propertyId);
610: }
611: }
612:
613: public void propertyChanged(Object source, int propId) {
614: firePropertyChange(propId);
615: }
616:
617: public void addPartPropertyListener(IPropertyChangeListener listener) {
618: partListeners.add(listener);
619: }
620:
621: public void removePartPropertyListener(
622: IPropertyChangeListener listener) {
623: partListeners.remove(listener);
624: }
625:
626: public void firePartPropertyChange(PropertyChangeEvent event) {
627: Object[] l = partListeners.getListeners();
628: for (int i = 0; i < l.length; i++) {
629: ((IPropertyChangeListener) l[i]).propertyChange(event);
630: }
631: }
632:
633: /* (non-Javadoc)
634: * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
635: */
636: public void propertyChange(PropertyChangeEvent event) {
637: firePartPropertyChange(event);
638: }
639:
640: /* (non-Javadoc)
641: * @see org.eclipse.ui.internal.LayoutPart#computePreferredSize(boolean, int, int, int)
642: */
643: public int computePreferredSize(boolean width,
644: int availableParallel, int availablePerpendicular,
645: int preferredParallel) {
646:
647: return ((WorkbenchPartReference) partReference)
648: .computePreferredSize(width, availableParallel,
649: availablePerpendicular, preferredParallel);
650: }
651:
652: /* (non-Javadoc)
653: * @see org.eclipse.ui.internal.LayoutPart#getSizeFlags(boolean)
654: */
655: public int getSizeFlags(boolean horizontal) {
656: return ((WorkbenchPartReference) partReference)
657: .getSizeFlags(horizontal);
658: }
659:
660: }
|