001: /*******************************************************************************
002: * Copyright (c) 2005, 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 - bug 51580
011: * Chris Torrence, ITT Visual Information Solutions - bugs 51580 202208
012: *******************************************************************************/package org.eclipse.ui.internal.presentations.util;
013:
014: import java.util.ArrayList;
015: import java.util.HashMap;
016: import java.util.Iterator;
017: import java.util.Map;
018:
019: import org.eclipse.core.runtime.Assert;
020: import org.eclipse.core.runtime.Plugin;
021: import org.eclipse.swt.SWT;
022: import org.eclipse.swt.events.DisposeEvent;
023: import org.eclipse.swt.events.DisposeListener;
024: import org.eclipse.swt.graphics.Point;
025: import org.eclipse.swt.graphics.Rectangle;
026: import org.eclipse.swt.widgets.Control;
027: import org.eclipse.ui.IMemento;
028: import org.eclipse.ui.PlatformUI;
029: import org.eclipse.ui.internal.preferences.IDynamicPropertyMap;
030: import org.eclipse.ui.internal.preferences.PreferenceStoreAdapter;
031: import org.eclipse.ui.internal.preferences.PreferencesAdapter;
032: import org.eclipse.ui.internal.preferences.PropertyMapAdapter;
033: import org.eclipse.ui.internal.preferences.ThemeManagerAdapter;
034: import org.eclipse.ui.internal.presentations.defaultpresentation.DefaultPartList;
035: import org.eclipse.ui.internal.util.PrefUtil;
036: import org.eclipse.ui.presentations.IPartMenu;
037: import org.eclipse.ui.presentations.IPresentablePart;
038: import org.eclipse.ui.presentations.IPresentationSerializer;
039: import org.eclipse.ui.presentations.IStackPresentationSite;
040: import org.eclipse.ui.presentations.StackDropResult;
041: import org.eclipse.ui.presentations.StackPresentation;
042:
043: /**
044: * @since 3.0
045: */
046: public final class TabbedStackPresentation extends StackPresentation {
047:
048: private PresentablePartFolder folder;
049: private ISystemMenu systemMenu;
050: private ISystemMenu partList;
051: private PreferenceStoreAdapter apiPreferences = new PreferenceStoreAdapter(
052: PrefUtil.getAPIPreferenceStore());
053: private ThemeManagerAdapter themePreferences = new ThemeManagerAdapter(
054: PlatformUI.getWorkbench().getThemeManager());
055:
056: private TabOrder tabs;
057:
058: private TabDragHandler dragBehavior;
059:
060: private boolean initializing = true;
061: private int ignoreSelectionChanges = 0;
062:
063: private TabFolderListener tabFolderListener = new TabFolderListener() {
064: public void handleEvent(TabFolderEvent e) {
065: switch (e.type) {
066: case TabFolderEvent.EVENT_MINIMIZE: {
067: getSite().setState(
068: IStackPresentationSite.STATE_MINIMIZED);
069: break;
070: }
071: case TabFolderEvent.EVENT_MAXIMIZE: {
072: getSite().setState(
073: IStackPresentationSite.STATE_MAXIMIZED);
074: break;
075: }
076: case TabFolderEvent.EVENT_RESTORE: {
077: getSite().setState(
078: IStackPresentationSite.STATE_RESTORED);
079: break;
080: }
081: case TabFolderEvent.EVENT_CLOSE: {
082: IPresentablePart part = folder.getPartForTab(e.tab);
083:
084: if (part != null) {
085: getSite().close(new IPresentablePart[] { part });
086: }
087: break;
088: }
089: case TabFolderEvent.EVENT_SHOW_LIST: {
090: showPartList();
091: break;
092: }
093: case TabFolderEvent.EVENT_GIVE_FOCUS_TO_PART: {
094: IPresentablePart part = getSite().getSelectedPart();
095: if (part != null) {
096: part.setFocus();
097: }
098: break;
099: }
100: case TabFolderEvent.EVENT_PANE_MENU: {
101: IPresentablePart part = getSite().getSelectedPart();
102: if (part != null) {
103: part.setFocus();
104: }
105: TabbedStackPresentation.this .showPaneMenu(folder
106: .getPartForTab(e.tab), new Point(e.x, e.y));
107: break;
108: }
109: case TabFolderEvent.EVENT_DRAG_START: {
110: AbstractTabItem beingDragged = e.tab;
111: Point initialLocation = new Point(e.x, e.y);
112:
113: if (beingDragged == null) {
114: getSite().dragStart(initialLocation, false);
115: } else {
116: IPresentablePart part = folder
117: .getPartForTab(beingDragged);
118:
119: try {
120: dragStart = folder.indexOf(part);
121: getSite().dragStart(part, initialLocation,
122: false);
123: } finally {
124: dragStart = -1;
125: }
126: }
127: break;
128: }
129: case TabFolderEvent.EVENT_TAB_SELECTED: {
130: if (ignoreSelectionChanges > 0) {
131: return;
132: }
133:
134: IPresentablePart part = folder.getPartForTab(e.tab);
135:
136: if (part != null) {
137: getSite().selectPart(part);
138: }
139: break;
140: }
141: case TabFolderEvent.EVENT_SYSTEM_MENU: {
142: IPresentablePart part = folder.getPartForTab(e.tab);
143:
144: if (part == null) {
145: part = getSite().getSelectedPart();
146: }
147:
148: if (part != null) {
149: showSystemMenu(new Point(e.x, e.y), part);
150: }
151: break;
152: }
153: case TabFolderEvent.EVENT_PREFERRED_SIZE: {
154: IPresentablePart part = folder.getPartForTab(e.tab);
155: if (part == null) {
156: // Standalone views with no title have no tab, so just get the part.
157: IPresentablePart[] parts = getSite().getPartList();
158: if (parts.length > 0)
159: part = parts[0];
160: }
161: if (part == getSite().getSelectedPart()) {
162: getSite().flushLayout();
163: }
164: break;
165: }
166: }
167: }
168: };
169:
170: private int dragStart = -1;
171: private Map prefs = new HashMap();
172:
173: public TabbedStackPresentation(IStackPresentationSite site,
174: AbstractTabFolder widget, ISystemMenu systemMenu) {
175: this (site, new PresentablePartFolder(widget), systemMenu);
176: }
177:
178: public TabbedStackPresentation(IStackPresentationSite site,
179: PresentablePartFolder folder, ISystemMenu systemMenu) {
180: this (site, folder, new LeftToRightTabOrder(folder),
181: new ReplaceDragHandler(folder.getTabFolder()),
182: systemMenu);
183: }
184:
185: public TabbedStackPresentation(IStackPresentationSite site,
186: PresentablePartFolder newFolder, TabOrder tabs,
187: TabDragHandler dragBehavior, ISystemMenu systemMenu) {
188: super (site);
189: this .systemMenu = systemMenu;
190:
191: this .folder = newFolder;
192: this .tabs = tabs;
193: this .dragBehavior = dragBehavior;
194:
195: // Add a dispose listener. This will call the presentationDisposed()
196: // method when the widget is destroyed.
197: folder.getTabFolder().getControl().addDisposeListener(
198: new DisposeListener() {
199: public void widgetDisposed(DisposeEvent e) {
200: presentationDisposed();
201: }
202: });
203:
204: folder.getTabFolder().addListener(tabFolderListener);
205:
206: this .partList = new DefaultPartList(site, newFolder);
207: }
208:
209: /**
210: * Restores a presentation from a previously stored state
211: *
212: * @param serializer (not null)
213: * @param savedState (not null)
214: */
215: public void restoreState(IPresentationSerializer serializer,
216: IMemento savedState) {
217: tabs.restoreState(serializer, savedState);
218: }
219:
220: /* (non-Javadoc)
221: * @see org.eclipse.ui.presentations.StackPresentation#saveState(org.eclipse.ui.presentations.IPresentationSerializer, org.eclipse.ui.IMemento)
222: */
223: public void saveState(IPresentationSerializer context,
224: IMemento memento) {
225: super .saveState(context, memento);
226:
227: tabs.saveState(context, memento);
228: }
229:
230: /**
231: * Returns true iff the presentation has been disposed
232: *
233: * @return true iff the presentation has been disposed
234: */
235: private boolean isDisposed() {
236: return folder == null || folder.isDisposed();
237: }
238:
239: /* (non-Javadoc)
240: * @see org.eclipse.ui.presentations.StackPresentation#setBounds(org.eclipse.swt.graphics.Rectangle)
241: */
242: public void setBounds(Rectangle bounds) {
243: folder.setBounds(bounds);
244: }
245:
246: /* (non-Javadoc)
247: * @see org.eclipse.ui.presentations.StackPresentation#computeMinimumSize()
248: */
249: public Point computeMinimumSize() {
250: return folder.getTabFolder().computeSize(SWT.DEFAULT,
251: SWT.DEFAULT);
252: }
253:
254: /**
255: * Returns the minimum size for this stack, taking into account
256: * the available perpendicular space.
257: * @param width indicates whether a width (=true) or a height (=false) is being computed
258: * @param availablePerpendicular available space perpendicular to the direction being measured
259: * or INFINITE if unbounded (pixels).
260: * @return returns the preferred minimum size (pixels).
261: * This is a width if width == true or a height if width == false.
262: */
263: private int computePreferredMinimumSize(boolean width,
264: int availablePerpendicular) {
265: int minSize;
266: int hint = availablePerpendicular == INFINITE ? SWT.DEFAULT
267: : availablePerpendicular;
268: if (width) {
269: minSize = folder.getTabFolder().computeSize(SWT.DEFAULT,
270: hint).x;
271: } else {
272: minSize = folder.getTabFolder().computeSize(hint,
273: SWT.DEFAULT).y;
274: }
275: return minSize;
276: }
277:
278: /* (non-Javadoc)
279: * @see org.eclipse.ui.ISizeProvider#computePreferredSize(boolean, int, int, int)
280: */
281: public int computePreferredSize(boolean width,
282: int availableParallel, int availablePerpendicular,
283: int preferredResult) {
284:
285: // If there is exactly one part in the stack, this just returns the
286: // preferred size of the part as the preferred size of the stack.
287: IPresentablePart[] parts = getSite().getPartList();
288: if (parts.length == 1 && parts[0] != null) {
289: int partSize = parts[0].computePreferredSize(width,
290: availableParallel, availablePerpendicular,
291: preferredResult);
292:
293: // Adjust preferred size to take into account tab and border trim.
294: int minSize = computePreferredMinimumSize(width,
295: availablePerpendicular);
296: if (width) {
297: // PaneFolder adds some bogus tab spacing, so just find the maximum width.
298: partSize = Math.max(minSize, partSize);
299: } else {
300: partSize += minSize;
301: }
302:
303: return partSize;
304: }
305:
306: if (preferredResult != INFINITE
307: || getSite().getState() == IStackPresentationSite.STATE_MINIMIZED) {
308: int minSize = computePreferredMinimumSize(width,
309: availablePerpendicular);
310:
311: if (getSite().getState() == IStackPresentationSite.STATE_MINIMIZED) {
312: return minSize;
313: }
314:
315: return Math.max(minSize, preferredResult);
316: }
317:
318: return INFINITE;
319: }
320:
321: /* (non-Javadoc)
322: * @see org.eclipse.ui.presentations.StackPresentation#getSizeFlags(boolean)
323: */
324: public int getSizeFlags(boolean width) {
325: int flags = 0;
326: // If there is exactly one part in the stack,
327: // then take into account the size flags of the part.
328: IPresentablePart[] parts = getSite().getPartList();
329: if (parts.length == 1 && parts[0] != null) {
330: flags |= parts[0].getSizeFlags(width);
331: }
332:
333: return flags | super .getSizeFlags(width);
334: }
335:
336: /* (non-Javadoc)
337: * @see org.eclipse.ui.presentations.StackPresentation#showPartList()
338: */
339: public void showPartList() {
340: if (partList != null) {
341: final int numberOfParts = folder.getTabFolder()
342: .getItemCount();
343: if (numberOfParts > 0) {
344: partList.show(getControl(), folder.getTabFolder()
345: .getPartListLocation(), getSite()
346: .getSelectedPart());
347: }
348: }
349: }
350:
351: /* (non-Javadoc)
352: * @see org.eclipse.ui.presentations.StackPresentation#dispose()
353: */
354: public void dispose() {
355: // Dispose the tab folder's widgetry
356: folder.getTabFolder().getControl().dispose();
357: }
358:
359: /**
360: * Called when the tab folder is disposed.
361: */
362: private void presentationDisposed() {
363: apiPreferences.dispose();
364: themePreferences.dispose();
365:
366: Iterator iter = prefs.values().iterator();
367: while (iter.hasNext()) {
368: PropertyMapAdapter next = (PropertyMapAdapter) iter.next();
369: next.dispose();
370: }
371:
372: if (systemMenu != null) {
373: systemMenu.dispose();
374: }
375:
376: if (partList != null) {
377: partList.dispose();
378: }
379:
380: systemMenu = null;
381: partList = null;
382:
383: }
384:
385: /* (non-Javadoc)
386: * @see org.eclipse.ui.presentations.StackPresentation#setActive(int)
387: */
388: public void setActive(int newState) {
389: folder.getTabFolder().setActive(newState);
390: }
391:
392: /* (non-Javadoc)
393: * @see org.eclipse.ui.presentations.StackPresentation#setVisible(boolean)
394: */
395: public void setVisible(boolean isVisible) {
396: IPresentablePart current = getSite().getSelectedPart();
397: if (current != null) {
398: current.setVisible(isVisible);
399: }
400:
401: folder.setVisible(isVisible);
402: }
403:
404: /* (non-Javadoc)
405: * @see org.eclipse.ui.presentations.StackPresentation#setState(int)
406: */
407: public void setState(int state) {
408: folder.getTabFolder().setState(state);
409: }
410:
411: /* (non-Javadoc)
412: * @see org.eclipse.ui.presentations.StackPresentation#getControl()
413: */
414: public Control getControl() {
415: return folder.getTabFolder().getControl();
416: }
417:
418: /**
419: * @return AbstractTabFolder the presentation's tab folder
420: */
421: public AbstractTabFolder getTabFolder() {
422: return folder.getTabFolder();
423: }
424:
425: /* (non-Javadoc)
426: * @see org.eclipse.ui.presentations.StackPresentation#addPart(org.eclipse.ui.presentations.IPresentablePart, java.lang.Object)
427: */
428: public void addPart(IPresentablePart newPart, Object cookie) {
429: ignoreSelectionChanges++;
430: try {
431: if (initializing) {
432: tabs.addInitial(newPart);
433: } else {
434: if (cookie == null) {
435: tabs.add(newPart);
436: } else {
437: int insertionPoint = dragBehavior
438: .getInsertionPosition(cookie);
439:
440: tabs.insert(newPart, insertionPoint);
441: }
442: }
443: } finally {
444: ignoreSelectionChanges--;
445: }
446:
447: if (tabs.getPartList().length == 1) {
448: if (newPart.getSizeFlags(true) != 0
449: || newPart.getSizeFlags(false) != 0) {
450: getSite().flushLayout();
451: }
452: }
453: }
454:
455: /* (non-Javadoc)
456: * @see org.eclipse.ui.presentations.StackPresentation#movePart(org.eclipse.ui.presentations.IPresentablePart, java.lang.Object)
457: */
458: public void movePart(IPresentablePart toMove, Object cookie) {
459: ignoreSelectionChanges++;
460: try {
461: int insertionPoint = dragBehavior
462: .getInsertionPosition(cookie);
463:
464: if (insertionPoint == folder.indexOf(toMove)) {
465: return;
466: }
467:
468: tabs.move(toMove, insertionPoint);
469: } finally {
470: ignoreSelectionChanges--;
471: }
472: }
473:
474: /* (non-Javadoc)
475: * @see org.eclipse.ui.presentations.StackPresentation#removePart(org.eclipse.ui.presentations.IPresentablePart)
476: */
477: public void removePart(IPresentablePart oldPart) {
478: ignoreSelectionChanges++;
479: try {
480: tabs.remove(oldPart);
481: } finally {
482: ignoreSelectionChanges--;
483: }
484: }
485:
486: /* (non-Javadoc)
487: * @see org.eclipse.ui.presentations.StackPresentation#selectPart(org.eclipse.ui.presentations.IPresentablePart)
488: */
489: public void selectPart(IPresentablePart toSelect) {
490: initializing = false;
491:
492: tabs.select(toSelect);
493: }
494:
495: /* (non-Javadoc)
496: * @see org.eclipse.ui.presentations.StackPresentation#dragOver(org.eclipse.swt.widgets.Control, org.eclipse.swt.graphics.Point)
497: */
498: public StackDropResult dragOver(Control currentControl,
499: Point location) {
500: return dragBehavior.dragOver(currentControl, location,
501: dragStart);
502: }
503:
504: /**
505: * @param part
506: * @param point
507: */
508: public void showSystemMenu() {
509: showSystemMenu(folder.getTabFolder().getSystemMenuLocation(),
510: getSite().getSelectedPart());
511: }
512:
513: public void showSystemMenu(Point displayCoordinates,
514: IPresentablePart context) {
515: if (context != getSite().getSelectedPart()) {
516: getSite().selectPart(context);
517: }
518: systemMenu.show(getControl(), displayCoordinates, context);
519: }
520:
521: /* (non-Javadoc)
522: * @see org.eclipse.ui.presentations.StackPresentation#showPaneMenu()
523: */
524: public void showPaneMenu() {
525: IPresentablePart part = getSite().getSelectedPart();
526:
527: if (part != null) {
528: showPaneMenu(part, folder.getTabFolder()
529: .getPaneMenuLocation());
530: }
531: }
532:
533: public void showPaneMenu(IPresentablePart part, Point location) {
534: Assert.isTrue(!isDisposed());
535:
536: IPartMenu menu = part.getMenu();
537:
538: if (menu != null) {
539: menu.showMenu(location);
540: }
541: }
542:
543: /* (non-Javadoc)
544: * @see org.eclipse.ui.presentations.StackPresentation#getTabList(org.eclipse.ui.presentations.IPresentablePart)
545: */
546: public Control[] getTabList(IPresentablePart part) {
547: ArrayList list = new ArrayList();
548: if (folder.getTabFolder().getTabPosition() == SWT.BOTTOM) {
549: if (part.getControl() != null) {
550: list.add(part.getControl());
551: }
552: }
553:
554: list.add(folder.getTabFolder().getControl());
555:
556: if (part.getToolBar() != null) {
557: list.add(part.getToolBar());
558: }
559:
560: if (folder.getTabFolder().getTabPosition() == SWT.TOP) {
561: if (part.getControl() != null) {
562: list.add(part.getControl());
563: }
564: }
565:
566: return (Control[]) list.toArray(new Control[list.size()]);
567: }
568:
569: public void setPartList(ISystemMenu menu) {
570: this .partList = menu;
571: }
572:
573: public IDynamicPropertyMap getTheme() {
574: return themePreferences;
575: }
576:
577: public IDynamicPropertyMap getApiPreferences() {
578: return apiPreferences;
579: }
580:
581: public IDynamicPropertyMap getPluginPreferences(Plugin toQuery) {
582: String id = toQuery.getBundle().getSymbolicName();
583: IDynamicPropertyMap result = (IDynamicPropertyMap) prefs
584: .get(id);
585:
586: if (result != null) {
587: return result;
588: }
589:
590: result = new PreferencesAdapter(toQuery.getPluginPreferences());
591: prefs.put(id, result);
592: return result;
593: }
594:
595: /**
596: * Move the tabs around. This is for testing <b>ONLY</b>.
597: * @param part the part to move
598: * @param index the new index
599: * @since 3.2
600: */
601: public void moveTab(IPresentablePart part, int index) {
602: tabs.move(part, index);
603: folder.layout(true);
604: }
605:
606: /**
607: * Get the tab list. This is for testing <b>ONLY</b>.
608: * @return the presentable parts in order.
609: * @since 3.2
610: */
611: public IPresentablePart[] getPartList() {
612: return tabs.getPartList();
613: }
614:
615: /**
616: * Cause the folder to hide or show its
617: * Minimize and Maximize affordances.
618: *
619: * @param show
620: * <code>true</code> - the min/max buttons are visible.
621: * @since 3.3
622: */
623: public void showMinMax(boolean show) {
624: folder.getTabFolder().showMinMax(show);
625: }
626: }
|