001: /*******************************************************************************
002: * Copyright (c) 2004, 2006 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.Iterator;
016: import java.util.List;
017:
018: import org.eclipse.core.runtime.Assert;
019: import org.eclipse.jface.util.Geometry;
020: import org.eclipse.swt.SWT;
021: import org.eclipse.swt.events.ControlEvent;
022: import org.eclipse.swt.events.ControlListener;
023: import org.eclipse.swt.events.DisposeEvent;
024: import org.eclipse.swt.events.DisposeListener;
025: import org.eclipse.swt.events.ShellAdapter;
026: import org.eclipse.swt.events.ShellEvent;
027: import org.eclipse.swt.events.ShellListener;
028: import org.eclipse.swt.graphics.Point;
029: import org.eclipse.swt.graphics.Rectangle;
030: import org.eclipse.swt.widgets.Composite;
031: import org.eclipse.swt.widgets.Control;
032: import org.eclipse.ui.IPropertyListener;
033: import org.eclipse.ui.internal.dnd.DragUtil;
034: import org.eclipse.ui.internal.dnd.SwtUtil;
035: import org.eclipse.ui.presentations.IPresentablePart;
036: import org.eclipse.ui.presentations.IStackPresentationSite;
037:
038: /**
039: * @since 3.1
040: */
041: public final class PresentablePartFolder implements
042: IPresentablePartList {
043: private AbstractTabFolder folder;
044: private IPresentablePart current;
045: //private ProxyControl toolbarProxy;
046: private Control contentProxy;
047: private static PartInfo tempPartInfo = new PartInfo();
048:
049: /**
050: * Movement listener. Updates the bounds of the target to match the
051: * bounds of the dummy control.
052: */
053: private ControlListener contentListener = new ControlListener() {
054:
055: public void controlMoved(ControlEvent e) {
056: layoutContent();
057: }
058:
059: public void controlResized(ControlEvent e) {
060: }
061:
062: };
063:
064: private ShellListener shellListener = new ShellAdapter() {
065: public void shellActivated(ShellEvent e) {
066: folder.shellActive(true);
067: }
068:
069: public void shellDeactivated(ShellEvent e) {
070: folder.shellActive(false);
071: }
072: };
073:
074: /**
075: * Listener attached to all child parts. It responds to changes in part properties
076: */
077: private IPropertyListener childPropertyChangeListener = new IPropertyListener() {
078: public void propertyChanged(Object source, int property) {
079:
080: if (source instanceof IPresentablePart) {
081: IPresentablePart part = (IPresentablePart) source;
082:
083: childPropertyChanged(part, property);
084: }
085: }
086: };
087:
088: /**
089: * Dispose listener that is attached to the main control. It triggers cleanup of
090: * any listeners. This is required to prevent memory leaks.
091: */
092: private DisposeListener tabDisposeListener = new DisposeListener() {
093: public void widgetDisposed(DisposeEvent e) {
094: if (e.widget == folder.getControl()) {
095: // If we're disposing the main control...
096: disposed();
097: }
098: }
099: };
100: private List partList = new ArrayList(4);
101:
102: public PresentablePartFolder(AbstractTabFolder folder) {
103: this .folder = folder;
104:
105: folder.getControl().getShell().addShellListener(shellListener);
106: folder.shellActive(folder.getControl().getDisplay()
107: .getActiveShell() == folder.getControl().getShell());
108:
109: folder.getControl().addDisposeListener(tabDisposeListener);
110:
111: //toolbarProxy = new ProxyControl(folder.getToolbarParent());
112:
113: // NOTE: if the shape of contentProxy changes, the fix for bug 85899 in EmptyTabFolder.computeSize may need adjustment.
114: contentProxy = new Composite(folder.getContentParent(),
115: SWT.NONE);
116: contentProxy.setVisible(false);
117: for (Control current = contentProxy; current != folder
118: .getControl().getParent(); current = current
119: .getParent()) {
120: current.addControlListener(contentListener);
121: }
122: folder.setContent(contentProxy);
123:
124: }
125:
126: /**
127: *
128: * @since 3.1
129: */
130: private void layoutContent() {
131: if (current != null) {
132: Rectangle clientArea = DragUtil
133: .getDisplayBounds(contentProxy);
134:
135: current.setBounds(Geometry.toControl(folder.getControl()
136: .getParent(), clientArea));
137: }
138: }
139:
140: /**
141: *
142: * @since 3.1
143: */
144: protected void disposed() {
145: folder.getControl().getShell().removeShellListener(
146: shellListener);
147: Iterator iter = partList.iterator();
148: while (iter.hasNext()) {
149: IPresentablePart next = (IPresentablePart) iter.next();
150:
151: next.removePropertyListener(childPropertyChangeListener);
152: }
153: }
154:
155: /* (non-Javadoc)
156: * @see org.eclipse.ui.internal.presentations.util.IPresentablePartList#getPartList()
157: */
158: public IPresentablePart[] getPartList() {
159: AbstractTabItem[] items = folder.getItems();
160: IPresentablePart[] result = new IPresentablePart[items.length];
161:
162: for (int i = 0; i < items.length; i++) {
163: result[i] = getPartForTab(items[i]);
164:
165: }
166:
167: return result;
168: }
169:
170: /**
171: * Adds the given presentable part directly into this presentation at the
172: * given index. Does nothing if a tab already exists for the given part.
173: * This is intended to be called by TabOrder and its subclasses.
174: *
175: * @param part part to add
176: * @param idx index to insert at
177: */
178: public void insert(IPresentablePart part, int idx) {
179: Assert.isTrue(!folder.getControl().isDisposed());
180:
181: if (getTab(part) != null) {
182: if (indexOf(part) != idx)
183: move(part, idx);
184: return;
185: }
186:
187: idx = Math.min(idx, folder.getItemCount());
188:
189: AbstractTabItem item;
190:
191: int style = SWT.NONE;
192:
193: if (part.isCloseable()) {
194: style |= SWT.CLOSE;
195: }
196:
197: item = folder.add(idx, style);
198:
199: item.setData(part);
200:
201: initTab(item, part);
202:
203: part.addPropertyListener(childPropertyChangeListener);
204: partList.add(part);
205: }
206:
207: public void remove(IPresentablePart toRemove) {
208: if (toRemove == current) {
209: select(null);
210: }
211:
212: internalRemove(toRemove);
213: }
214:
215: private void internalRemove(IPresentablePart toRemove) {
216: AbstractTabItem item = getTab(toRemove);
217: if (item != null) {
218: item.dispose();
219: }
220: if (partList.contains(toRemove)) {
221: toRemove
222: .removePropertyListener(childPropertyChangeListener);
223: partList.remove(toRemove);
224: }
225: }
226:
227: /**
228: * Moves the given part to the given index. When this method returns,
229: * indexOf(part) will return newIndex.
230: *
231: * @param part
232: * @param newIndex
233: */
234: public void move(IPresentablePart part, int newIndex) {
235: int currentIndex = indexOf(part);
236:
237: if (currentIndex == newIndex) {
238: return;
239: }
240:
241: internalRemove(part);
242: insert(part, newIndex);
243:
244: if (current == part) {
245: folder.setSelection(getTab(part));
246: }
247: }
248:
249: /**
250: * Returns the number of parts in this folder
251: */
252: public int size() {
253: return folder.getItemCount();
254: }
255:
256: public void setBounds(Rectangle bounds) {
257: Point minSize = folder.computeSize(bounds.width, SWT.DEFAULT);
258:
259: if (folder.getState() == IStackPresentationSite.STATE_MINIMIZED
260: && minSize.y < bounds.height) {
261: bounds = Geometry.copy(bounds);
262: bounds.height = minSize.y;
263: }
264:
265: // Set the tab folder's bounds
266: folder.getControl().setBounds(bounds);
267:
268: layout(false);
269: }
270:
271: public void select(IPresentablePart toSelect) {
272:
273: if (toSelect == current) {
274: return;
275: }
276:
277: if (toSelect != null) {
278: toSelect.setVisible(true);
279: }
280:
281: if (current != null) {
282: current.setVisible(false);
283: }
284:
285: current = toSelect;
286:
287: AbstractTabItem selectedItem = getTab(toSelect);
288:
289: folder.setSelection(selectedItem);
290:
291: if (selectedItem != null) {
292: // Determine if we need to un-bold this tab
293: selectedItem.setBold(false);
294: initTab(selectedItem, toSelect);
295: } else {
296: setToolbar(null);
297: }
298:
299: layout(true);
300: }
301:
302: private void setToolbar(Control newToolbar) {
303: if (folder.getToolbar() != newToolbar) {
304: folder.setToolbar(newToolbar);
305: }
306: }
307:
308: private boolean isVisible = true;
309:
310: private void childPropertyChanged(IPresentablePart part,
311: int property) {
312: AbstractTabItem tab = getTab(part);
313:
314: switch (property) {
315: case IPresentablePart.PROP_HIGHLIGHT_IF_BACK:
316: if (tab != null && getCurrent() != part) {//Set bold if it doesn't currently have focus
317: tab.setBold(true);
318: initTab(tab, part);
319: }
320: break;
321:
322: case IPresentablePart.PROP_TOOLBAR:
323: if (tab != null && getCurrent() == part) {
324: folder.flushToolbarSize();
325: }
326: /* falls through */
327: case IPresentablePart.PROP_CONTENT_DESCRIPTION:
328: case IPresentablePart.PROP_PANE_MENU:
329: case IPresentablePart.PROP_TITLE:
330: if (tab != null) {
331: initTab(tab, part);
332: if (getCurrent() == part) {
333: layout(true);
334: }
335: }
336: break;
337: case IPresentablePart.PROP_PREFERRED_SIZE:
338: folder.fireEvent(new TabFolderEvent(
339: TabFolderEvent.EVENT_PREFERRED_SIZE, tab, 0, 0));
340: break;
341: default:
342: if (tab != null)
343: initTab(tab, part);
344: }
345: }
346:
347: protected void initTab(AbstractTabItem item, IPresentablePart part) {
348: tempPartInfo.set(part);
349: item.setInfo(tempPartInfo);
350:
351: item.setBusy(part.isBusy());
352: if (part == getCurrent()) {
353: folder.setSelectedInfo(tempPartInfo);
354: folder.enablePaneMenu(part.getMenu() != null);
355:
356: setToolbar(part.getToolBar());
357: }
358: }
359:
360: public boolean isDisposed() {
361: return SwtUtil.isDisposed(folder.getControl());
362: }
363:
364: public IPresentablePart getPartForTab(AbstractTabItem tab) {
365: Assert.isTrue(!isDisposed());
366:
367: if (tab == null) {
368: return null;
369: }
370:
371: IPresentablePart part = (IPresentablePart) tab.getData();
372:
373: return part;
374: }
375:
376: /**
377: * Returns the tab for the given part, or null if there is no such tab
378: *
379: * @param part the part being searched for
380: * @return the tab for the given part, or null if there is no such tab
381: */
382: public AbstractTabItem getTab(IPresentablePart part) {
383: Assert.isTrue(!isDisposed());
384:
385: return folder.findItem(part);
386: }
387:
388: public int indexOf(IPresentablePart part) {
389: AbstractTabItem item = getTab(part);
390:
391: if (item == null) {
392: return -1;
393: }
394:
395: return folder.indexOf(item);
396: }
397:
398: public AbstractTabFolder getTabFolder() {
399: return folder;
400: }
401:
402: public void setVisible(boolean isVisible) {
403: this .isVisible = isVisible;
404: getTabFolder().setVisible(isVisible);
405: if (isVisible) {
406: layout(true);
407: }
408: }
409:
410: public void layout(boolean changed) {
411: if (!isVisible) {
412: // Don't bother with layout if we're not visible
413: return;
414: }
415: // Lay out the tab folder and compute the client area
416: folder.layout(changed);
417:
418: //toolbarProxy.layout();
419:
420: layoutContent();
421: }
422:
423: public IPresentablePart getCurrent() {
424: return current;
425: }
426: }
|