Source code: org/eclipse/ui/internal/PartStack.java
1 /*******************************************************************************
2 * Copyright (c) 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.ui.internal;
12
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Iterator;
16 import java.util.List;
17
18 import org.eclipse.core.runtime.IStatus;
19 import org.eclipse.core.runtime.Status;
20 import org.eclipse.jface.action.ContributionItem;
21 import org.eclipse.jface.action.IMenuManager;
22 import org.eclipse.jface.util.Assert;
23 import org.eclipse.jface.util.Geometry;
24 import org.eclipse.jface.window.Window;
25 import org.eclipse.swt.graphics.Cursor;
26 import org.eclipse.swt.graphics.Point;
27 import org.eclipse.swt.graphics.Rectangle;
28 import org.eclipse.swt.widgets.Composite;
29 import org.eclipse.swt.widgets.Control;
30 import org.eclipse.swt.widgets.Display;
31 import org.eclipse.ui.IMemento;
32 import org.eclipse.ui.IWorkbenchPartReference;
33 import org.eclipse.ui.PlatformUI;
34 import org.eclipse.ui.XMLMemento;
35 import org.eclipse.ui.internal.dnd.DragUtil;
36 import org.eclipse.ui.internal.dnd.IDragOverListener;
37 import org.eclipse.ui.internal.dnd.IDropTarget;
38 import org.eclipse.ui.internal.dnd.SwtUtil;
39 import org.eclipse.ui.internal.presentations.PresentationFactoryUtil;
40 import org.eclipse.ui.internal.presentations.PresentationSerializer;
41 import org.eclipse.ui.internal.util.Util;
42 import org.eclipse.ui.presentations.AbstractPresentationFactory;
43 import org.eclipse.ui.presentations.IPresentablePart;
44 import org.eclipse.ui.presentations.IStackPresentationSite;
45 import org.eclipse.ui.presentations.StackDropResult;
46 import org.eclipse.ui.presentations.StackPresentation;
47
48 /**
49 * Implements the common behavior for stacks of Panes (ie: EditorStack and ViewStack)
50 * This layout container has PartPanes as children and belongs to a PartSashContainer.
51 *
52 * @since 3.0
53 */
54 public abstract class PartStack extends LayoutPart implements ILayoutContainer {
55
56 private List children = new ArrayList(3);
57 protected int appearance = PresentationFactoryUtil.ROLE_VIEW;
58
59 // inactiveCurrent is only used when restoring the persisted state of
60 // perspective on startup.
61 private LayoutPart current;
62
63 private boolean ignoreSelectionChanges = false;
64
65 protected IMemento savedPresentationState = null;
66
67 private DefaultStackPresentationSite presentationSite = new DefaultStackPresentationSite() {
68
69 public void close(IPresentablePart part) {
70 PartStack.this.close(part);
71 }
72
73 public void close(IPresentablePart[] parts) {
74 PartStack.this.close(parts);
75 }
76
77 public void dragStart(IPresentablePart beingDragged, Point initialLocation, boolean keyboard) {
78 PartStack.this.dragStart(beingDragged, initialLocation, keyboard);
79 }
80
81 public void dragStart(Point initialLocation, boolean keyboard) {
82 PartStack.this.dragStart(null, initialLocation, keyboard);
83 }
84
85 public boolean isCloseable(IPresentablePart part) {
86 return PartStack.this.isCloseable(part);
87 }
88
89 public boolean isPartMoveable(IPresentablePart part) {
90 return PartStack.this.isMoveable(part);
91 }
92
93 public void selectPart(IPresentablePart toSelect) {
94 PartStack.this.presentationSelectionChanged(toSelect);
95 }
96
97 public boolean supportsState(int state) {
98 return PartStack.this.supportsState(state);
99 }
100
101 public void setState(int newState) {
102 PartStack.this.setState(newState);
103 }
104
105 public IPresentablePart getSelectedPart() {
106 return PartStack.this.getSelectedPart();
107 }
108
109 public void addSystemActions(IMenuManager menuManager) {
110 PartStack.this.addSystemActions(menuManager);
111 }
112
113 public boolean isStackMoveable() {
114 return canMoveFolder();
115 }
116 };
117
118 protected abstract boolean isMoveable(IPresentablePart part);
119 protected abstract boolean isCloseable(IPresentablePart part);
120 protected abstract void addSystemActions(IMenuManager menuManager);
121 protected abstract boolean supportsState(int newState);
122 protected abstract boolean canMoveFolder();
123 protected abstract void derefPart(LayoutPart toDeref);
124 protected abstract boolean allowsDrop(PartPane part);
125
126 protected static void appendToGroupIfPossible(IMenuManager m, String groupId, ContributionItem item) {
127 try {
128 m.appendToGroup(groupId, item);
129 } catch (IllegalArgumentException e) {
130 m.add(item);
131 }
132 }
133
134 /**
135 * Creates a new PartStack, given a constant determining which presentation to use
136 *
137 * @param appearance one of the PresentationFactoryUtil.ROLE_* constants
138 */
139 public PartStack(int appearance) {
140 super("PartStack"); //$NON-NLS-1$
141
142 this.appearance = appearance;
143 }
144
145 /**
146 * Returns the currently selected IPresentablePart, or null if none
147 *
148 * @return
149 */
150 protected IPresentablePart getSelectedPart() {
151 if (current == null) {
152 return null;
153 }
154
155 return current.getPresentablePart();
156 }
157
158 protected IStackPresentationSite getPresentationSite() {
159 return presentationSite;
160 }
161
162 /**
163 * Tests the integrity of this object. Throws an exception if the object's state
164 * is invalid. For use in test suites.
165 */
166 public void testInvariants() {
167 Control focusControl = Display.getCurrent().getFocusControl();
168
169 boolean currentFound = false;
170
171 LayoutPart[] children = getChildren();
172
173 for (int idx = 0; idx < children.length; idx++) {
174 LayoutPart child = children[idx];
175
176 // No null children allowed
177 Assert.isNotNull(child, "null children are not allowed in PartStack"); //$NON-NLS-1$
178
179 // This object can only contain placeholders or PartPanes
180 Assert.isTrue(child instanceof PartPlaceholder || child instanceof PartPane,
181 "PartStack can only contain PartPlaceholders or PartPanes"); //$NON-NLS-1$
182
183 // Ensure that all the PartPanes have an associated presentable part
184 IPresentablePart part = child.getPresentablePart();
185 if (child instanceof PartPane) {
186 Assert.isNotNull(part, "All PartPanes must have a non-null IPresentablePart"); //$NON-NLS-1$
187 }
188
189 // Ensure that the child's backpointer points to this stack
190 ILayoutContainer childContainer = child.getContainer();
191
192 // Disable tests for placeholders -- PartPlaceholder backpointers don't
193 // obey the usual rules -- they sometimes point to a container placeholder
194 // for this stack instead of the real stack.
195 if (!(child instanceof PartPlaceholder)) {
196
197 if (isDisposed()) {
198
199 // Currently, we allow null backpointers if the widgetry is disposed.
200 // However, it is never valid for the child to have a parent other than
201 // this object
202 if (childContainer != null) {
203 Assert.isTrue(childContainer == this,
204 "PartStack has a child that thinks it has a different parent"); //$NON-NLS-1$
205 }
206 } else {
207 // If the widgetry exists, the child's backpointer must point to us
208 Assert.isTrue(childContainer == this,
209 "PartStack has a child that thinks it has a different parent"); //$NON-NLS-1$
210
211 // If this child has focus, then ensure that it is selected and that we have
212 // the active appearance.
213
214 if (SwtUtil.isChild(child.getControl(), focusControl)) {
215 Assert.isTrue(child == current, "The part with focus is not the selected part"); //$NON-NLS-1$
216 // focus check commented out since it fails when focus workaround in LayoutPart.setVisible is not present
217 // Assert.isTrue(getActive() == StackPresentation.AS_ACTIVE_FOCUS);
218 }
219 }
220 }
221
222 // Ensure that "current" points to a valid child
223 if (child == current) {
224 currentFound = true;
225 }
226
227 // Test the child's internal state
228 child.testInvariants();
229 }
230
231 // If we have at least one child, ensure that the "current" pointer points to one of them
232 if (!isDisposed() && getPresentableParts().size() > 0) {
233 Assert.isTrue(currentFound);
234
235 if (!isDisposed()) {
236 StackPresentation presentation = getPresentation();
237
238 // If the presentation controls have focus, ensure that we have the active appearance
239 if (SwtUtil.isChild(presentation.getControl(), focusControl)) {
240 Assert.isTrue(getActive() == StackPresentation.AS_ACTIVE_FOCUS,
241 "The presentation has focus but does not have the active appearance"); //$NON-NLS-1$
242 }
243 }
244 }
245 }
246
247 /* (non-Javadoc)
248 * @see org.eclipse.ui.internal.LayoutPart#describeLayout(java.lang.StringBuffer)
249 */
250 public void describeLayout(StringBuffer buf) {
251 int activeState = getActive();
252 if (activeState == StackPresentation.AS_ACTIVE_FOCUS) {
253 buf.append("active "); //$NON-NLS-1$
254 } else if (activeState == StackPresentation.AS_ACTIVE_NOFOCUS) {
255 buf.append("active_nofocus "); //$NON-NLS-1$
256 }
257
258 buf.append("("); //$NON-NLS-1$
259
260 LayoutPart[] children = ((ILayoutContainer)this).getChildren();
261
262 int visibleChildren = 0;
263
264 for (int idx = 0; idx < children.length; idx++) {
265
266 LayoutPart next = children[idx];
267 if (!(next instanceof PartPlaceholder)) {
268 if (idx > 0) {
269 buf.append(", "); //$NON-NLS-1$
270 }
271
272 if (next == current) {
273 buf.append("*"); //$NON-NLS-1$
274 }
275
276 next.describeLayout(buf);
277
278 visibleChildren++;
279 }
280 }
281
282 buf.append(")"); //$NON-NLS-1$
283 }
284
285 /**
286 * See IVisualContainer#add
287 */
288 public void add(LayoutPart child) {
289 children.add(child);
290 showPart(child, null);
291 }
292
293 /**
294 * Add a part at a particular position
295 */
296 protected void add(LayoutPart newChild, Object cookie) {
297 children.add(newChild);
298
299 showPart(newChild, cookie);
300 }
301
302 /*
303 * (non-Javadoc)
304 *
305 * @see org.eclipse.ui.internal.ILayoutContainer#allowsAutoFocus()
306 */
307 public boolean allowsAutoFocus() {
308 if (presentationSite.getState() == IStackPresentationSite.STATE_MINIMIZED) { return false; }
309
310 ILayoutContainer parent = getContainer();
311
312 if (parent != null && !parent.allowsAutoFocus()) { return false; }
313
314 return true;
315 }
316
317 /**
318 * @param parts
319 */
320 protected void close(IPresentablePart[] parts) {
321 for (int idx = 0; idx < parts.length; idx++) {
322 IPresentablePart part = parts[idx];
323
324 close(part);
325 }
326 }
327
328 /**
329 * @param part
330 */
331 protected void close(IPresentablePart part) {
332 if (!presentationSite.isCloseable(part)) { return; }
333
334 LayoutPart layoutPart = getPaneFor(part);
335
336 if (layoutPart != null && layoutPart instanceof PartPane) {
337 PartPane viewPane = (PartPane) layoutPart;
338
339 viewPane.doHide();
340 }
341 }
342
343 public boolean isDisposed() {
344 return getPresentation() == null;
345 }
346
347 protected AbstractPresentationFactory getFactory() {
348 AbstractPresentationFactory factory = ((WorkbenchWindow) getPage()
349 .getWorkbenchWindow()).getWindowConfigurer()
350 .getPresentationFactory();
351
352 return factory;
353 }
354
355 public void createControl(Composite parent) {
356 if (!isDisposed()) {
357 return;
358 }
359
360 AbstractPresentationFactory factory = getFactory();
361
362 PresentationSerializer serializer = new PresentationSerializer(getPresentableParts());
363
364 StackPresentation presentation = PresentationFactoryUtil.createPresentation(factory,
365 appearance, parent, presentationSite, serializer, savedPresentationState);
366
367 createControl(parent, presentation);
368 }
369
370 public void createControl(Composite parent, StackPresentation presentation) {
371
372 Assert.isTrue(isDisposed());
373
374 if (presentationSite.getPresentation() != null) return;
375
376 presentationSite.setPresentation(presentation);
377
378 // Add all visible children to the presentation
379 Iterator iter = children.iterator();
380 while (iter.hasNext()) {
381 LayoutPart part = (LayoutPart) iter.next();
382
383 showPart(part, null);
384 }
385
386 Control ctrl = getPresentation().getControl();
387
388 // Add a drop target that lets us drag views directly to a particular
389 // tab
390 DragUtil.addDragTarget(ctrl, new IDragOverListener() {
391
392 public IDropTarget drag(Control currentControl,
393 final Object draggedObject, Point position,
394 Rectangle dragRectangle) {
395
396 if (!(draggedObject instanceof PartPane)) { return null; }
397
398 final PartPane pane = (PartPane) draggedObject;
399 if (!allowsDrop(pane)) {
400 return null;
401 }
402
403 // Don't allow views to be dragged between windows
404 if (pane.getWorkbenchWindow() != getWorkbenchWindow()) { return null; }
405
406 // Regardless of the wishes of the presentation, ignore 4 pixels around the edge of the control.
407 // This ensures that it will always be possible to dock around the edge of the control.
408 {
409 Point controlCoordinates = currentControl.getParent().toControl(position);
410 Rectangle bounds = currentControl.getBounds();
411 int closestSide = Geometry.getClosestSide(bounds, controlCoordinates);
412
413 if (Geometry.getDistanceFromEdge(bounds, controlCoordinates, closestSide) < 5) {
414 return null;
415 }
416 }
417 // End of check for stacking on edge
418
419 final StackDropResult dropResult = getPresentation().dragOver(
420 currentControl, position);
421
422 if (dropResult == null) { return null; }
423
424 return new IDropTarget() {
425
426 public void drop() {
427
428 // If we're dragging a pane over itself do nothing
429 //if (dropResult.getInsertionPoint() == pane.getPresentablePart()) { return; };
430
431 // Don't worry about reparenting the view if we're
432 // simply rearranging tabs within this folder
433 if (pane.getContainer() != PartStack.this) {
434 derefPart(pane);
435 pane.reparent(getParent());
436 } else {
437 remove(pane);
438 }
439
440 add(pane, dropResult.getCookie());
441 setSelection(pane);
442 pane.setFocus();
443 }
444
445 public Cursor getCursor() {
446 return DragCursors.getCursor(DragCursors.CENTER);
447 }
448
449 public Rectangle getSnapRectangle() {
450 return dropResult.getSnapRectangle();
451 }
452 };
453 }
454
455 });
456
457 ctrl.setData(this);
458
459 updateActions();
460
461 // We should not have a placeholder selected once we've created the widgetry
462 if (current instanceof PartPlaceholder) {
463 current = null;
464 updateContainerVisibleTab();
465 }
466
467 refreshPresentationSelection();
468
469 Rectangle bounds = presentation.getControl().getBounds();
470 int minimumHeight = getMinimumHeight();
471
472 if (presentationSite.getState() == IStackPresentationSite.STATE_MINIMIZED
473 && bounds.height != minimumHeight) {
474 bounds.width = getMinimumWidth();
475 bounds.height = minimumHeight;
476 getPresentation().setBounds(bounds);
477 }
478 }
479
480 /**
481 * Saves the current state of the presentation to savedPresentationState, if the
482 * presentation exists.
483 */
484 protected void savePresentationState() {
485 if (isDisposed()) {
486 return;
487 }
488
489 {// Save the presentation's state before disposing it
490 XMLMemento memento = XMLMemento.createWriteRoot(IWorkbenchConstants.TAG_PRESENTATION);
491 memento.putString(IWorkbenchConstants.TAG_ID, getFactory().getId());
492
493 PresentationSerializer serializer = new PresentationSerializer(getPresentableParts());
494
495 getPresentation().saveState(serializer, memento);
496
497 // Store the memento in savedPresentationState
498 savedPresentationState = memento;
499 }
500 }
501
502 /**
503 * See LayoutPart#dispose
504 */
505 public void dispose() {
506
507 if (isDisposed()) return;
508
509 savePresentationState();
510
511 presentationSite.dispose();
512
513 Iterator iter = children.iterator();
514 while (iter.hasNext()) {
515 LayoutPart next = (LayoutPart) iter.next();
516
517 next.setContainer(null);
518 }
519 }
520
521 public void findSashes(LayoutPart part, PartPane.Sashes sashes) {
522 ILayoutContainer container = getContainer();
523
524 if (container != null) {
525 container.findSashes(this, sashes);
526 }
527 }
528
529 /**
530 * Forces the layout to be recomputed for all parts
531 */
532 private void forceLayout() {
533 PartSashContainer cont = (PartSashContainer) getContainer();
534 if (cont != null) {
535 LayoutTree tree = cont.getLayoutTree();
536 tree.setBounds(getParent().getClientArea());
537 }
538 }
539
540 /**
541 * Gets the presentation bounds.
542 */
543 public Rectangle getBounds() {
544 if (getPresentation() == null) { return new Rectangle(0, 0, 0, 0); }
545
546 return getPresentation().getControl().getBounds();
547 }
548
549 /**
550 * See IVisualContainer#getChildren
551 */
552 public LayoutPart[] getChildren() {
553 return (LayoutPart[]) children.toArray(new LayoutPart[children.size()]);
554 }
555
556 public Control getControl() {
557 StackPresentation presentation = getPresentation();
558
559 if (presentation == null) {
560 return null;
561 }
562
563 return presentation.getControl();
564 }
565
566 /**
567 * Answer the number of children.
568 */
569 public int getItemCount() {
570 if (isDisposed()) {
571 return children.size();
572 }
573 return getPresentableParts().size();
574 }
575
576 // getMinimumHeight() added by cagatayk@acm.org
577 /**
578 * @see LayoutPart#getMinimumHeight()
579 */
580 public int getMinimumHeight() {
581 if (getPresentation() == null) { return 0; }
582
583 return getPresentation().computeMinimumSize().y;
584 }
585
586 /**
587 * Returns the LayoutPart for the given IPresentablePart, or null if the given
588 * IPresentablePart is not in this stack. Returns null if given a null argument.
589 *
590 * @param part to locate or null
591 * @return
592 */
593 protected LayoutPart getPaneFor(IPresentablePart part) {
594 if (part == null) {
595 return null;
596 }
597
598 Iterator iter = children.iterator();
599 while (iter.hasNext()) {
600 LayoutPart next = (LayoutPart) iter.next();
601
602 if (next.getPresentablePart() == part) { return next; }
603 }
604
605 return null;
606 }
607
608 /**
609 * Get the parent control.
610 */
611 public Composite getParent() {
612 return getControl().getParent();
613 }
614
615 private IPresentablePart getPresentablePartAtIndex(int idx) {
616 List presentableParts = getPresentableParts();
617
618 if (idx >= 0 && idx < presentableParts.size()) { return (IPresentablePart) presentableParts
619 .get(idx); }
620
621 return null;
622 }
623
624 /**
625 * Returns a list of IPresentablePart
626 *
627 * @return
628 */
629 public List getPresentableParts() {
630 List result = new ArrayList(children.size());
631
632 Iterator iter = children.iterator();
633 while (iter.hasNext()) {
634 LayoutPart part = (LayoutPart) iter.next();
635
636 IPresentablePart presentablePart = part.getPresentablePart();
637
638 if (presentablePart != null) {
639 result.add(presentablePart);
640 }
641 }
642
643 return result;
644 }
645
646 protected StackPresentation getPresentation() {
647 return presentationSite.getPresentation();
648 }
649
650 /**
651 * Returns the visible child.
652 */
653 public PartPane getVisiblePart() {
654 if (current instanceof PartPane) {
655 return (PartPane)current;
656 }
657 return null;
658 }
659
660 private void presentationSelectionChanged(IPresentablePart newSelection) {
661 // Ignore selection changes that occur as a result of removing a part
662 if (ignoreSelectionChanges) {
663 return;
664 }
665 LayoutPart newPart = getPaneFor(newSelection);
666
667 // This method should only be called on objects that are already in the layout
668 Assert.isNotNull(newPart);
669
670 if (newPart == current) {
671 return;
672 }
673
674 setSelection(newPart);
675
676 if (newPart != null) {
677 newPart.setFocus();
678 }
679
680 // set the title of the detached window to reflect the active tab
681 Window window = getWindow();
682 if (window instanceof DetachedWindow) {
683 window.getShell().setText(newSelection.getTitle());
684 }
685 }
686
687 /**
688 * See IVisualContainer#remove
689 */
690 public void remove(LayoutPart child) {
691 IPresentablePart presentablePart = child.getPresentablePart();
692
693 // Need to remove it from the list of children before notifying the presentation
694 // since it may setVisible(false) on the part, leading to a partHidden notification,
695 // during which findView must not find the view being removed. See bug 60039.
696 children.remove(child);
697
698 StackPresentation presentation = getPresentation();
699
700 if (presentablePart != null && presentation != null) {
701 ignoreSelectionChanges = true;
702 presentation.removePart(presentablePart);
703 ignoreSelectionChanges = false;
704 }
705
706 if (!isDisposed()) {
707 child.setContainer(null);
708 }
709
710 if (child == current) {
711 updateContainerVisibleTab();
712 }
713 }
714
715 /**
716 * Reparent a part. Also reparent visible children...
717 */
718 public void reparent(Composite newParent) {
719 if (!newParent.isReparentable()) return;
720
721 Control control = getControl();
722 if ((control == null) || (control.getParent() == newParent)) return;
723
724 super.reparent(newParent);
725
726 Iterator iter = children.iterator();
727 while (iter.hasNext()) {
728 LayoutPart next = (LayoutPart) iter.next();
729 next.reparent(newParent);
730 }
731 }
732
733 /**
734 * See IVisualContainer#replace
735 */
736 public void replace(LayoutPart oldChild, LayoutPart newChild) {
737 // commented out but left for future reference - we're using this
738 // as the cookie for the part presentation but this will
739 // almost always be null (oldChilds being PartPlaceholders)
740 // even if they aren't null, we don't handle
741 // IPresentableParts as cookies
742 //
743 // IPresentablePart oldPart = oldChild.getPresentablePart();
744 IPresentablePart newPart = newChild.getPresentablePart();
745
746 int idx = children.indexOf(oldChild);
747 int numPlaceholders = 0;
748 //subtract the number of placeholders still existing in the list
749 //before this one - they wont have parts.
750 for (int i = 0; i < idx; i++) {
751 if (children.get(i) instanceof PartPlaceholder)
752 numPlaceholders++;
753 }
754 Integer cookie = new Integer(idx - numPlaceholders);
755 children.add(idx, newChild);
756
757 showPart(newChild, cookie);
758
759 if (oldChild == current && !(newChild instanceof PartPlaceholder)) {
760 setSelection(newChild);
761 }
762
763 remove(oldChild);
764 }
765
766 public boolean resizesVertically() {
767 return presentationSite.getState() != IStackPresentationSite.STATE_MINIMIZED;
768 }
769
770 /**
771 * @see IPersistable
772 */
773 public IStatus restoreState(IMemento memento) {
774 // Read the active tab.
775 String activeTabID = memento
776 .getString(IWorkbenchConstants.TAG_ACTIVE_PAGE_ID);
777
778 // Read the page elements.
779 IMemento[] children = memento.getChildren(IWorkbenchConstants.TAG_PAGE);
780 if (children != null) {
781 // Loop through the page elements.
782 for (int i = 0; i < children.length; i++) {
783 // Get the info details.
784 IMemento childMem = children[i];
785 String partID = childMem
786 .getString(IWorkbenchConstants.TAG_CONTENT);
787
788 // Create the part.
789 LayoutPart part = new PartPlaceholder(partID);
790 part.setContainer(this);
791 add(part);
792 //1FUN70C: ITPUI:WIN - Shouldn't set Container when not active
793 //part.setContainer(this);
794 if (partID.equals(activeTabID)) {
795 // Mark this as the active part.
796 current = part;
797 }
798 }
799 }
800
801 Integer expanded = memento.getInteger(IWorkbenchConstants.TAG_EXPANDED);
802 setState((expanded == null || expanded.intValue() != IStackPresentationSite.STATE_MINIMIZED) ? IStackPresentationSite.STATE_RESTORED
803 : IStackPresentationSite.STATE_MINIMIZED);
804
805 Integer appearance = memento.getInteger(IWorkbenchConstants.TAG_APPEARANCE);
806 if (appearance != null) {
807 this.appearance = appearance.intValue();
808 }
809
810 // Determine if the presentation has saved any info here
811 savedPresentationState = null;
812 IMemento[] presentationMementos = memento.getChildren(IWorkbenchConstants.TAG_PRESENTATION);
813
814 for (int idx = 0; idx < presentationMementos.length; idx++) {
815 IMemento child = presentationMementos[idx];
816
817 String id = child.getString(IWorkbenchConstants.TAG_ID);
818
819 if (Util.equals(id, getFactory().getId())) {
820 savedPresentationState = child;
821 break;
822 }
823 }
824
825 return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, 0, "", null); //$NON-NLS-1$
826 }
827
828 /**
829 * @see IPersistable
830 */
831 public IStatus saveState(IMemento memento) {
832
833 // Save the active tab.
834 if (current != null)
835 memento.putString(IWorkbenchConstants.TAG_ACTIVE_PAGE_ID,
836 current.getCompoundId());
837
838 Iterator iter = children.iterator();
839 while (iter.hasNext()) {
840 LayoutPart next = (LayoutPart) iter.next();
841
842 IMemento childMem = memento
843 .createChild(IWorkbenchConstants.TAG_PAGE);
844
845 IPresentablePart part = next.getPresentablePart();
846 String tabText = "LabelNotFound"; //$NON-NLS-1$
847 if (part != null) {
848 tabText = part.getName();
849 }
850 childMem.putString(IWorkbenchConstants.TAG_LABEL, tabText);
851 childMem.putString(IWorkbenchConstants.TAG_CONTENT, next.getCompoundId());
852 }
853
854 memento
855 .putInteger(
856 IWorkbenchConstants.TAG_EXPANDED,
857 (presentationSite.getState() == IStackPresentationSite.STATE_MINIMIZED) ? IStackPresentationSite.STATE_MINIMIZED
858 : IStackPresentationSite.STATE_RESTORED);
859
860 memento.putInteger(IWorkbenchConstants.TAG_APPEARANCE, appearance);
861
862 savePresentationState();
863
864 if (savedPresentationState != null) {
865 IMemento presentationState = memento.createChild(IWorkbenchConstants.TAG_PRESENTATION);
866 presentationState.putMemento(savedPresentationState);
867 }
868
869 return new Status(IStatus.OK, PlatformUI.PLUGIN_ID, 0, "", null); //$NON-NLS-1$
870 }
871
872 protected WorkbenchPage getPage() {
873 WorkbenchWindow window = (WorkbenchWindow)getWorkbenchWindow();
874
875 if (window == null) {
876 return null;
877 }
878
879 return (WorkbenchPage)window.getActivePage();
880 }
881
882 /**
883 * Set the active appearence on the tab folder.
884 *
885 * @param active
886 */
887 public void setActive(int activeState) {
888
889 if (activeState != StackPresentation.AS_INACTIVE) {
890 if (presentationSite.getState() == IStackPresentationSite.STATE_MINIMIZED) {
891 setState(IStackPresentationSite.STATE_RESTORED);
892 }
893 }
894
895 presentationSite.setActive(activeState);
896 }
897
898 public int getActive() {
899 return presentationSite.getActive();
900 }
901
902 /**
903 * Sets the presentation bounds.
904 */
905 public void setBounds(Rectangle r) {
906 if (getPresentation() != null) {
907 getPresentation().setBounds(r);
908 }
909 }
910
911 public void setSelection(LayoutPart part) {
912 if (current == part) {
913 return;
914 }
915
916 current = part;
917
918 if (!isDisposed()) {
919 updateActions();
920 }
921 refreshPresentationSelection();
922 }
923
924 /**
925 * Subclasses should override this method to update the enablement state of their
926 * actions
927 */
928 protected void updateActions() {
929
930 }
931
932 private void refreshPresentationSelection() {
933 if (current != null) {
934 IPresentablePart presentablePart = current.getPresentablePart();
935 StackPresentation presentation = getPresentation();
936
937 if (presentablePart != null && presentation != null) {
938
939 current.createControl(getParent());
940 if (current.getControl().getParent() != getControl().getParent()) {
941 current.reparent(getControl().getParent());
942 }
943
944 current.moveAbove(getPresentation().getControl());
945
946 presentation.selectPart(presentablePart);
947 }
948 }
949 }
950
951 protected void setState(int newState) {
952 if (!supportsState(newState) || newState == presentationSite.getState()) { return; }
953
954 int oldState = presentationSite.getState();
955
956 if (current != null) {
957 if (newState == IStackPresentationSite.STATE_MAXIMIZED) {
958 PartPane pane = getVisiblePart();
959 if (pane != null) {
960 pane.doZoom();
961 }
962 } else {
963 presentationSite.setPresentationState(newState);
964
965 WorkbenchPage page = getPage();
966 if (page != null) {
967 if (page.isZoomed()) {
968 page.zoomOut();
969 }
970
971 updateControlBounds();
972
973 if (oldState == IStackPresentationSite.STATE_MINIMIZED) {
974 forceLayout();
975 }
976 }
977 }
978 }
979
980 if (presentationSite.getState() == IStackPresentationSite.STATE_MINIMIZED) {
981 WorkbenchPage page = getPage();
982
983 if (page != null) {
984 page.refreshActiveView();
985 }
986 }
987 }
988
989 public void setZoomed(boolean isZoomed) {
990 super.setZoomed(isZoomed);
991
992 if (isZoomed) {
993 presentationSite
994 .setPresentationState(IStackPresentationSite.STATE_MAXIMIZED);
995 } else if (presentationSite.getState() == IStackPresentationSite.STATE_MAXIMIZED) {
996 presentationSite.setPresentationState(IStackPresentationSite.STATE_RESTORED);
997 }
998 }
999
1000 /**
1001 * Makes the given part visible in the presentation
1002 *
1003 * @param presentablePart
1004 */
1005 private void showPart(LayoutPart part, Object cookie) {
1006
1007 if (isDisposed()) {
1008 return;
1009 }
1010
1011 part.setContainer(this);
1012
1013 IPresentablePart presentablePart = part.getPresentablePart();
1014
1015 if (presentablePart == null) { return; }
1016
1017 presentationSite.getPresentation().addPart(presentablePart, cookie);
1018
1019 if (current == null) {
1020 setSelection(part);
1021 }
1022 }
1023
1024 /**
1025 * Update the container to show the correct visible tab based on the
1026 * activation list.
1027 *
1028 * @param org.eclipse.ui.internal.ILayoutContainer
1029 */
1030 private void updateContainerVisibleTab() {
1031 LayoutPart[] parts = getChildren();
1032
1033 if (parts.length < 1) {
1034 setSelection(null);
1035 return;
1036 }
1037
1038 PartPane selPart = null;
1039 int topIndex = 0;
1040 WorkbenchPage page = getPage();
1041
1042 if (page != null) {
1043 IWorkbenchPartReference sortedPartsArray[] = page.getSortedParts();
1044 List sortedParts = Arrays.asList(sortedPartsArray);
1045 for (int i = 0; i < parts.length; i++) {
1046 if (parts[i] instanceof PartPane) {
1047 IWorkbenchPartReference part = ((PartPane) parts[i])
1048 .getPartReference();
1049 int index = sortedParts.indexOf(part);
1050 if (index >= topIndex) {
1051 topIndex = index;
1052 selPart = (PartPane) parts[i];
1053 }
1054 }
1055 }
1056
1057 }
1058
1059 if (selPart == null) {
1060 List presentableParts = getPresentableParts();
1061 if (presentableParts.size() != 0) {
1062 IPresentablePart part = (IPresentablePart)getPresentableParts().get(0);
1063
1064 selPart = (PartPane)getPaneFor(part);
1065 }
1066 }
1067
1068 setSelection(selPart);
1069 }
1070
1071 private void updateControlBounds() {
1072 StackPresentation presentation = getPresentation();
1073
1074 if (presentation != null) {
1075 Rectangle bounds = presentation.getControl().getBounds();
1076 int minimumHeight = getMinimumHeight();
1077
1078 if (presentationSite.getState() == IStackPresentationSite.STATE_MINIMIZED
1079 && bounds.height != minimumHeight) {
1080 bounds.width = getMinimumWidth();
1081 bounds.height = minimumHeight;
1082 getPresentation().setBounds(bounds);
1083
1084 forceLayout();
1085 }
1086 }
1087 }
1088
1089 /**
1090 *
1091 */
1092 public void showSystemMenu() {
1093 getPresentation().showSystemMenu();
1094 }
1095
1096 public void showPaneMenu() {
1097 getPresentation().showPaneMenu();
1098 }
1099
1100 public void showPartList() {
1101 getPresentation().showPartList();
1102 }
1103
1104 /**
1105 * @param pane
1106 * @return
1107 */
1108 public Control[] getTabList(LayoutPart part) {
1109 if (part != null) {
1110 IPresentablePart presentablePart = part.getPresentablePart();
1111 StackPresentation presentation = getPresentation();
1112
1113 if (presentablePart != null && presentation != null) {
1114 return presentation.getTabList(presentablePart);
1115 }
1116 }
1117
1118 return new Control[0];
1119 }
1120
1121 /**
1122 *
1123 * @param beingDragged
1124 * @param initialLocation
1125 * @param keyboard
1126 */
1127 public void dragStart(IPresentablePart beingDragged, Point initialLocation, boolean keyboard) {
1128 if (beingDragged == null) {
1129 if (canMoveFolder()) {
1130 if (presentationSite.getState() == IStackPresentationSite.STATE_MAXIMIZED) {
1131 setState(IStackPresentationSite.STATE_RESTORED);
1132 }
1133
1134 DragUtil.performDrag(PartStack.this, Geometry.toDisplay(
1135 getParent(), getPresentation().getControl().getBounds()),
1136 initialLocation, !keyboard);
1137 }
1138 } else {
1139 if (presentationSite.isPartMoveable(beingDragged)) {
1140 LayoutPart pane = getPaneFor(beingDragged);
1141
1142 if (pane != null) {
1143 if (presentationSite.getState() == IStackPresentationSite.STATE_MAXIMIZED) {
1144 presentationSite.setState(IStackPresentationSite.STATE_RESTORED);
1145 }
1146
1147 DragUtil.performDrag(pane, Geometry.toDisplay(getParent(),
1148 getPresentation().getControl().getBounds()),
1149 initialLocation, !keyboard);
1150 }
1151 }
1152 }
1153 }
1154
1155 /**
1156 * @return Returns the savedPresentationState.
1157 */
1158 public IMemento getSavedPresentationState() {
1159 return savedPresentationState;
1160 }
1161}