KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > internal > menus > TrimBarManager


1 /*******************************************************************************
2  * Copyright (c) 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  ******************************************************************************/

11
12 package org.eclipse.ui.internal.menus;
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.HashMap JavaDoc;
16 import java.util.HashSet JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.List JavaDoc;
19 import java.util.Map JavaDoc;
20 import java.util.Set JavaDoc;
21
22 import org.eclipse.core.commands.common.NotDefinedException;
23 import org.eclipse.core.runtime.CoreException;
24 import org.eclipse.core.runtime.IStatus;
25 import org.eclipse.jface.menus.IWidget;
26 import org.eclipse.swt.SWT;
27 import org.eclipse.swt.layout.RowLayout;
28 import org.eclipse.swt.widgets.Composite;
29 import org.eclipse.ui.internal.WindowTrimProxy;
30 import org.eclipse.ui.internal.WorkbenchPlugin;
31 import org.eclipse.ui.internal.WorkbenchWindow;
32 import org.eclipse.ui.internal.layout.IWindowTrim;
33 import org.eclipse.ui.internal.layout.TrimLayout;
34 import org.eclipse.ui.internal.misc.StatusUtil;
35 import org.eclipse.ui.menus.IWorkbenchWidget;
36
37 /**
38  * <p>
39  * An implementation that supports 'trim' elements defined in using the
40  * <code>org.eclipse.ui.menus</code> extension point.
41  * </p>
42  * <p>
43  * This class is not intended to be used outside of the
44  * <code>org.eclipse.ui.workbench</code> plug-in.
45  * </p>
46  *
47  * @since 3.2
48  */

