001: /*******************************************************************************
002: * Copyright (c) 2000, 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: * Benjamin Muskalla - Bug 29633 [EditorMgmt] "Open" menu should
011: * have Open With-->Other
012: *******************************************************************************/package org.eclipse.ui.actions;
013:
014: import java.util.ArrayList;
015: import java.util.Arrays;
016: import java.util.Collections;
017: import java.util.Comparator;
018: import java.util.Hashtable;
019:
020: import org.eclipse.core.resources.IFile;
021: import org.eclipse.core.resources.IResource;
022: import org.eclipse.core.runtime.IAdaptable;
023: import org.eclipse.jface.action.ContributionItem;
024: import org.eclipse.jface.resource.ImageDescriptor;
025: import org.eclipse.jface.window.Window;
026: import org.eclipse.osgi.util.NLS;
027: import org.eclipse.swt.SWT;
028: import org.eclipse.swt.graphics.Image;
029: import org.eclipse.swt.widgets.Event;
030: import org.eclipse.swt.widgets.Listener;
031: import org.eclipse.swt.widgets.Menu;
032: import org.eclipse.swt.widgets.MenuItem;
033: import org.eclipse.ui.IEditorDescriptor;
034: import org.eclipse.ui.IEditorRegistry;
035: import org.eclipse.ui.IWorkbenchPage;
036: import org.eclipse.ui.PartInitException;
037: import org.eclipse.ui.PlatformUI;
038: import org.eclipse.ui.dialogs.EditorSelectionDialog;
039: import org.eclipse.ui.ide.IDE;
040: import org.eclipse.ui.internal.WorkbenchPage;
041: import org.eclipse.ui.internal.ide.DialogUtil;
042: import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
043: import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
044: import org.eclipse.ui.part.FileEditorInput;
045:
046: import com.ibm.icu.text.Collator;
047:
048: /**
049: * A menu for opening files in the workbench.
050: * <p>
051: * An <code>OpenWithMenu</code> is used to populate a menu with
052: * "Open With" actions. One action is added for each editor which is applicable
053: * to the selected file. If the user selects one of these items, the corresponding
054: * editor is opened on the file.
055: * </p>
056: * <p>
057: * This class may be instantiated; it is not intended to be subclassed.
058: * </p>
059: */
060: public class OpenWithMenu extends ContributionItem {
061: private IWorkbenchPage page;
062:
063: private IAdaptable file;
064:
065: private IEditorRegistry registry = PlatformUI.getWorkbench()
066: .getEditorRegistry();
067:
068: private static Hashtable imageCache = new Hashtable(11);
069:
070: /**
071: * The id of this action.
072: */
073: public static final String ID = PlatformUI.PLUGIN_ID
074: + ".OpenWithMenu";//$NON-NLS-1$
075:
076: /**
077: * Match both the input and id, so that different types of editor can be opened on the same input.
078: */
079: private static final int MATCH_BOTH = IWorkbenchPage.MATCH_INPUT
080: | IWorkbenchPage.MATCH_ID;
081:
082: /*
083: * Compares the labels from two IEditorDescriptor objects
084: */
085: private static final Comparator comparer = new Comparator() {
086: private Collator collator = Collator.getInstance();
087:
088: public int compare(Object arg0, Object arg1) {
089: String s1 = ((IEditorDescriptor) arg0).getLabel();
090: String s2 = ((IEditorDescriptor) arg1).getLabel();
091: return collator.compare(s1, s2);
092: }
093: };
094:
095: /**
096: * Constructs a new instance of <code>OpenWithMenu</code>.
097: *
098: * @param page the page where the editor is opened if an item within
099: * the menu is selected
100: * @deprecated As there is no way to set the file with this constructor use a
101: * different constructor.
102: */
103: public OpenWithMenu(IWorkbenchPage page) {
104: this (page, null);
105: }
106:
107: /**
108: * Constructs a new instance of <code>OpenWithMenu</code>.
109: *
110: * @param page the page where the editor is opened if an item within
111: * the menu is selected
112: * @param file the selected file
113: */
114: public OpenWithMenu(IWorkbenchPage page, IAdaptable file) {
115: super (ID);
116: this .page = page;
117: this .file = file;
118: }
119:
120: /**
121: * Returns an image to show for the corresponding editor descriptor.
122: *
123: * @param editorDesc the editor descriptor, or null for the system editor
124: * @return the image or null
125: */
126: private Image getImage(IEditorDescriptor editorDesc) {
127: ImageDescriptor imageDesc = getImageDescriptor(editorDesc);
128: if (imageDesc == null) {
129: return null;
130: }
131: Image image = (Image) imageCache.get(imageDesc);
132: if (image == null) {
133: image = imageDesc.createImage();
134: imageCache.put(imageDesc, image);
135: }
136: return image;
137: }
138:
139: /**
140: * Returns the image descriptor for the given editor descriptor,
141: * or null if it has no image.
142: */
143: private ImageDescriptor getImageDescriptor(
144: IEditorDescriptor editorDesc) {
145: ImageDescriptor imageDesc = null;
146: if (editorDesc == null) {
147: imageDesc = registry.getImageDescriptor(getFileResource()
148: .getName());
149: //TODO: is this case valid, and if so, what are the implications for content-type editor bindings?
150: } else {
151: imageDesc = editorDesc.getImageDescriptor();
152: }
153: if (imageDesc == null) {
154: if (editorDesc.getId().equals(
155: IEditorRegistry.SYSTEM_EXTERNAL_EDITOR_ID)) {
156: imageDesc = registry
157: .getSystemExternalEditorImageDescriptor(getFileResource()
158: .getName());
159: }
160: }
161: return imageDesc;
162: }
163:
164: /**
165: * Creates the menu item for the editor descriptor.
166: *
167: * @param menu the menu to add the item to
168: * @param descriptor the editor descriptor, or null for the system editor
169: * @param preferredEditor the descriptor of the preferred editor, or <code>null</code>
170: */
171: private void createMenuItem(Menu menu,
172: final IEditorDescriptor descriptor,
173: final IEditorDescriptor preferredEditor) {
174: // XXX: Would be better to use bold here, but SWT does not support it.
175: final MenuItem menuItem = new MenuItem(menu, SWT.RADIO);
176: boolean isPreferred = preferredEditor != null
177: && descriptor.getId().equals(preferredEditor.getId());
178: menuItem.setSelection(isPreferred);
179: menuItem.setText(descriptor.getLabel());
180: Image image = getImage(descriptor);
181: if (image != null) {
182: menuItem.setImage(image);
183: }
184: Listener listener = new Listener() {
185: public void handleEvent(Event event) {
186: switch (event.type) {
187: case SWT.Selection:
188: if (menuItem.getSelection()) {
189: openEditor(descriptor, false);
190: }
191: break;
192: }
193: }
194: };
195: menuItem.addListener(SWT.Selection, listener);
196: }
197:
198: /**
199: * Creates the Other... menu item
200: *
201: * @param menu the menu to add the item to
202: */
203: private void createOtherMenuItem(final Menu menu) {
204: final IFile fileResource = getFileResource();
205: if (fileResource == null) {
206: return;
207: }
208: new MenuItem(menu, SWT.SEPARATOR);
209: final MenuItem menuItem = new MenuItem(menu, SWT.PUSH);
210: menuItem.setText(IDEWorkbenchMessages.OpenWithMenu_Other);
211: Listener listener = new Listener() {
212: public void handleEvent(Event event) {
213: switch (event.type) {
214: case SWT.Selection:
215: EditorSelectionDialog dialog = new EditorSelectionDialog(
216: menu.getShell());
217: dialog
218: .setMessage(NLS
219: .bind(
220: IDEWorkbenchMessages.OpenWithMenu_OtherDialogDescription,
221: fileResource.getName()));
222: if (dialog.open() == Window.OK) {
223: IEditorDescriptor editor = dialog
224: .getSelectedEditor();
225: if (editor != null) {
226: openEditor(editor, editor.isOpenExternal());
227: }
228: }
229: break;
230: }
231: }
232: };
233: menuItem.addListener(SWT.Selection, listener);
234: }
235:
236: /* (non-Javadoc)
237: * Fills the menu with perspective items.
238: */
239: public void fill(Menu menu, int index) {
240: IFile file = getFileResource();
241: if (file == null) {
242: return;
243: }
244:
245: IEditorDescriptor defaultEditor = registry
246: .findEditor(IDEWorkbenchPlugin.DEFAULT_TEXT_EDITOR_ID); // may be null
247: IEditorDescriptor preferredEditor = IDE.getDefaultEditor(file); // may be null
248:
249: Object[] editors = registry.getEditors(file.getName(), IDE
250: .getContentType(file));
251: Collections.sort(Arrays.asList(editors), comparer);
252:
253: boolean defaultFound = false;
254:
255: //Check that we don't add it twice. This is possible
256: //if the same editor goes to two mappings.
257: ArrayList alreadyMapped = new ArrayList();
258:
259: for (int i = 0; i < editors.length; i++) {
260: IEditorDescriptor editor = (IEditorDescriptor) editors[i];
261: if (!alreadyMapped.contains(editor)) {
262: createMenuItem(menu, editor, preferredEditor);
263: if (defaultEditor != null
264: && editor.getId().equals(defaultEditor.getId())) {
265: defaultFound = true;
266: }
267: alreadyMapped.add(editor);
268: }
269: }
270:
271: // Only add a separator if there is something to separate
272: if (editors.length > 0) {
273: new MenuItem(menu, SWT.SEPARATOR);
274: }
275:
276: // Add default editor. Check it if it is saved as the preference.
277: if (!defaultFound && defaultEditor != null) {
278: createMenuItem(menu, defaultEditor, preferredEditor);
279: }
280:
281: // Add system editor (should never be null)
282: IEditorDescriptor descriptor = registry
283: .findEditor(IEditorRegistry.SYSTEM_EXTERNAL_EDITOR_ID);
284: createMenuItem(menu, descriptor, preferredEditor);
285:
286: // Add system in-place editor (can be null)
287: descriptor = registry
288: .findEditor(IEditorRegistry.SYSTEM_INPLACE_EDITOR_ID);
289: if (descriptor != null) {
290: createMenuItem(menu, descriptor, preferredEditor);
291: }
292: createDefaultMenuItem(menu, file);
293:
294: // add Other... menu item
295: createOtherMenuItem(menu);
296: }
297:
298: /**
299: * Converts the IAdaptable file to IFile or null.
300: */
301: private IFile getFileResource() {
302: if (this .file instanceof IFile) {
303: return (IFile) this .file;
304: }
305: IResource resource = (IResource) this .file
306: .getAdapter(IResource.class);
307: if (resource instanceof IFile) {
308: return (IFile) resource;
309: }
310:
311: return null;
312: }
313:
314: /* (non-Javadoc)
315: * Returns whether this menu is dynamic.
316: */
317: public boolean isDynamic() {
318: return true;
319: }
320:
321: /**
322: * Opens the given editor on the selected file.
323: *
324: * @param editor the editor descriptor, or null for the system editor
325: * @param openUsingDescriptor use the descriptor's editor ID for opening if false (normal case),
326: * or use the descriptor itself if true (needed to fix bug 178235).
327: */
328: private void openEditor(IEditorDescriptor editor,
329: boolean openUsingDescriptor) {
330: IFile file = getFileResource();
331: if (file == null) {
332: return;
333: }
334: try {
335: if (openUsingDescriptor) {
336: ((WorkbenchPage) page).openEditorFromDescriptor(
337: new FileEditorInput(file), editor, true, null);
338: } else {
339: String editorId = editor == null ? IEditorRegistry.SYSTEM_EXTERNAL_EDITOR_ID
340: : editor.getId();
341:
342: ((WorkbenchPage) page).openEditor(new FileEditorInput(
343: file), editorId, true, MATCH_BOTH);
344: // only remember the default editor if the open succeeds
345: IDE.setDefaultEditor(file, editorId);
346: }
347: } catch (PartInitException e) {
348: DialogUtil.openError(page.getWorkbenchWindow().getShell(),
349: IDEWorkbenchMessages.OpenWithMenu_dialogTitle, e
350: .getMessage(), e);
351: }
352: }
353:
354: /**
355: * Creates the menu item for clearing the current selection.
356: *
357: * @param menu the menu to add the item to
358: * @param file the file being edited
359: */
360: private void createDefaultMenuItem(Menu menu, final IFile file) {
361: final MenuItem menuItem = new MenuItem(menu, SWT.RADIO);
362: menuItem.setSelection(IDE.getDefaultEditor(file) == null);
363: menuItem
364: .setText(IDEWorkbenchMessages.DefaultEditorDescription_name);
365:
366: Listener listener = new Listener() {
367: public void handleEvent(Event event) {
368: switch (event.type) {
369: case SWT.Selection:
370: if (menuItem.getSelection()) {
371: IDE.setDefaultEditor(file, null);
372: try {
373: IEditorDescriptor desc = IDE
374: .getEditorDescriptor(file);
375: page.openEditor(new FileEditorInput(file),
376: desc.getId(), true, MATCH_BOTH);
377: } catch (PartInitException e) {
378: DialogUtil
379: .openError(
380: page.getWorkbenchWindow()
381: .getShell(),
382: IDEWorkbenchMessages.OpenWithMenu_dialogTitle,
383: e.getMessage(), e);
384: }
385: }
386: break;
387: }
388: }
389: };
390:
391: menuItem.addListener(SWT.Selection, listener);
392: }
393: }
|