001: /*******************************************************************************
002: * Copyright (c) 2006, 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: *******************************************************************************/package org.eclipse.ui.internal.menus;
011:
012: import java.util.ArrayList;
013: import java.util.HashMap;
014: import java.util.Iterator;
015: import java.util.List;
016: import java.util.Map;
017:
018: import org.eclipse.core.runtime.CoreException;
019: import org.eclipse.core.runtime.IConfigurationElement;
020: import org.eclipse.core.runtime.IStatus;
021: import org.eclipse.jface.menus.IWidget;
022: import org.eclipse.swt.SWT;
023: import org.eclipse.swt.layout.RowLayout;
024: import org.eclipse.swt.widgets.Composite;
025: import org.eclipse.ui.internal.WindowTrimProxy;
026: import org.eclipse.ui.internal.WorkbenchWindow;
027: import org.eclipse.ui.internal.layout.IWindowTrim;
028: import org.eclipse.ui.internal.layout.TrimLayout;
029: import org.eclipse.ui.internal.misc.StatusUtil;
030: import org.eclipse.ui.menus.AbstractWorkbenchTrimWidget;
031: import org.eclipse.ui.menus.IMenuService;
032: import org.eclipse.ui.menus.IWorkbenchWidget;
033: import org.eclipse.ui.statushandlers.StatusManager;
034:
035: /**
036: * <p>
037: * An implementation that supports 'trim' elements defined in using the
038: * <code>org.eclipse.ui.menus</code> extension point.
039: * </p>
040: * <p>
041: * This class is not intended to be used outside of the
042: * <code>org.eclipse.ui.workbench</code> plug-in.
043: * </p>
044: *
045: * @since 3.2
046: */
047: public class TrimBarManager2 {
048:
049: /**
050: * A List of the URI's representing the trim areas
051: */
052: private MenuLocationURI[] trimAreaURIs = {
053: new MenuLocationURI("toolbar:command1"), //$NON-NLS-1$
054: new MenuLocationURI("toolbar:command2"), //$NON-NLS-1$
055: new MenuLocationURI("toolbar:vertical1"), //$NON-NLS-1$
056: new MenuLocationURI("toolbar:vertical2"), //$NON-NLS-1$
057: new MenuLocationURI("toolbar:status"), //$NON-NLS-1$
058: };
059:
060: /**
061: * The SWT 'side' corresponding to a URI
062: */
063: int[] swtSides = { SWT.TOP, SWT.TOP, SWT.LEFT, SWT.RIGHT,
064: SWT.BOTTOM };
065: /**
066: * The window on which this menu manager exists; never <code>null</code>.
067: */
068: private STrimBuilder fTrimBuilder;
069:
070: private WorkbenchMenuService fMenuService;
071:
072: private boolean fDirty;
073:
074: /**
075: * Constructs a new instance of <code>TrimBarManager</code>.
076: *
077: * @param window
078: * The window on which this menu manager exists; must not be
079: * <code>null</code>.
080: */
081: public TrimBarManager2(final WorkbenchWindow window) {
082: if (window == null) {
083: throw new IllegalArgumentException(
084: "The window cannot be null"); //$NON-NLS-1$
085: }
086:
087: // Remember the parameters
088: fMenuService = (WorkbenchMenuService) window.getWorkbench()
089: .getService(IMenuService.class);
090: fTrimBuilder = new STrimBuilder(window);
091:
092: // New layouts are always 'dirty'
093: fDirty = true;
094: }
095:
096: /**
097: * Hacked version of the update method that allows the hiding of any trim
098: * sited at SWT.TOP. This is because the Intro management wants there to be
099: * no trim at the top but can only currently indicate this by using the
100: * CoolBar's visibility...
101: *
102: * @param force
103: * @param recursive
104: * @param hideTopTrim
105: */
106: public void update(boolean force, boolean recursive,
107: boolean hideTopTrim) {
108: if (force || isDirty()) {
109: // Re-render the trim based on the new layout
110: fTrimBuilder.build(hideTopTrim);
111: setDirty(false);
112: }
113: }
114:
115: /**
116: * Copied from the <code>MenuManager</code> method...
117: *
118: * @param force
119: * If true then do the update even if not 'dirty'
120: * @param recursive
121: * Update recursively
122: *
123: * @see org.eclipse.jface.action.MenuManager#update(boolean, boolean)
124: */
125: public void update(boolean force, boolean recursive) {
126: update(force, recursive, false);
127: }
128:
129: /**
130: * Set the dirty state of the layout
131: *
132: * @param isDirty
133: */
134: private void setDirty(boolean isDirty) {
135: fDirty = isDirty;
136: }
137:
138: /**
139: * Returns the 'dirty' state of the layout
140: *
141: * @return Always returns 'true' for now
142: */
143: private boolean isDirty() {
144: return fDirty;
145: }
146:
147: /**
148: * This is a convenience class that maintains the list of the widgets in the
149: * group. This allows any position / orientation changes to the group to be
150: * passed on to all the widgets for that group.
151: *
152: * @since 3.2
153: *
154: */
155: private class TrimWidgetProxy extends WindowTrimProxy {
156:
157: private List widgets;
158: private TrimAdditionCacheEntry cacheEntry;
159: private int originalSide;
160: private int curSide;
161:
162: private Composite parent;
163:
164: /**
165: * Constructor that takes in any information necessary to implement an
166: * IWindowTrim and also has enough state to manage a group with multiple
167: * IWidget contributions.
168: *
169: */
170: public TrimWidgetProxy(List widgets, int side,
171: Composite parent, TrimAdditionCacheEntry entry,
172: boolean resizeable) {
173: super (parent, entry.getId(), entry.getId(), SWT.TOP
174: | SWT.BOTTOM | SWT.LEFT | SWT.RIGHT, resizeable);
175:
176: // Remember our widget structure
177: this .widgets = widgets;
178: this .curSide = side;
179: this .originalSide = side;
180: this .parent = parent;
181: this .cacheEntry = entry;
182: }
183:
184: /*
185: * (non-Javadoc)
186: *
187: * @see org.eclipse.ui.internal.WindowTrimProxy#dock(int)
188: */
189: public void dock(int newSide) {
190: // out with the old...
191: for (Iterator iter = widgets.iterator(); iter.hasNext();) {
192: IWidget widget = (IWidget) iter.next();
193: widget.dispose();
194:
195: cacheEntry.removeWidget(widget);
196: }
197:
198: // ...in with the new
199: for (Iterator iter = widgets.iterator(); iter.hasNext();) {
200: IWorkbenchWidget widget = (IWorkbenchWidget) iter
201: .next();
202: if (widget instanceof AbstractWorkbenchTrimWidget)
203: ((AbstractWorkbenchTrimWidget) widget).fill(parent,
204: curSide, newSide);
205: else
206: widget.fill(parent);
207: }
208:
209: curSide = newSide;
210: parent.layout();
211: }
212:
213: /**
214: * Disposes all the widgets contributed into this group and then
215: * disposes the group's 'proxy' control
216: */
217: public void dispose() {
218: for (Iterator iter = widgets.iterator(); iter.hasNext();) {
219: IWidget widget = (IWidget) iter.next();
220: widget.dispose();
221:
222: // Remove the IWidget from the entry's cache
223: cacheEntry.removeWidget(widget);
224: }
225:
226: getControl().dispose();
227: }
228:
229: /**
230: * @return The side that the trim was declared to be on
231: */
232: public int getSide() {
233: return originalSide;
234: }
235:
236: /**
237: * @return Whether this addition is at the start or end of the
238: * containing trim area
239: */
240: public boolean isAtStart() {
241: //Delegate to the cache entry
242: return cacheEntry.isAtStart();
243: }
244: }
245:
246: /**
247: * A convenience class that implements the 'rendering' code necessary to
248: * turn the contributions to the 'trim' bar into actual SWT controls.
249: *
250: * @since 3.2
251: *
252: */
253: private class STrimBuilder {
254: /**
255: * The WorkbenchWindow that this builder is for
256: */
257: private WorkbenchWindow fWindow;
258:
259: /**
260: * The list of <code>WindowTrimProxy</code> elements currently
261: * rendered in the WorkbenchWindow. Used to support the update mechanism
262: * (specifically, it's needed to implement the <code>tearDown</code>
263: * method).
264: */
265: private List curGroups = new ArrayList();
266:
267: /**
268: * Map to cache which trim has already been initialized
269: */
270: private Map initializedTrim = new HashMap();
271:
272: /**
273: * Construct a trim builder for the given WorkbenchWindow
274: *
275: * @param window
276: * The WorkbenchWindow to render the trim on
277: */
278: public STrimBuilder(WorkbenchWindow window) {
279: fWindow = window;
280: }
281:
282: /**
283: * Remove any rendered trim. This method will always be directly
284: * followed by a call to the 'build' method to update the contents.
285: */
286: public void tearDown() {
287: // First, remove all trim
288: for (Iterator iter = curGroups.iterator(); iter.hasNext();) {
289: TrimWidgetProxy proxy = (TrimWidgetProxy) iter.next();
290: fWindow.getTrimManager().removeTrim(proxy);
291:
292: try {
293: proxy.dispose();
294: } catch (Throwable e) {
295: IStatus status = null;
296: if (e instanceof CoreException) {
297: status = ((CoreException) e).getStatus();
298: } else {
299: status = StatusUtil
300: .newStatus(
301: IStatus.ERROR,
302: "Internal plug-in widget delegate error on dispose.", e); //$NON-NLS-1$
303: }
304: StatusUtil
305: .handleStatus(
306: status,
307: "widget delegate failed on dispose: id = " + proxy.getId(), StatusManager.LOG); //$NON-NLS-1$
308: }
309: }
310:
311: // Clear out the old list
312: curGroups.clear();
313: }
314:
315: /**
316: * Construct the trim based on the contributions.
317: *
318: * @param layout
319: * The new layout information
320: * @param hideTopTrim
321: * <code>true</code> iff we don't want to display trim
322: * contributed into the SWT.TOP area. This is because the
323: * 'Intro' View hides the CBanner (and so, presumably, also
324: * wants to not show any other trim at the top.
325: *
326: * @param window
327: * The widnow to 'render' the trim into
328: *
329: */
330: public void build(boolean hideTopTrim) {
331: tearDown();
332:
333: for (int i = 0; i < trimAreaURIs.length; i++) {
334: processAdditions(trimAreaURIs[i], hideTopTrim);
335: }
336: }
337:
338: /**
339: * @param menuLocationURI
340: * @param hideTopTrim
341: */
342: private void processAdditions(MenuLocationURI trimURI,
343: boolean hideTopTrim) {
344: List additions = fMenuService.getAdditionsForURI(trimURI);
345: if (additions.size() == 0)
346: return;
347:
348: int swtSide = getSide(trimURI);
349:
350: // Dont show trim on the top if it's 'hidden'
351: if (swtSide == SWT.TOP && hideTopTrim)
352: return;
353:
354: // Each trim addition represents a 'group' into which one or more
355: // widgets can be placed...
356: for (Iterator iterator = additions.iterator(); iterator
357: .hasNext();) {
358: TrimAdditionCacheEntry trimEntry = (TrimAdditionCacheEntry) iterator
359: .next();
360: String groupId = trimEntry.getId();
361:
362: // Get the list of IConfgurationElements representing
363: // widgets in this group
364: List widgets = trimEntry.getWidgets();
365: if (widgets.size() == 0)
366: continue;
367:
368: // Create a 'container' composite for the group
369: Composite grpComposite = new Composite(fWindow
370: .getShell(), SWT.NONE);
371: grpComposite.setToolTipText(groupId);
372:
373: // Create the layout for the 'group' container...-no- border margins
374: RowLayout rl = new RowLayout();
375: rl.marginBottom = rl.marginHeight = rl.marginLeft = rl.marginRight = rl.marginTop = rl.marginWidth = 0;
376: grpComposite.setLayout(rl);
377:
378: // keep track of whether -any- of the widgets are resizeable
379: boolean resizeable = false;
380:
381: for (Iterator widgetIter = widgets.iterator(); widgetIter
382: .hasNext();) {
383: IWorkbenchWidget widget = (IWorkbenchWidget) widgetIter
384: .next();
385: IConfigurationElement widgetElement = trimEntry
386: .getElement(widget);
387: if (widget != null) {
388: resizeable |= trimEntry
389: .fillMajor(widgetElement);
390: renderTrim(grpComposite, widget, swtSide);
391: }
392: }
393:
394: // Create the trim proxy for this group
395: TrimWidgetProxy groupTrimProxy = new TrimWidgetProxy(
396: widgets, swtSide, grpComposite, trimEntry,
397: resizeable);
398: curGroups.add(groupTrimProxy);
399:
400: // 'Site' the group in its default location
401: placeGroup(groupTrimProxy);
402: }
403: }
404:
405: private void placeGroup(final TrimWidgetProxy proxy) {
406: // Get the placement parameters
407: final int side = proxy.getSide();
408: boolean atStart = proxy.isAtStart();
409:
410: // Place the trim before any other trim if it's
411: // at the 'start'; otherwise place it at the end
412: IWindowTrim beforeMe = null;
413: if (atStart) {
414: List trim = fWindow.getTrimManager().getAreaTrim(side);
415: if (trim.size() > 0)
416: beforeMe = (IWindowTrim) trim.get(0);
417: }
418:
419: // Add the group into trim...safely
420: try {
421: proxy.dock(side); // ensure that the widgets are properly oriented
422: TrimLayout tl = (TrimLayout) fWindow.getShell()
423: .getLayout();
424: tl.addTrim(side, proxy, beforeMe);
425: } catch (Throwable e) {
426: IStatus status = null;
427: if (e instanceof CoreException) {
428: status = ((CoreException) e).getStatus();
429: } else {
430: status = StatusUtil
431: .newStatus(
432: IStatus.ERROR,
433: "Internal plug-in widget delegate error on dock.", e); //$NON-NLS-1$
434: }
435: StatusUtil
436: .handleStatus(
437: status,
438: "widget delegate failed on dock: id = " + proxy.getId(), //$NON-NLS-1$
439: StatusManager.LOG);
440: }
441: }
442:
443: /**
444: * Render a particular SWidget into a given group
445: *
446: * @param groupComposite
447: * The parent to create the widgets under
448: * @param widget
449: * The SWidget to render
450: * @param side
451: */
452: private void renderTrim(final Composite groupComposite,
453: IWidget iw, final int side) {
454: // OK, fill the widget
455: if (iw != null) {
456: // The -first- time trim is displayed we'll initialize it
457: if (iw instanceof IWorkbenchWidget
458: && initializedTrim.get(iw) == null) {
459: IWorkbenchWidget iww = (IWorkbenchWidget) iw;
460: iww.init(fWindow);
461: initializedTrim.put(iw, iw);
462: }
463:
464: if (iw instanceof AbstractWorkbenchTrimWidget)
465: ((AbstractWorkbenchTrimWidget) iw).fill(
466: groupComposite, SWT.DEFAULT, side);
467: else
468: iw.fill(groupComposite);
469: }
470: }
471:
472: private int getSide(MenuLocationURI uri) {
473: for (int i = 0; i < trimAreaURIs.length; i++) {
474: if (trimAreaURIs[i].getRawString().equals(
475: uri.getRawString()))
476: return swtSides[i];
477: }
478: return SWT.BOTTOM;
479: }
480:
481: /**
482: * Reposition any contributed trim whose id is -not- a 'knownId'. If the
483: * id is known then the trim has already been positioned from the stored
484: * workbench state. If it isn't then it's a new contribution whose
485: * default position may have been trashed by the WorkbenchWindow's
486: * 'restoreState' handling.
487: *
488: * @param knownIds
489: * A List of strings containing the ids of any trim that was
490: * explicitly positioned during the restore state.
491: */
492: public void updateLocations(List knownIds) {
493: for (Iterator iter = curGroups.iterator(); iter.hasNext();) {
494: TrimWidgetProxy proxy = (TrimWidgetProxy) iter.next();
495: if (!knownIds.contains(proxy.getId())) {
496: placeGroup(proxy);
497: }
498: }
499: }
500: }
501:
502: /**
503: * Updates the placement of any contributed trim that is -not- in the
504: * 'knownIds' list (which indicates that it has already been placed using
505: * cached workspace data.
506: *
507: * Forward on to the bulder for implementation
508: */
509: public void updateLocations(List knownIds) {
510: fTrimBuilder.updateLocations(knownIds);
511: }
512:
513: /**
514: * unhook the menu service.
515: */
516: public void dispose() {
517: fMenuService = null;
518: fTrimBuilder = null;
519: }
520: }
|