49 public class TrimBarManager {
50
51     /**
52      * The window on which this menu manager exists; never <code>null</code>.
53      */

54     private STrimBuilder fTrimBuilder;
55
56     private IMenuService fMenuService;
57
58     private boolean fDirty;
59
60     /**
61      * Constructs a new instance of <code>TrimBarManager</code>.
62      *
63      * @param window
64      * The window on which this menu manager exists; must not be
65      * <code>null</code>.
66      */

67     public TrimBarManager(final WorkbenchWindow window) {
68         if (window == null) {
69             throw new IllegalArgumentException JavaDoc("The window cannot be null"); //$NON-NLS-1$
70
}
71
72         // Remember the parameters
73
fMenuService = (IMenuService) window.getService(IMenuService.class);
74         fTrimBuilder = new STrimBuilder(window);
75
76         // New layouts are always 'dirty'
77
fDirty = true;
78     }
79
80     /**
81      * Hacked version of the update method that allows the hiding of any trim
82      * sited at SWT.TOP. This is because the Intro management wants there to be
83      * no trim at the top but can only currently indicate this by using the
84      * CoolBar's visibility...
85      *
86      * @param force
87      * @param recursive
88      * @param hideTopTrim
89      */

90     public void update(boolean force, boolean recursive, boolean hideTopTrim) {
91         if (force || isDirty()) {
92             // Re-render the trim based on the new layout
93
SMenuLayout layout = fMenuService.getLayout();
94             fTrimBuilder.build(layout, hideTopTrim);
95             setDirty(false);
96         }
97     }
98
99     /**
100      * Copied from the <code>MenuManager</code> method...
101      *
102      * @param force
103      * If true then do the update even if not 'dirty'
104      * @param recursive
105      * Update recursively
106      *
107      * @see org.eclipse.jface.action.MenuManager#update(boolean, boolean)
108      */

109     public void update(boolean force, boolean recursive) {
110         update(force, recursive, false);
111     }
112
113     /**
114      * Set the dirty state of the layout
115      *
116      * @param isDirty
117      */

118     private void setDirty(boolean isDirty) {
119         fDirty = isDirty;
120     }
121
122     /**
123      * Returns the 'dirty' state of the layout
124      *
125      * @return Always returns 'true' for now
126      */

127     private boolean isDirty() {
128         return fDirty;
129     }
130
131     /**
132      * This is a convenience class that maintains the list of the widgets in the
133      * group. This allows any position / orientation changes to the group to be
134      * passed on to all the widgets for that group.
135      *
136      * @since 3.2
137      *
138      */

139     private class TrimWidgetProxy extends WindowTrimProxy {
140
141         private List JavaDoc widgets;
142
143         private int curSide;
144
145         private Composite parent;
146
147         private SGroup group;
148
149         /**
150          * Constructor that takes in any information necessary to implement an
151          * IWindowTrim and also has enoughstate to manage a group with multiple
152          * IWidget contributions.
153          *
154          * @param widgets
155          * The list of IWidget instances representing the elements of
156          * this group.
157          * @param side
158          * The SWT side that the trim will initially be placed on
159          * @param parent
160          * The group's composite (and the parent of its widgets)
161          * @param id
162          * The id of the group
163          * @param displayName
164          * The display name of the group
165          * @param validSides
166          * Which SWT sides this trim can be moved to
167          * @param resizeable
168          * <code>true</code> iff the group contains at least one
169          * widget that can use extra space in the trim.
170          */

171         public TrimWidgetProxy(List JavaDoc widgets, int side, Composite parent,
172                 SGroup group, int validSides, boolean resizeable) {
173             super(parent, group.getId(), group.getId(), validSides, resizeable);
174
175             // Remember our widget structure
176
this.widgets = widgets;
177             this.curSide = side;
178             this.parent = parent;
179             this.group = group;
180         }
181
182         /*
183          * (non-Javadoc)
184          *
185          * @see org.eclipse.ui.internal.WindowTrimProxy#dock(int)
186          */

187         public void dock(int newSide) {
188             // out with the old...
189
for (Iterator JavaDoc iter = widgets.iterator(); iter.hasNext();) {
190                 IWidget widget = (IWidget) iter.next();
191                 widget.dispose();
192             }
193
194             // ...in with the new
195
for (Iterator JavaDoc iter = widgets.iterator(); iter.hasNext();) {
196                 WidgetProxy widget = (WidgetProxy) iter.next();
197                 widget.fill(parent, curSide, newSide);
198             }
199
200             curSide = newSide;
201
202             parent.layout();
203         }
204
205         public SGroup getGroup() {
206             return group;
207         }
208
209         /**
210          * Disposes all the widgets contributed into this group and then
211          * disposes the group's 'proxy' control
212          */

213         public void dispose() {
214             for (Iterator JavaDoc iter = widgets.iterator(); iter.hasNext();) {
215                 IWidget widget = (IWidget) iter.next();
216                 widget.dispose();
217             }
218
219             getControl().dispose();
220         }
221     }
222
223     /**
224      * A convenience class that implements the 'rendering' code necessary to
225      * turn the contributions to the 'trim' bar into actual SWT controls.
226      *
227      * @since 3.2
228      *
229      */

230     private class STrimBuilder {
231         /**
232          * The WorkbenchWindow that this builder is for
233          */

234         private WorkbenchWindow fWindow;
235         
236         /**
237          * Map of trim that fails during the trim 'fill' handling.
238          * This is used to prevent multiple error dialogs from appearing
239          * during a single session.
240          */

241         private Set JavaDoc bogusTrim = new HashSet JavaDoc();
242
243         /**
244          * The list of <code>WindowTrimProxy</code> elements currently
245          * rendered in the WorkbenchWindow. Used to support the update mechanism
246          * (specifically, it's needed to implement the <code>tearDown</code>
247          * method).
248          */

249         private List JavaDoc curGroups = new ArrayList JavaDoc();
250
251         /**
252          * Map to cache which trim has already been initialized
253          */

254         private Map JavaDoc initializedTrim = new HashMap JavaDoc();
255         
256         /**
257          * Construct a trim builder for the given WorkbenchWindow
258          *
259          * @param window
260          * The WorkbenchWindow to render the trim on
261          */

262         public STrimBuilder(WorkbenchWindow window) {
263             fWindow = window;
264         }
265
266         /**
267          * Remove any rendered trim. This method will always be directly
268          * followed by a call to the 'build' method to update the contents.
269          */

270         public void tearDown() {
271             // First, remove all trim
272
for (Iterator JavaDoc iter = curGroups.iterator(); iter.hasNext();) {
273                 TrimWidgetProxy proxy = (TrimWidgetProxy) iter.next();
274                 fWindow.getTrimManager().removeTrim(proxy);
275
276                 proxy.dispose();
277             }
278
279             // Clear out the old list
280
curGroups.clear();
281         }
282
283         /**
284          * Construct the trim based on the contributions.
285          *
286          * @param layout
287          * The new layout information
288          * @param hideTopTrim
289          * <code>true</code> iff we don't want to display trim
290          * contributed into the SWT.TOP area. This is because the
291          * 'Intro' View hides the CBanner (and so, presumably, also
292          * wants to not show any other trim at the top.
293          *
294          * @param window
295          * The widnow to 'render' the trim into
296          *
297          */

298         public void build(SMenuLayout layout, boolean hideTopTrim) {
299             tearDown();
300
301             // Get all 'trim' related info
302
ILayoutNode trimInfo = layout.getBar(SBar.TYPE_TRIM);
303
304             // Walk the layout tree 'rendering' its elements
305
List JavaDoc kids = trimInfo.getChildrenSorted();
306             for (Iterator JavaDoc iter = kids.iterator(); iter.hasNext();) {
307                 ILayoutNode node = (ILayoutNode) iter.next();
308                 MenuElement element = node.getMenuElement();
309                 if (element instanceof SWidget) {
310                     // SWidget widget = (SWidget) element;
311
// renderTrim(null, widget, SWT.BOTTOM);
312
} else if (element instanceof SGroup) {
313                     // Only render the top trim if
314
renderGroup(node, hideTopTrim);
315                 }
316             }
317         }
318
319         /**
320          * Create a composite to contain the group and then call the 'fill' for
321          * any 'trim' widgets located in that group.
322          *
323          * @param groupNode
324          * The layout node representing the group
325          */

326         private void renderGroup(ILayoutNode groupNode, boolean hideTopTrim) {
327             SGroup group = (SGroup) groupNode.getMenuElement();
328             if ("testGroup".equals(group.getId())) { //$NON-NLS-1$
329
int i = 0;
330                 i++;
331             }
332             List JavaDoc kids = groupNode.getChildrenSorted();
333
334             // Don't show empty groups
335
if (kids.size() == 0)
336                 return;
337
338             int side = getSide(group);
339
340             if (hideTopTrim && side == SWT.TOP)
341                 return;
342
343             // Create a 'container' composite for the group
344
Composite grpComposite = new Composite(fWindow.getShell(), SWT.NONE);
345             grpComposite.setToolTipText(group.getId());
346             
347             // Create the layout for the 'group' container...-no- border margins
348
RowLayout rl = new RowLayout();
349             rl.marginBottom = rl.marginHeight = rl.marginLeft = rl.marginRight = rl.marginTop = rl.marginWidth = 0;
350             grpComposite.setLayout(rl);
351
352             // are -any- of the widgets resizeable?
353
boolean resizeable = false;
354
355             // Walk the layout tree 'rendering' its trim elements
356
List JavaDoc groupWidgets = new ArrayList JavaDoc();
357             for (Iterator JavaDoc iter = kids.iterator(); iter.hasNext();) {
358                 ILayoutNode node = (ILayoutNode) iter.next();
359                 MenuElement element = node.getMenuElement();
360                 if (element instanceof SWidget) {
361                     SWidget sWidget = (SWidget) element;
362
363                     // update the resizeable state for each widget
364
resizeable = resizeable || isResizeable(sWidget);
365
366                     // Add any successful defs into the group's list
367
IWidget iw = renderTrim(grpComposite, sWidget, side);
368                     if (iw != null)
369                         groupWidgets.add(iw);
370                 } else if (element instanceof SGroup) {
371                     // No sub-group support
372
}
373             }
374
375             // Create the trim proxy for this group
376
TrimWidgetProxy groupTrimProxy = new TrimWidgetProxy(groupWidgets,
377                     side, grpComposite, group, SWT.TOP | SWT.BOTTOM | SWT.LEFT
378                             | SWT.RIGHT, resizeable);
379             curGroups.add(groupTrimProxy);
380
381             // 'Site' the group in its default location
382
placeGroup(groupTrimProxy);
383         }
384
385         private void placeGroup(final TrimWidgetProxy proxy) {
386             // Get the placement parameters
387
final int side = getSide(proxy.getGroup());
388             boolean atStart = isAtStart(proxy.getGroup());
389
390             // Place the trim before any other trim if it's
391
// at the 'start'; otherwise place it at the end
392
IWindowTrim beforeMe = null;
393             if (atStart) {
394                 List JavaDoc trim = fWindow.getTrimManager().getAreaTrim(side);
395                 if (trim.size() > 0)
396                     beforeMe = (IWindowTrim) trim.get(0);
397             }
398
399             // Add the group into trim...safely
400
try {
401                 proxy.dock(side); // ensure that the widgets are properly oriented
402
TrimLayout tl = (TrimLayout) fWindow.getShell().getLayout();
403                 tl.addTrim(side, proxy, beforeMe);
404             } catch (Throwable JavaDoc e) {
405                 IStatus status = null;
406                 if (e instanceof CoreException) {
407                     status = ((CoreException) e).getStatus();
408                 } else {
409                     status = StatusUtil
410                             .newStatus(
411                                     IStatus.ERROR,
412                                     "Internal plug-in widget delegate error on dock.", e); //$NON-NLS-1$
413
}
414                 WorkbenchPlugin
415                         .log(
416                                 "widget delegate failed on dock: id = " + proxy.getId(), status); //$NON-NLS-1$
417
}
418         }
419
420         /**
421          * Render a particular SWidget into a given group
422          *
423          * @param groupComposite
424          * The parent to create the widgets under
425          * @param widget
426          * The SWidget to render
427          * @param side
428          */

429         private IWidget renderTrim(final Composite groupComposite, final SWidget widget,
430                 final int side) {
431             // Have we already tried (and failed) to load this??
432
if (bogusTrim.contains(widget))
433                 return null;
434             
435             // OK, fill the widget
436
IWidget iw = null;
437             try {
438                 iw = widget.getWidget();
439                 if (iw != null) {
440                     // The -first- time trim is displayed we'll initialize it
441
if (iw instanceof IWorkbenchWidget && initializedTrim.get(iw) == null) {
442                         IWorkbenchWidget iww = (IWorkbenchWidget) iw;
443                         iww.init(fWindow);
444                         initializedTrim.put(iw, iw);
445                     }
446                     
447                     if (iw instanceof WidgetProxy)
448                         ((WidgetProxy) iw).fill(groupComposite, SWT.DEFAULT, side);
449                     else
450                         iw.fill(groupComposite);
451                 }
452             } catch (Throwable JavaDoc e) {
453                 // Remember that this is a 'failed' widget
454
bogusTrim.add(widget);
455                 
456                 IStatus status = null;
457                 if (e instanceof CoreException) {
458                     status = ((CoreException) e).getStatus();
459                 } else {
460                     status = StatusUtil
461                             .newStatus(
462                                     IStatus.ERROR,
463                                     "Internal plug-in widget delegate error on creation.", e); //$NON-NLS-1$
464
}
465                 WorkbenchPlugin
466                         .log(
467                                 "Could not create widget delegate for id: " + widget.getId(), status); //$NON-NLS-1$
468

469                 return null;
470             }
471
472             return iw;
473         }
474         
475         /**
476          * Returns <code>true</code> if the trim want extra space if possible.
477          *
478          * @param widget
479          * The SWidget to check
480          * @return <code>true</code> iff the widget wants extra space
481          */

482         private boolean isResizeable(SWidget widget) {
483             try {
484                 SLayout layout = widget.getLayout();
485                 return layout.fillMajor();
486             } catch (NotDefinedException e) {
487                 // TODO Auto-generated catch block
488
e.printStackTrace();
489             }
490
491             return false;
492         }
493
494         /**
495          * Determine the SWT curSide that a particular group should be placed on
496          * by default. This is usually accomplished by the contributor making
497          * their own group 'relativeTo' one of the workbench defined groups
498          * (which equate to the available set of trim 'areas'.
499          *
500          * @param group
501          * The group to determine the curSide for
502          * @return The 'curSide' to place the group on. <code>SWT.BOTTOM</code>
503          * by default
504          */

505         private int getSide(SGroup group) {
506             int side = SWT.BOTTOM; // Default
507
try {
508                 SLocation[] locs = group.getLocations();
509                 if (locs.length == 0)
510                     return side;
511
512                 // We don't support multiple locations yet so use the first one
513
SOrder order = locs[0].getOrdering();
514                 String JavaDoc relTo = order.getRelativeTo();
515                 if ("command1".equals(relTo)) //$NON-NLS-1$
516
side = SWT.TOP;
517                 else if ("command2".equals(relTo)) //$NON-NLS-1$
518
side = SWT.TOP;
519                 else if ("vertical1".equals(relTo)) //$NON-NLS-1$
520
side = SWT.LEFT;
521                 else if ("vertical2".equals(relTo)) //$NON-NLS-1$
522
side = SWT.RIGHT;
523                 else if ("status".equals(relTo)) //$NON-NLS-1$
524
side = SWT.BOTTOM;
525                 else {
526                     // TODO: reursively walk the 'relTo' chain until we find
527
// one of the 'magic' groups or find one with no relTo
528
}
529             } catch (NotDefinedException e) {
530                 // TODO Auto-generated catch block
531
e.printStackTrace();
532             }
533
534             return side;
535         }
536
537         /**
538          * Return the trim element that this trim should be placed before in the
539          * order
540          *
541          * @param group
542          * @return
543          */

544         private boolean isAtStart(SGroup group) {
545             boolean atStart = false;
546             try {
547                 SLocation[] locs = group.getLocations();
548                 if (locs.length == 0)
549                     return atStart;
550
551                 // We don't support multiple locations yet so use the first one
552
SOrder order = locs[0].getOrdering();
553                 atStart = SOrder.POSITION_BEFORE == order.getPosition();
554             } catch (NotDefinedException e) {
555                 // TODO Auto-generated catch block
556
e.printStackTrace();
557             }
558
559             return atStart;
560         }
561
562         /**
563          * Reposition any contributed trim whose id is -not- a 'knownId'. If the
564          * id is known then the trim has already been positioned from the stored
565          * workbench state. If it isn't then it's a new contribution whose
566          * default position may have been trashed by the WorkbenchWindow's
567          * 'restoreState' handling.
568          *
569          * @param knownIds
570          * A List of strings containing the ids of any trim that was
571          * explicitly positioned during the restore state.
572          */

573         public void updateLocations(List JavaDoc knownIds) {
574             for (Iterator JavaDoc iter = curGroups.iterator(); iter.hasNext();) {
575                 TrimWidgetProxy proxy = (TrimWidgetProxy) iter.next();
576                 if (!knownIds.contains(proxy.getGroup().getId())) {
577                     placeGroup(proxy);
578                 }
579             }
580         }
581     }
582
583     /**
584      * Updates the placement of any contributed trim that is -not- in the
585      * 'knownIds' list (which indicates that it has already been placed using
586      * cached workspace data.
587      *
588      * Forward on to the bulder for implementation
589      */

590     public void updateLocations(List JavaDoc knownIds) {
591         fTrimBuilder.updateLocations(knownIds);
592     }
593     
594     /**
595      * unhook the menu service.
596      */

597     public void dispose() {
598         fMenuService = null;
599         fTrimBuilder = null;
600     }
601 }
602
Popular Tags