/*******************************************************************************
* Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved.
* This program and the accompanying materials are made available under the terms
* of the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* William Chen (Wind River) [360494]Provide an "Open With" action in the pop
* up menu of file system nodes of Target Explorer.
*******************************************************************************/
package org.eclipse.tcf.te.tcf.filesystem.ui.internal.handlers;
import java.io.File;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.action.ContributionItem;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.runtime.IFSTreeNode;
import org.eclipse.tcf.te.tcf.filesystem.ui.activator.UIPlugin;
import org.eclipse.tcf.te.tcf.filesystem.ui.internal.operations.UiExecutor;
import org.eclipse.tcf.te.tcf.filesystem.ui.nls.Messages;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.EditorSelectionDialog;
import org.eclipse.ui.ide.FileStoreEditorInput;
/**
* A menu for opening files in the target explorer.
* <p>
* An <code>OpenWithMenu</code> is used to populate a menu with "Open With" actions. One action is
* added for each editor which is applicable to the selected file. If the user selects one of these
* items, the corresponding editor is opened on the file.
* </p>
*
* @since 3.7 - Copied and modified based on org.eclipse.ui.actions.OpenWithMenu to avoid
* introducing org.eclipse.core.resources
*/
public class OpenWithMenu extends ContributionItem {
private static final String DEFAULT_TEXT_EDITOR = "org.eclipse.ui.DefaultTextEditor"; //$NON-NLS-1$
/**
* The id of this action.
*/
public static final String ID = UIPlugin.getUniqueIdentifier() + ".OpenWithMenu";//$NON-NLS-1$
/*
* Compares the labels from two IEditorDescriptor objects
*/
private static final Comparator<IEditorDescriptor> comparer = new Comparator<IEditorDescriptor>() {
private Collator collator = Collator.getInstance();
@Override
public int compare(IEditorDescriptor arg0, IEditorDescriptor arg1) {
String s1 = arg0.getLabel();
String s2 = arg1.getLabel();
return collator.compare(s1, s2);
}
};
// The selected tree node.
IFSTreeNode node;
// The current workbench page.
IWorkbenchPage page;
// The editor registry.
IEditorRegistry registry;
/**
* Create an instance using the specified page and the specified IFSTreeNode.
*
* @param page The page to open the editor.
* @param node The IFSTreeNode to be opened.
*/
public OpenWithMenu(IWorkbenchPage page, IFSTreeNode node) {
super(ID);
this.node = node;
this.page = page;
this.registry = PlatformUI.getWorkbench().getEditorRegistry();
}
/**
* Returns an image to show for the corresponding editor descriptor.
*
* @param editorDesc the editor descriptor, or null for the system editor
* @return the image or null
*/
private Image getImage(IEditorDescriptor editorDesc) {
ImageDescriptor imageDesc = getImageDescriptor(editorDesc);
if (imageDesc == null) {
return null;
}
return imageDesc.createImage();
}
/**
* Returns the image descriptor for the given editor descriptor, or null if it has no image.
*/
private ImageDescriptor getImageDescriptor(IEditorDescriptor editorDesc) {
ImageDescriptor imageDesc = null;
if (editorDesc == null) {
imageDesc = registry.getImageDescriptor(node.getName());
// TODO: is this case valid, and if so, what are the implications for content-simulator
// editor bindings?
}
else {
imageDesc = editorDesc.getImageDescriptor();
}
if (imageDesc == null) {
if (editorDesc != null && editorDesc.getId().equals(IEditorRegistry.SYSTEM_EXTERNAL_EDITOR_ID)) {
imageDesc = registry.getSystemExternalEditorImageDescriptor(node.getName());
}
}
return imageDesc;
}
/**
* Creates the menu item for the editor descriptor.
*
* @param menu the menu to add the item to
* @param descriptor the editor descriptor, or null for the system editor
* @param preferredEditor the descriptor of the preferred editor, or <code>null</code>
*/
private void createMenuItem(Menu menu, final IEditorDescriptor descriptor, final IEditorDescriptor preferredEditor) {
final MenuItem menuItem = new MenuItem(menu, SWT.RADIO);
boolean isPreferred = preferredEditor != null && descriptor.getId()
.equals(preferredEditor.getId());
menuItem.setSelection(isPreferred);
menuItem.setText(descriptor.getLabel());
Image image = getImage(descriptor);
if (image != null) {
menuItem.setImage(image);
}
Listener listener = new Listener() {
@Override
public void handleEvent(Event event) {
switch (event.type) {
case SWT.Selection:
if (menuItem.getSelection()) {
syncOpen(descriptor, false);
}
break;
default:
break;
}
}
};
menuItem.addListener(SWT.Selection, listener);
}
/**
* Creates the Other... menu item
*
* @param menu the menu to add the item to
*/
@SuppressWarnings("unused")
private void createOtherMenuItem(final Menu menu) {
new MenuItem(menu, SWT.SEPARATOR);
final MenuItem menuItem = new MenuItem(menu, SWT.PUSH);
menuItem.setText(Messages.OpenWithMenu_OpenWith);
Listener listener = new Listener() {
@Override
public void handleEvent(Event event) {
switch (event.type) {
case SWT.Selection:
EditorSelectionDialog dialog = new EditorSelectionDialog(menu.getShell());
dialog.setMessage(NLS
.bind(Messages.OpenWithMenu_ChooseEditorForOpening, node.getName()));
if (dialog.open() == Window.OK) {
IEditorDescriptor editor = dialog.getSelectedEditor();
if (editor != null) {
syncOpen(editor, editor.isOpenExternal());
}
}
break;
default:
break;
}
}
};
menuItem.addListener(SWT.Selection, listener);
}
/**
* Get the default editor for this IFSTreeNode.
*
* @return The descriptor of the default editor.
*/
private IEditorDescriptor getDefaultEditor() {
// Try file specific editor.
try {
String editorID = node.getPreferredEditorID();
if (editorID != null) {
IEditorDescriptor desc = registry.findEditor(editorID);
if (desc != null) {
return desc;
}
}
}
catch (Exception e) {
// do nothing
}
// Try lookup with filename
return registry.getDefaultEditor(node.getName(), node.getContentType());
}
/*
* (non-Javadoc) Fills the menu with perspective items.
*/
@SuppressWarnings("unused")
@Override
public void fill(Menu menu, int index) {
IEditorDescriptor defaultEditor = registry.findEditor(DEFAULT_TEXT_EDITOR);
IEditorDescriptor preferredEditor = getDefaultEditor();
IEditorDescriptor[] editors = registry.getEditors(node.getName(), node.getContentType());
Collections.sort(Arrays.asList(editors), comparer);
boolean defaultFound = false;
// Check that we don't add it twice. This is possible
// if the same editor goes to two mappings.
ArrayList<IEditorDescriptor> alreadyMapped = new ArrayList<IEditorDescriptor>();
for (int i = 0; i < editors.length; i++) {
IEditorDescriptor editor = editors[i];
if (!alreadyMapped.contains(editor)) {
createMenuItem(menu, editor, preferredEditor);
if (defaultEditor != null && editor.getId().equals(defaultEditor.getId())) {
defaultFound = true;
}
alreadyMapped.add(editor);
}
}
// Only add a separator if there is something to separate
if (editors.length > 0) {
new MenuItem(menu, SWT.SEPARATOR);
}
// Add default editor. Check it if it is saved as the preference.
if (!defaultFound && defaultEditor != null) {
createMenuItem(menu, defaultEditor, preferredEditor);
}
// Add system editor (should never be null)
IEditorDescriptor descriptor = registry
.findEditor(IEditorRegistry.SYSTEM_EXTERNAL_EDITOR_ID);
createMenuItem(menu, descriptor, preferredEditor);
createDefaultMenuItem(menu);
// add Other... menu item
createOtherMenuItem(menu);
}
/*
* (non-Javadoc) Returns whether this menu is dynamic.
*/
@Override
public boolean isDynamic() {
return true;
}
/**
* Creates the menu item for clearing the current selection.
*
* @param menu the menu to add the item to
* @param file the file being edited
*/
private void createDefaultMenuItem(Menu menu) {
final MenuItem menuItem = new MenuItem(menu, SWT.RADIO);
menuItem.setSelection(getDefaultEditor() == null);
menuItem.setText(Messages.OpenWithMenu_DefaultEditor);
Listener listener = new Listener() {
@Override
public void handleEvent(Event event) {
switch (event.type) {
case SWT.Selection:
if (menuItem.getSelection()) {
node.setPreferredEditorID(null);
try {
syncOpen(getEditorDescriptor(), false);
}
catch (PartInitException e) {
e.printStackTrace();
}
}
break;
default:
break;
}
}
};
menuItem.addListener(SWT.Selection, listener);
}
/**
* Get an appropriate editor for the IFSTreeNode. If the default editor is not found, it will
* search the in-place editor, the external editor and finally the default text editor.
*
* @return An appropriate editor to open the node using "Default Editor".
* @throws PartInitException
*/
protected IEditorDescriptor getEditorDescriptor() throws PartInitException {
IEditorDescriptor defaultDescriptor = getDefaultEditor();
if (defaultDescriptor != null) {
return defaultDescriptor;
}
IEditorDescriptor editorDesc = null;
// next check the OS for in-place editor (OLE on Win32)
if (registry.isSystemInPlaceEditorAvailable(node.getName())) {
editorDesc = registry.findEditor(IEditorRegistry.SYSTEM_INPLACE_EDITOR_ID);
}
// next check with the OS for an external editor
if (editorDesc == null && registry.isSystemExternalEditorAvailable(node.getName())) {
editorDesc = registry.findEditor(IEditorRegistry.SYSTEM_EXTERNAL_EDITOR_ID);
}
// next lookup the default text editor
if (editorDesc == null) {
editorDesc = registry.findEditor(DEFAULT_TEXT_EDITOR);
}
// if no valid editor found, bail out
if (editorDesc == null) {
throw new PartInitException(Messages.OpenWithMenu_NoEditorFound);
}
return editorDesc;
}
/**
* Synchronize and open the file using the specified editor descriptor. If openUsingDescriptor
* is true, it will try to use an external editor to open it if an eclipse editor is not
* available.
*
* @param editorDescriptor The editor descriptor used to open the node.
* @param openUsingDescriptor If an external editor should be used to open the node.
*/
protected void syncOpen(IEditorDescriptor editorDescriptor, boolean openUsingDescriptor) {
File file = node.getCacheFile();
if (!file.exists() && !UiExecutor.execute(node.operationDownload(null)).isOK()) {
return;
}
openInEditor(editorDescriptor, openUsingDescriptor);
}
/**
* Open the editor using the specified editor descriptor. If openUsingDescriptor is true, it
* will try to use an external editor to open it if an eclipse editor is not available.
*
* @param editorDescriptor The editor descriptor used to open the node.
* @param openUsingDescriptor If an external editor should be used to open the node.
*/
private void openInEditor(IEditorDescriptor editorDescriptor, boolean openUsingDescriptor) {
try {
IPath path = new Path(node.getCacheFile().getAbsolutePath());
IFileStore fileStore = EFS.getLocalFileSystem().getStore(path);
FileStoreEditorInput input = new FileStoreEditorInput(fileStore);
if (openUsingDescriptor) {
String editorId = editorDescriptor.getId();
page.openEditor(input, editorId, true, IWorkbenchPage.MATCH_INPUT | IWorkbenchPage.MATCH_ID);
}
else {
String editorId = IEditorRegistry.SYSTEM_EXTERNAL_EDITOR_ID;
if (editorDescriptor != null) editorId = editorDescriptor.getId();
page.openEditor(input, editorId, true, IWorkbenchPage.MATCH_INPUT | IWorkbenchPage.MATCH_ID);
node.setPreferredEditorID(editorId);
}
}
catch (PartInitException e) {
e.printStackTrace();
}
}
}