001: /*******************************************************************************
002: * Copyright (c) 2000, 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: * Tonny Madsen, RCP Company - bug 201055
011: *******************************************************************************/package org.eclipse.ui.actions;
012:
013: import com.ibm.icu.text.Collator;
014: import java.util.ArrayList;
015: import java.util.Collections;
016: import java.util.Comparator;
017: import java.util.HashMap;
018: import java.util.Iterator;
019: import java.util.List;
020: import java.util.Map;
021:
022: import org.eclipse.jface.action.Action;
023: import org.eclipse.jface.action.ContributionItem;
024: import org.eclipse.jface.action.IAction;
025: import org.eclipse.jface.action.IContributionItem;
026: import org.eclipse.jface.action.IMenuListener;
027: import org.eclipse.jface.action.IMenuManager;
028: import org.eclipse.jface.action.MenuManager;
029: import org.eclipse.jface.action.Separator;
030: import org.eclipse.jface.window.Window;
031: import org.eclipse.swt.SWT;
032: import org.eclipse.swt.events.SelectionEvent;
033: import org.eclipse.swt.widgets.Event;
034: import org.eclipse.swt.widgets.Menu;
035: import org.eclipse.swt.widgets.MenuItem;
036: import org.eclipse.ui.IPerspectiveDescriptor;
037: import org.eclipse.ui.IPerspectiveRegistry;
038: import org.eclipse.ui.IWorkbenchPage;
039: import org.eclipse.ui.IWorkbenchPreferenceConstants;
040: import org.eclipse.ui.IWorkbenchWindow;
041: import org.eclipse.ui.activities.WorkbenchActivityHelper;
042: import org.eclipse.ui.internal.WorkbenchMessages;
043: import org.eclipse.ui.internal.dialogs.SelectPerspectiveDialog;
044: import org.eclipse.ui.internal.util.PrefUtil;
045:
046: /**
047: * A menu for perspective selection.
048: * <p>
049: * A <code>PerspectiveMenu</code> is used to populate a menu with
050: * perspective shortcut items. If the user selects one of these items
051: * an action is performed for the selected perspective.
052: * </p><p>
053: * The visible perspective items within the menu are dynamic and reflect the
054: * available set generated by each subclass. The default available set consists
055: * of the perspective shortcut list of the current perspective.
056: * </p><p>
057: * This class is abstract. Subclasses must implement the <code>run</code> method,
058: * which performs a specialized action for the selected perspective.
059: * </p>
060: */
061: public abstract class PerspectiveMenu extends ContributionItem {
062: private IPerspectiveRegistry reg;
063:
064: private IWorkbenchWindow window;
065:
066: private boolean showActive = false;
067:
068: private boolean dirty = true;
069:
070: private IMenuListener menuListener = new IMenuListener() {
071: public void menuAboutToShow(IMenuManager manager) {
072: manager.markDirty();
073: dirty = true;
074: }
075: };
076:
077: private Comparator comparator = new Comparator() {
078: private Collator collator = Collator.getInstance();
079:
080: public int compare(Object ob1, Object ob2) {
081: IPerspectiveDescriptor d1 = (IPerspectiveDescriptor) ob1;
082: IPerspectiveDescriptor d2 = (IPerspectiveDescriptor) ob2;
083: return collator.compare(d1.getLabel(), d2.getLabel());
084: }
085: };
086:
087: /**
088: * The translatable message to show when there are no perspectives.
089: *
090: * @since 3.1
091: */
092: private static final String NO_TARGETS_MSG = WorkbenchMessages.Workbench_showInNoPerspectives;
093:
094: /**
095: * The map of perspective identifiers (String) to actions
096: * (OpenPerspectiveAction). This map may be empty, but it is never
097: * <code>null</code>.
098: *
099: * @since 3.1
100: */
101: private Map actions = new HashMap();
102:
103: /**
104: * The action for that allows the user to choose any perspective to open.
105: *
106: * @since 3.1
107: */
108: private Action openOtherAction = new Action(
109: WorkbenchMessages.PerspectiveMenu_otherItem) {
110: public final void runWithEvent(final Event event) {
111: runOther(new SelectionEvent(event));
112: }
113: };
114:
115: /**
116: * Constructs a new instance of <code>PerspectiveMenu</code>.
117: *
118: * @param window the window containing this menu
119: * @param id the menu id
120: */
121: public PerspectiveMenu(IWorkbenchWindow window, String id) {
122: super (id);
123: this .window = window;
124: reg = window.getWorkbench().getPerspectiveRegistry();
125: openOtherAction
126: .setActionDefinitionId("org.eclipse.ui.perspectives.showPerspective"); //$NON-NLS-1$
127: }
128:
129: /*
130: * (non-Javadoc) Fills the menu with perspective items.
131: */
132: public void fill(Menu menu, int index) {
133: if (getParent() instanceof MenuManager) {
134: ((MenuManager) getParent()).addMenuListener(menuListener);
135: }
136:
137: if (!dirty) {
138: return;
139: }
140:
141: final MenuManager manager = new MenuManager();
142: fillMenu(manager);
143: final IContributionItem items[] = manager.getItems();
144: if (items.length == 0) {
145: MenuItem item = new MenuItem(menu, SWT.NONE, index++);
146: item.setText(NO_TARGETS_MSG);
147: item.setEnabled(false);
148: } else {
149: for (int i = 0; i < items.length; i++) {
150: items[i].fill(menu, index++);
151: }
152: }
153: dirty = false;
154: }
155:
156: /**
157: * Fills the given menu manager with all the open perspective actions
158: * appropriate for the currently active perspective. Filtering is applied to
159: * the actions based on the activities and capabilities mechanism.
160: *
161: * @param manager
162: * The menu manager that should receive the menu items; must not
163: * be <code>null</code>.
164: * @since 3.1
165: */
166: private final void fillMenu(final MenuManager manager) {
167: // Clear out the manager so that we have a blank slate.
168: manager.removeAll();
169:
170: // Collect and sort perspective descriptors.
171: final List persps = getPerspectiveItems();
172: Collections.sort(persps, comparator);
173:
174: /*
175: * Convert the perspective descriptors to actions, and filter out
176: * actions using the activity/capability mechanism.
177: */
178: final List actions = new ArrayList(persps.size());
179: for (Iterator i = persps.iterator(); i.hasNext();) {
180: final IPerspectiveDescriptor descriptor = (IPerspectiveDescriptor) i
181: .next();
182: final IAction action = getAction(descriptor.getId());
183: if (action != null) {
184: if (WorkbenchActivityHelper.filterItem(action)) {
185: continue;
186: }
187: actions.add(action);
188: }
189: }
190:
191: // Go through and add each of the actions to the menu manager.
192: for (Iterator i = actions.iterator(); i.hasNext();) {
193: manager.add((IAction) i.next());
194: }
195:
196: if (PrefUtil
197: .getAPIPreferenceStore()
198: .getBoolean(
199: IWorkbenchPreferenceConstants.SHOW_OTHER_IN_PERSPECTIVE_MENU)) {
200: // Add a separator and then "Other..."
201: if (actions.size() > 0) {
202: manager.add(new Separator());
203: }
204: manager.add(openOtherAction);
205: }
206: }
207:
208: /**
209: * Returns the action for the given perspective id. This is a lazy cache. If
210: * the action does not already exist, then it is created. If there is no
211: * perspective with the given identifier, then the action is not created.
212: *
213: * @param id
214: * The identifier of the perspective for which the action should
215: * be retrieved.
216: * @return The action for the given identifier; or <code>null</code> if
217: * there is no perspective with the given identifier.
218: * @since 3.1
219: */
220: private final IAction getAction(final String id) {
221: IAction action = (IAction) actions.get(id);
222: if (action == null) {
223: IPerspectiveDescriptor descriptor = reg
224: .findPerspectiveWithId(id);
225: if (descriptor != null) {
226: action = new OpenPerspectiveAction(window, descriptor,
227: this );
228: action.setActionDefinitionId(id);
229: actions.put(id, action);
230: }
231: }
232: return action;
233: }
234:
235: /* (non-Javadoc)
236: * Returns the perspective shortcut items for the active perspective.
237: *
238: * @return a list of <code>IPerspectiveDescriptor</code> items
239: */
240: private ArrayList getPerspectiveShortcuts() {
241: ArrayList list = new ArrayList();
242:
243: IWorkbenchPage page = window.getActivePage();
244: if (page == null) {
245: return list;
246: }
247:
248: String[] ids = page.getPerspectiveShortcuts();
249:
250: for (int i = 0; i < ids.length; i++) {
251: IPerspectiveDescriptor desc = reg
252: .findPerspectiveWithId(ids[i]);
253: if (desc != null && !list.contains(desc)) {
254: if (WorkbenchActivityHelper.filterItem(desc)) {
255: continue;
256: }
257: list.add(desc);
258: }
259: }
260:
261: return list;
262: }
263:
264: /**
265: * Returns the available list of perspectives to display in the menu.
266: * <p>
267: * By default, the list contains the perspective shortcuts
268: * for the current perspective.
269: * </p><p>
270: * Subclasses can override this method to return a different list.
271: * </p>
272: *
273: * @return an <code>ArrayList<code> of perspective items <code>IPerspectiveDescriptor</code>
274: */
275: protected ArrayList getPerspectiveItems() {
276: /* Allow the user to see all the perspectives they have
277: * selected via Customize Perspective. Bugzilla bug #23445 */
278: ArrayList shortcuts = getPerspectiveShortcuts();
279: ArrayList list = new ArrayList(shortcuts.size());
280:
281: // Add perspective shortcuts from the active perspective
282: int size = shortcuts.size();
283: for (int i = 0; i < size; i++) {
284: if (!list.contains(shortcuts.get(i))) {
285: list.add(shortcuts.get(i));
286: }
287: }
288:
289: return list;
290: }
291:
292: /**
293: * Returns whether the menu item representing the active perspective
294: * will have a check mark.
295: *
296: * @return <code>true</code> if a check mark is shown, <code>false</code> otherwise
297: */
298: protected boolean getShowActive() {
299: return showActive;
300: }
301:
302: /**
303: * Returns the window for this menu.
304: *
305: * @return the window
306: */
307: protected IWorkbenchWindow getWindow() {
308: return window;
309: }
310:
311: /* (non-Javadoc)
312: * Returns whether this menu is dynamic.
313: */
314: public boolean isDirty() {
315: return dirty;
316: }
317:
318: /* (non-Javadoc)
319: * Returns whether this menu is dynamic.
320: */
321: public boolean isDynamic() {
322: return true;
323: }
324:
325: /**
326: * Runs an action for a particular perspective. The behavior of the
327: * action is defined by the subclass.
328: *
329: * @param desc the selected perspective
330: */
331: protected abstract void run(IPerspectiveDescriptor desc);
332:
333: /**
334: * Runs an action for a particular perspective. The behavior of the action
335: * is defined by the subclass. By default, this just calls
336: * <code>run(IPerspectiveDescriptor)</code>.
337: *
338: * @param desc
339: * the selected perspective
340: * @param event
341: * SelectionEvent - the event send along with the selection
342: * callback
343: */
344: protected void run(IPerspectiveDescriptor desc, SelectionEvent event) {
345: //Do a run without the event by default
346: run(desc);
347: }
348:
349: /* (non-Javadoc)
350: * Show the "other" dialog, select a perspective, and run it. Pass on the selection
351: * event should the meny need it.
352: */
353: void runOther(SelectionEvent event) {
354: SelectPerspectiveDialog dlg = new SelectPerspectiveDialog(
355: window.getShell(), reg);
356: dlg.open();
357: if (dlg.getReturnCode() == Window.CANCEL) {
358: return;
359: }
360: IPerspectiveDescriptor desc = dlg.getSelection();
361: if (desc != null) {
362: run(desc, event);
363: }
364: }
365:
366: /**
367: * Sets the showActive flag. If <code>showActive == true</code> then the
368: * active perspective is hilighted with a check mark.
369: *
370: * @param b the new showActive flag
371: */
372: protected void showActive(boolean b) {
373: showActive = b;
374: }
375: }
|