001: /*******************************************************************************
002: * Copyright (c) 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.layout;
011:
012: import org.eclipse.jface.action.ContributionItem;
013: import org.eclipse.jface.action.MenuManager;
014: import org.eclipse.jface.action.ToolBarManager;
015: import org.eclipse.swt.SWT;
016: import org.eclipse.swt.events.SelectionEvent;
017: import org.eclipse.swt.events.SelectionListener;
018: import org.eclipse.swt.graphics.Cursor;
019: import org.eclipse.swt.graphics.Point;
020: import org.eclipse.swt.graphics.Rectangle;
021: import org.eclipse.swt.widgets.Control;
022: import org.eclipse.swt.widgets.CoolBar;
023: import org.eclipse.swt.widgets.CoolItem;
024: import org.eclipse.swt.widgets.Event;
025: import org.eclipse.swt.widgets.Listener;
026: import org.eclipse.swt.widgets.Menu;
027: import org.eclipse.swt.widgets.MenuItem;
028: import org.eclipse.swt.widgets.ToolBar;
029: import org.eclipse.swt.widgets.ToolItem;
030: import org.eclipse.ui.internal.IChangeListener;
031: import org.eclipse.ui.internal.IntModel;
032: import org.eclipse.ui.internal.RadioMenu;
033: import org.eclipse.ui.internal.TrimFrame;
034: import org.eclipse.ui.internal.WorkbenchMessages;
035: import org.eclipse.ui.internal.WorkbenchWindow;
036: import org.eclipse.ui.internal.dnd.DragUtil;
037: import org.eclipse.ui.presentations.PresentationUtil;
038:
039: /**
040: * This control provides common UI functionality for trim elements
041: * that wish to use a ToolBarManager-based implementation.
042: * <p>
043: * It provides the following features:
044: * <p>
045: * Drag affordance and handling:
046: * <ol>
047: * <li>The ToolBar is contained within a CoolBar/Item to provide the same
048: * drag handle affordance as the rest of the trim
049: * <li>Drag handling is provided to allow rearrangement within a trim side or
050: * to other sides, depending on the values returned by <code>IWindowTrim.getValidSides</code></li>
051: * </ol>
052: * </p>
053: * <p>
054: * Context Menu:
055: * <ol>
056: * <li>A "Dock on" menu item is provided to allow changing the side, depending on the values returned by
057: * <code>IWindowTrim.getValidSides</code></li>
058: * <li>A "Close" menu item is provided to allow the User to close (hide) the trim element,
059: * based on the value returned by <code>IWindowTrim.isCloseable</code>
060: * </ol>
061: * </p>
062: * <p>
063: * @since 3.3
064: * </p>
065: */
066: public abstract class TrimToolBarBase implements IWindowTrim {
067: // Fields
068: protected String id;
069: protected int orientation;
070: protected WorkbenchWindow wbw;
071: protected TrimLayout layout;
072: protected ToolBarManager tbMgr = null;
073: protected ToolItem contextToolItem = null;
074:
075: // CoolBar handling
076: private TrimFrame frame = null;
077: private CoolBar cb = null;
078: private CoolItem ci = null;
079:
080: // Context Menu
081: private MenuManager dockMenuManager;
082: private ContributionItem dockContributionItem = null;
083: private Menu sidesMenu;
084: private MenuItem dockCascade;
085: private RadioMenu radioButtons;
086: private IntModel radioVal = new IntModel(0);
087: // private Menu showMenu;
088: // private MenuItem showCascade;
089:
090: /*
091: * Listeners...
092: */
093:
094: private Listener tbListener = new Listener() {
095: public void handleEvent(Event event) {
096: Point loc = new Point(event.x, event.y);
097: if (event.type == SWT.MenuDetect) {
098: showToolBarPopup(loc);
099: }
100: }
101: };
102:
103: /**
104: * This listener brings up the context menu
105: */
106: private Listener cbListener = new Listener() {
107: public void handleEvent(Event event) {
108: Point loc = new Point(event.x, event.y);
109: if (event.type == SWT.MenuDetect) {
110: showDockTrimPopup(loc);
111: }
112: }
113: };
114:
115: /**
116: * This listener starts a drag operation when
117: * the Drag and Drop manager tells it to
118: */
119: private Listener dragListener = new Listener() {
120: public void handleEvent(Event event) {
121: // Only allow 'left mouse' drags...
122: if (event.button != 3) {
123: Point position = DragUtil.getEventLoc(event);
124: startDraggingTrim(position);
125: }
126: }
127: };
128:
129: /**
130: * Create a new trim UI handle for a particular IWindowTrim item
131: *
132: * @param layout the TrimLayout we're being used in
133: * @param trim the IWindowTrim we're acting on behalf of
134: * @param curSide the SWT side that the trim is currently on
135: */
136: protected TrimToolBarBase(String id, int curSide,
137: WorkbenchWindow wbw) {
138: this .id = id;
139: this .wbw = wbw;
140: this .layout = (TrimLayout) wbw.getTrimManager();
141: }
142:
143: /**
144: * @param loc
145: */
146: private void showToolBarPopup(Point loc) {
147: Point tbLoc = tbMgr.getControl().toControl(loc);
148: contextToolItem = tbMgr.getControl().getItem(tbLoc);
149: MenuManager mm = tbMgr.getContextMenuManager();
150: if (mm != null) {
151: Menu menu = mm.createContextMenu(wbw.getShell());
152: menu.setLocation(loc.x, loc.y);
153: menu.setVisible(true);
154: }
155: }
156:
157: /**
158: * Initialize the ToolBarManger for this instance. We create a
159: * new ToolBarManager whenever we need to and this gives the
160: * derived class a chance to define the ICI's and context
161: * menu...
162: *
163: * @param mgr The manager to initialize
164: */
165: public abstract void initToolBarManager(ToolBarManager mgr);
166:
167: /**
168: * Hook any necessary listeners to the new ToolBar instance.
169: * <p>
170: * NOTE: Clients should add a dispose listener if necessary to
171: * unhook listeners added through this call.
172: * </p>
173: * @param mgr The ToolBarManager whose control is to be hooked
174: */
175: public abstract void hookControl(ToolBarManager mgr);
176:
177: /**
178: * Set up the trim with its cursor, drag listener, context menu and menu listener.
179: * This method can also be used to 'recycle' a trim handle as long as the new handle
180: * is for trim under the same parent as it was originally used for.
181: */
182: private void createControl(int curSide) {
183: // out with the old
184: dispose();
185:
186: this .radioVal.set(curSide);
187:
188: // remember the orientation to use
189: orientation = (curSide == SWT.LEFT || curSide == SWT.RIGHT) ? SWT.VERTICAL
190: : SWT.HORIZONTAL;
191:
192: frame = new TrimFrame(wbw.getShell());
193:
194: // Create the necessary parts...
195: cb = new CoolBar(frame.getComposite(), orientation | SWT.FLAT);
196: ci = new CoolItem(cb, SWT.FLAT);
197:
198: // Create (and 'fill') the toolbar
199: tbMgr = new ToolBarManager(orientation | SWT.FLAT);
200:
201: // Have the subclass define any manager content
202: initToolBarManager(tbMgr);
203:
204: // Create the new ToolBar
205: ToolBar tb = tbMgr.createControl(cb);
206: ci.setControl(tb);
207:
208: // Have the subclass hook any listeners
209: hookControl(tbMgr);
210:
211: // set up the frame's layout
212: update(true);
213:
214: // Set the cursor affordance
215: Cursor dragCursor = getControl().getDisplay().getSystemCursor(
216: SWT.CURSOR_SIZEALL);
217: cb.setCursor(dragCursor);
218:
219: // Now, we have to explicity set the arrow for the TB
220: Cursor tbCursor = getControl().getDisplay().getSystemCursor(
221: SWT.CURSOR_ARROW);
222: tb.setCursor(tbCursor);
223:
224: //cb.setBackground(cb.getDisplay().getSystemColor(SWT.COLOR_RED));
225:
226: // Set up the dragging behaviour
227: PresentationUtil.addDragListener(cb, dragListener);
228:
229: // Create the docking context menu
230: dockMenuManager = new MenuManager();
231: dockContributionItem = getDockingContribution();
232: dockMenuManager.add(dockContributionItem);
233:
234: tb.addListener(SWT.MenuDetect, tbListener);
235: cb.addListener(SWT.MenuDetect, cbListener);
236:
237: //tbMgr.getControl().setBackground(cb.getDisplay().getSystemColor(SWT.COLOR_GREEN));
238: //tbMgr.getControl().pack(true);
239: cb.pack(true);
240: cb.setVisible(true);
241:
242: tbMgr.getControl().setVisible(true);
243: cb.setVisible(true);
244: frame.getComposite().setVisible(true);
245: }
246:
247: /**
248: * Handle the event generated when a User selects a new side to
249: * dock this trim on using the context menu
250: */
251: private void handleShowOnChange() {
252: if (getControl() == null)
253: return;
254:
255: layout.removeTrim(this );
256: dock(radioVal.get());
257: layout.addTrim(radioVal.get(), this , null);
258:
259: // perform an optimized layout to show the trim in its new location
260: LayoutUtil.resize(getControl());
261: }
262:
263: /**
264: * Force the toobar to re-synch to the model
265: * @param changed true if changes have occurred in the structure
266: */
267: public void update(boolean changed) {
268: tbMgr.update(changed);
269:
270: // Force a resize
271: tbMgr.getControl().pack();
272: Point size = tbMgr.getControl().getSize();
273: //tbMgr.getControl().setBounds(0, 0, size.x, size.y);
274: Point ps = ci.computeSize(size.x, size.y);
275: ci.setPreferredSize(ps);
276: ci.setSize(ps);
277: cb.pack();
278: cb.update();
279: LayoutUtil.resize(getControl());
280: }
281:
282: /**
283: * Construct (if necessary) a context menu contribution item and return it. This
284: * is explicitly <code>public</code> so that trim elements can retrieve the item
285: * and add it into their own context menus if desired.
286: *
287: * @return The contribution item for the handle's context menu.
288: */
289: private ContributionItem getDockingContribution() {
290: if (dockContributionItem == null) {
291: dockContributionItem = new ContributionItem() {
292: public void fill(Menu menu, int index) {
293: // populate from superclass
294: super .fill(menu, index);
295:
296: // Add a 'Close' menu entry if the trim supports the operation
297: if (isCloseable()) {
298: MenuItem closeItem = new MenuItem(menu,
299: SWT.PUSH, index++);
300: closeItem
301: .setText(WorkbenchMessages.TrimCommon_Close);
302:
303: closeItem
304: .addSelectionListener(new SelectionListener() {
305: public void widgetSelected(
306: SelectionEvent e) {
307: handleCloseTrim();
308: }
309:
310: public void widgetDefaultSelected(
311: SelectionEvent e) {
312: }
313: });
314:
315: new MenuItem(menu, SWT.SEPARATOR, index++);
316: }
317:
318: // Test Hook: add a menu entry that brings up a dialog to allow
319: // testing with various GUI prefs.
320: // MenuItem closeItem = new MenuItem(menu, SWT.PUSH, index++);
321: // closeItem.setText("Change Preferences"); //$NON-NLS-1$
322: //
323: // closeItem.addSelectionListener(new SelectionListener() {
324: // public void widgetSelected(SelectionEvent e) {
325: // handleChangePreferences();
326: // }
327: //
328: // public void widgetDefaultSelected(SelectionEvent e) {
329: // }
330: // });
331: //
332: // new MenuItem(menu, SWT.SEPARATOR, index++);
333:
334: // Create a cascading menu to allow the user to dock the trim
335: dockCascade = new MenuItem(menu, SWT.CASCADE,
336: index++);
337: {
338: dockCascade
339: .setText(WorkbenchMessages.TrimCommon_DockOn);
340:
341: sidesMenu = new Menu(dockCascade);
342: radioButtons = new RadioMenu(sidesMenu,
343: radioVal);
344:
345: radioButtons.addMenuItem(
346: WorkbenchMessages.TrimCommon_Top,
347: new Integer(SWT.TOP));
348: radioButtons.addMenuItem(
349: WorkbenchMessages.TrimCommon_Bottom,
350: new Integer(SWT.BOTTOM));
351: radioButtons.addMenuItem(
352: WorkbenchMessages.TrimCommon_Left,
353: new Integer(SWT.LEFT));
354: radioButtons.addMenuItem(
355: WorkbenchMessages.TrimCommon_Right,
356: new Integer(SWT.RIGHT));
357:
358: dockCascade.setMenu(sidesMenu);
359: }
360:
361: // if the radioVal changes it means that the User wants to change the docking location
362: radioVal.addChangeListener(new IChangeListener() {
363: public void update(boolean changed) {
364: if (changed) {
365: handleShowOnChange();
366: }
367: }
368: });
369:
370: // Provide Show / Hide trim capabilities
371: // showCascade = new MenuItem(menu, SWT.CASCADE, index++);
372: // {
373: // showCascade.setText(WorkbenchMessages.TrimCommon_ShowTrim);
374: //
375: // showMenu = new Menu(dockCascade);
376: //
377: // // Construct a 'hide/show' cascade from -all- the existing trim...
378: // List trimItems = layout.getAllTrim();
379: // Iterator d = trimItems.iterator();
380: // while (d.hasNext()) {
381: // IWindowTrim trimItem = (IWindowTrim) d.next();
382: // MenuItem item = new MenuItem(showMenu, SWT.CHECK);
383: // item.setText(trimItem.getDisplayName());
384: // item.setSelection(trimItem.getControl().getVisible());
385: // item.setData(trimItem);
386: //
387: // // TODO: Make this work...wire it off for now
388: // item.setEnabled(false);
389: //
390: // item.addSelectionListener(new SelectionListener() {
391: //
392: // public void widgetSelected(SelectionEvent e) {
393: // IWindowTrim trim = (IWindowTrim) e.widget.getData();
394: // layout.setTrimVisible(trim, !trim.getControl().getVisible());
395: // }
396: //
397: // public void widgetDefaultSelected(SelectionEvent e) {
398: // }
399: //
400: // });
401: // }
402: //
403: // showCascade.setMenu(showMenu);
404: // }
405: }
406: };
407: }
408: return dockContributionItem;
409: }
410:
411: /**
412: * @return The side the trm is currently on
413: */
414: public int getCurrentSide() {
415: return radioVal.get();
416: }
417:
418: /**
419: * Test Hook: Bring up a dialog that allows the user to
420: * modify the trimdragging GUI preferences.
421: */
422: // private void handleChangePreferences() {
423: // TrimDragPreferenceDialog dlg = new TrimDragPreferenceDialog(getShell());
424: // dlg.open();
425: // }
426: /**
427: * Handle the event generated when the "Close" item is
428: * selected on the context menu. This removes the associated
429: * trim and calls back to the IWidnowTrim to inform it that
430: * the User has closed the trim.
431: */
432: private void handleCloseTrim() {
433: handleClose();
434: }
435:
436: /* (non-Javadoc)
437: * @see org.eclipse.swt.widgets.Widget#dispose()
438: */
439: public void dispose() {
440: if (getControl() == null || getControl().isDisposed())
441: return;
442:
443: if (radioButtons != null) {
444: radioButtons.dispose();
445: }
446:
447: // tidy up...
448: getControl().removeListener(SWT.MenuDetect, cbListener);
449:
450: tbMgr.dispose();
451: tbMgr = null;
452:
453: getControl().dispose();
454: frame = null;
455: }
456:
457: /**
458: * Begins dragging the trim
459: *
460: * @param position initial mouse position
461: */
462: private void startDraggingTrim(Point position) {
463: Rectangle fakeBounds = new Rectangle(100000, 0, 0, 0);
464: DragUtil.performDrag(this , fakeBounds, position, true);
465: }
466:
467: /**
468: * Shows the popup menu for an item in the fast view bar.
469: */
470: private void showDockTrimPopup(Point pt) {
471: Menu menu = dockMenuManager
472: .createContextMenu(this .getControl());
473: menu.setLocation(pt.x, pt.y);
474: menu.setVisible(true);
475: }
476:
477: /* (non-Javadoc)
478: * @see org.eclipse.ui.internal.layout.IWindowTrim#dock(int)
479: */
480: public void dock(int dropSide) {
481: createControl(dropSide);
482: }
483:
484: /* (non-Javadoc)
485: * @see org.eclipse.ui.internal.layout.IWindowTrim#getControl()
486: */
487: public Control getControl() {
488: if (frame == null)
489: return null;
490:
491: return frame.getComposite();
492: }
493:
494: /* (non-Javadoc)
495: * @see org.eclipse.ui.internal.layout.IWindowTrim#getDisplayName()
496: */
497: public String getDisplayName() {
498: return id;
499: }
500:
501: /* (non-Javadoc)
502: * @see org.eclipse.ui.internal.layout.IWindowTrim#getHeightHint()
503: */
504: public int getHeightHint() {
505: return getControl().computeSize(SWT.DEFAULT, SWT.DEFAULT, true).y;
506: }
507:
508: /* (non-Javadoc)
509: * @see org.eclipse.ui.internal.layout.IWindowTrim#getId()
510: */
511: public String getId() {
512: return id;
513: }
514:
515: /* (non-Javadoc)
516: * @see org.eclipse.ui.internal.layout.IWindowTrim#getValidSides()
517: */
518: public int getValidSides() {
519: return SWT.BOTTOM | SWT.TOP | SWT.LEFT | SWT.RIGHT;
520: }
521:
522: /* (non-Javadoc)
523: * @see org.eclipse.ui.internal.layout.IWindowTrim#getWidthHint()
524: */
525: public int getWidthHint() {
526: return getControl().computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x;
527: }
528:
529: /* (non-Javadoc)
530: * @see org.eclipse.ui.internal.layout.IWindowTrim#handleClose()
531: */
532: public void handleClose() {
533: }
534:
535: /* (non-Javadoc)
536: * @see org.eclipse.ui.internal.layout.IWindowTrim#isCloseable()
537: */
538: public boolean isCloseable() {
539: return false;
540: }
541:
542: /* (non-Javadoc)
543: * @see org.eclipse.ui.internal.layout.IWindowTrim#isResizeable()
544: */
545: public boolean isResizeable() {
546: return false;
547: }
548: }
|