Bug 107436 - [IDE] navigator: show file/folder in "Windows Explorer",
Finder, Nautilus

Use a platform dependent command to open the external file explorer

Signed-off-by: Alexandros Karypidis <akarypid@yahoo.gr>
Change-Id: I849baa6c86bef25422b1eebd501693a0640aa768
diff --git a/bundles/org.eclipse.ui.ide/plugin.properties b/bundles/org.eclipse.ui.ide/plugin.properties
index 88c04fb..91aecfd 100644
--- a/bundles/org.eclipse.ui.ide/plugin.properties
+++ b/bundles/org.eclipse.ui.ide/plugin.properties
@@ -195,6 +195,9 @@
 command.showResourceByPath.description= Show a resource in the Navigator given its path
 commandParameter.showResourceByPath.resourcePath.name= Resource Path
 
+command.showInSystemExplorer.name = Show In (System Explorer)
+command.showInSystemExplorer.description = Show in system's explorer (file manager)
+
 KeyBindingActionSet.label = Keyboard Shortcuts
 KeyBindingActionSet.showKeyAssist.label = &Key Assist...
 
diff --git a/bundles/org.eclipse.ui.ide/plugin.xml b/bundles/org.eclipse.ui.ide/plugin.xml
index 66688ca..eaae206 100644
--- a/bundles/org.eclipse.ui.ide/plugin.xml
+++ b/bundles/org.eclipse.ui.ide/plugin.xml
@@ -999,6 +999,12 @@
             id="org.eclipse.ui.ide.copyConfigCommand"
             name="%command.copyConfigCommand.name">
       </command>
+      <command
+            categoryId="org.eclipse.ui.category.navigate"
+            name="%command.showInSystemExplorer.name"
+            id="org.eclipse.ui.ide.showInSystemExplorer"
+            description="%command.showInSystemExplorer.description">
+      </command>
    </extension>
    
    <extension
@@ -2034,6 +2040,13 @@
                style="push">
          </command>
       </menuContribution>
+      <menuContribution
+            locationURI="popup:org.eclipse.ui.menus.showInMenu">
+ 	     <command
+             commandId="org.eclipse.ui.ide.showInSystemExplorer"
+             style="push">
+         </command>
+      </menuContribution>
    </extension>
    <extension
          point="org.eclipse.ui.handlers">
@@ -2217,6 +2230,18 @@
             </iterate>
          </enabledWhen>
       </handler>
+      <handler
+            class="org.eclipse.ui.internal.ide.handlers.ShowInSystemExplorerHandler"
+            commandId="org.eclipse.ui.ide.showInSystemExplorer">
+         <enabledWhen>
+            <with variable="selection">
+                    <count value="1" />
+                    <iterate ifEmpty="false">
+                        <adapt type="org.eclipse.core.resources.IResource" />
+                    </iterate>
+             </with>
+         </enabledWhen>
+      </handler>
    </extension>
    <extension
          point="org.eclipse.core.expressions.propertyTesters">
diff --git a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEInternalPreferences.java b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEInternalPreferences.java
index 479d2b5..228bd43 100644
--- a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEInternalPreferences.java
+++ b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEInternalPreferences.java
@@ -118,5 +118,12 @@
     /**
      * Workspace name, will be displayed in the window title.
      */
-	public static final String WORKSPACE_NAME = "WORKSPACE_NAME"; //$NON-NLS-1$
+    public static final String WORKSPACE_NAME = "WORKSPACE_NAME"; //$NON-NLS-1$
+
+    /**
+     * System explore command, used to launch file manager showing selected resource.
+     */
+    public static final String WORKBENCH_SYSTEM_EXPLORER = "SYSTEM_EXPLORER"; //$NON-NLS-1$
+    
+    
 }
diff --git a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEPreferenceInitializer.java b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEPreferenceInitializer.java
index 279d539..806f376 100644
--- a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEPreferenceInitializer.java
+++ b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEPreferenceInitializer.java
@@ -17,6 +17,7 @@
 import org.eclipse.ui.IWorkbenchActionConstants;
 import org.eclipse.ui.IWorkbenchPreferenceConstants;
 import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.internal.ide.handlers.ShowInSystemExplorerHandler;
 import org.eclipse.ui.internal.views.markers.MarkerSupportInternalUtilities;
 
 /**
@@ -87,6 +88,8 @@
 		
 		node.put(IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_MODE, IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_MODE_PROMPT);
 		node.put(IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_VIRTUAL_FOLDER_MODE, IDEInternalPreferences.IMPORT_FILES_AND_FOLDERS_MODE_PROMPT);
+
+		node.put(IDEInternalPreferences.WORKBENCH_SYSTEM_EXPLORER, ShowInSystemExplorerHandler.getDefaultCommand());
 	}
 
 	private String getHelpSeparatorKey(String groupId) {
diff --git a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java
index 56041f6..a13bb3c 100644
--- a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java
+++ b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java
@@ -513,6 +513,11 @@
 	public static String IDEWorkspacePreference_openReferencedProjects;
 	public static String IDEWorkspacePreference_closeUnrelatedProjectsToolTip;
 	public static String IDEWorkspacePreference_workspaceName;
+	public static String IDEWorkbenchPreference_workbenchSystemExplorer;
+
+	public static String ShowInSystemExplorerHandler_LinuxDefaultCommand;
+	public static String ShowInSystemExplorerHandler_MacOSXDefaultCommand;
+	public static String ShowInSystemExplorerHandler_Win32DefaultCommand;
 
 	// --- Linked Resources ---
 	public static String LinkedResourcesPreference_explanation;
diff --git a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/dialogs/IDEWorkspacePreferencePage.java b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/dialogs/IDEWorkspacePreferencePage.java
index 45f370d..957be9d 100644
--- a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/dialogs/IDEWorkspacePreferencePage.java
+++ b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/dialogs/IDEWorkspacePreferencePage.java
@@ -82,6 +82,8 @@
 
 	private RadioGroupFieldEditor openReferencesEditor;
 
+	private StringFieldEditor systemExplorer;
+
     /*
      * (non-Javadoc)
      * 
@@ -115,6 +117,10 @@
 		createSpace(composite);
 		
 		createOpenPrefControls(composite);
+
+		createSpace(composite);
+		createSystemExplorerGroup(composite);
+		createSpace(composite);
 		
 		Composite lower = new Composite(composite,SWT.NONE);
 		GridLayout lowerLayout = new GridLayout();
@@ -323,6 +329,38 @@
 		lineSeparatorEditor = new LineDelimiterEditor(lineComposite);
 		lineSeparatorEditor.doLoad();
     }
+
+	/**
+	 * Create the widget for the system explorer command.
+	 * 
+	 * @param composite
+	 */
+	protected void createSystemExplorerGroup(Composite composite) {
+		Composite groupComposite = new Composite(composite, SWT.LEFT);
+		GridLayout layout = new GridLayout();
+		layout.numColumns = 2;
+		groupComposite.setLayout(layout);
+		GridData gd = new GridData();
+		gd.horizontalAlignment = GridData.FILL;
+		gd.grabExcessHorizontalSpace = true;
+		groupComposite.setLayoutData(gd);
+
+		systemExplorer = new StringFieldEditor(IDEInternalPreferences.WORKBENCH_SYSTEM_EXPLORER,
+				IDEWorkbenchMessages.IDEWorkbenchPreference_workbenchSystemExplorer, 40, groupComposite);
+		systemExplorer.setPreferenceStore(getIDEPreferenceStore());
+		systemExplorer.setPage(this);
+
+		systemExplorer.load();
+
+		systemExplorer.setPropertyChangeListener(new IPropertyChangeListener() {
+			public void propertyChange(PropertyChangeEvent event) {
+				if (event.getProperty().equals(FieldEditor.IS_VALID)) {
+					setValid(systemExplorer.isValid());
+				}
+			}
+		});
+	}
+
     /**
      * Returns the IDE preference store.
      * @return the preference store.
@@ -408,6 +446,8 @@
 		lineSeparatorEditor.loadDefault();
 		openReferencesEditor.loadDefault();
 
+		systemExplorer.loadDefault();
+
         super.performDefaults();
     }
 
@@ -459,7 +499,9 @@
         }
         
         workspaceName.store();
-        
+
+		systemExplorer.store();
+
         Preferences preferences = ResourcesPlugin.getPlugin()
                 .getPluginPreferences();
 
@@ -478,7 +520,8 @@
         encodingEditor.store();
 		lineSeparatorEditor.store();
 		openReferencesEditor.store();
-        return super.performOk();
+
+		return super.performOk();
     }
 
 }
diff --git a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/handlers/ShowInSystemExplorerHandler.java b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/handlers/ShowInSystemExplorerHandler.java
new file mode 100644
index 0000000..87eb6b3
--- /dev/null
+++ b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/handlers/ShowInSystemExplorerHandler.java
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * Copyright (c) 2013 IBM Corporation 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:
+ *     IBM Corporation - initial API and implementation
+ ******************************************************************************/
+
+package org.eclipse.ui.internal.ide.handlers;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.common.NotDefinedException;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.ILog;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.util.Util;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.handlers.HandlerUtil;
+import org.eclipse.ui.internal.ide.IDEInternalPreferences;
+import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
+import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
+
+/**
+ * @since 3.106
+ */
+public class ShowInSystemExplorerHandler extends AbstractHandler {
+
+	/**
+	 * Command id
+	 */
+	public static final String ID = "org.eclipse.ui.showIn.systemExplorer"; //$NON-NLS-1$
+
+	private static final String VARIABLE_RE = "\\$\\{selected_resource_loc\\}"; //$NON-NLS-1$
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.
+	 * ExecutionEvent)
+	 */
+	public Object execute(ExecutionEvent event) throws ExecutionException {
+		ILog log = IDEWorkbenchPlugin.getDefault().getLog();
+
+		ISelection selection = HandlerUtil.getCurrentSelection(event);
+		if ((selection == null) || (selection.isEmpty())
+				|| (!(selection instanceof IStructuredSelection))) {
+			return null;
+		}
+
+		Object selectedObject = ((IStructuredSelection) selection)
+				.getFirstElement();
+		IResource item = (IResource) org.eclipse.ui.internal.util.Util
+				.getAdapter(selectedObject, IResource.class);
+		if (item == null) {
+			return null;
+		}
+
+		String logMsgPrefix;
+		try {
+			logMsgPrefix = event.getCommand().getName() + ": "; //$NON-NLS-1$
+		} catch (NotDefinedException e) {
+			// will used id instead...
+			logMsgPrefix = event.getCommand().getId() + ": "; //$NON-NLS-1$
+		}
+
+		try {
+			String canonicalPath = getSystemExplorerPath(item);
+			if (canonicalPath == null) {
+				ErrorDialog
+						.openError(
+								Display.getDefault().getActiveShell(),
+								"Could not determine resource location", //$NON-NLS-1$
+								"The system could not determine the selected resource's location.", //$NON-NLS-1$
+								new Status(
+										IStatus.ERROR,
+										IDEWorkbenchPlugin.getDefault()
+												.getBundle().getSymbolicName(),
+										logMsgPrefix
+												+ "Could not determine resource's location.")); //$NON-NLS-1$
+				return null;
+			}
+			String launchCmd = formShowInSytemExplorerCommand(canonicalPath);
+
+			if ("".equals(launchCmd)) { //$NON-NLS-1$
+				ErrorDialog
+						.openError(
+								Display.getDefault().getActiveShell(),
+								"System explorer is not set", //$NON-NLS-1$
+								"Please set the system explorer command in the workbench preferences.", //$NON-NLS-1$
+								new Status(IStatus.ERROR, IDEWorkbenchPlugin
+										.getDefault().getBundle()
+										.getSymbolicName(), logMsgPrefix
+										+ "Command for launching is not set.")); //$NON-NLS-1$
+				return null;
+			}
+
+			log.log(new Status(IStatus.INFO, IDEWorkbenchPlugin.getDefault()
+					.getBundle().getSymbolicName(), launchCmd));
+
+			Process p = Runtime.getRuntime().exec(launchCmd, null,
+					item.getWorkspace().getRoot().getLocation().toFile());
+			int retCode = p.waitFor();
+			if (retCode != 0) {
+				log.log(new Status(
+						IStatus.ERROR,
+						IDEWorkbenchPlugin.getDefault().getBundle()
+								.getSymbolicName(),
+						logMsgPrefix
+								+ "Execution of launch command failed with return code: " + retCode)); //$NON-NLS-1$
+			}
+		} catch (Exception e) {
+			log.log(new Status(IStatus.ERROR, IDEWorkbenchPlugin.getDefault()
+					.getBundle().getSymbolicName(), logMsgPrefix
+					+ "Unhandled failure.", e)); //$NON-NLS-1$
+			throw new ExecutionException("Show in Explorer command failed.", e); //$NON-NLS-1$
+		}
+		return null;
+	}
+
+	/**
+	 * Prepare command for launching system explorer to show a path
+	 * 
+	 * @param path
+	 *            the path to show
+	 * @return the command that shows the path
+	 */
+	private String formShowInSytemExplorerCommand(String path) {
+		String command = IDEWorkbenchPlugin.getDefault().getPreferenceStore()
+				.getString(IDEInternalPreferences.WORKBENCH_SYSTEM_EXPLORER);
+		return command.replaceAll(VARIABLE_RE, path);
+	}
+
+	/**
+	 * Returns the path used for a resource when showing it in the system
+	 * explorer
+	 * 
+	 * @see File#getCanonicalPath()
+	 * @param resource
+	 *            the {@link IResource} object to be used
+	 * @return the canonical path to show in the system explorer for this
+	 *         resource, or null if it cannot be determined
+	 * @throws IOException
+	 *             if an I/O error occurs while trying to determine the path
+	 */
+	private String getSystemExplorerPath(IResource resource) throws IOException {
+		IPath location = resource.getLocation();
+		if (location == null)
+			return null;
+		File f = location.toFile();
+		return f.getCanonicalPath();
+	}
+
+	/**
+	 * The default command for launching the system explorer on this platform.
+	 * 
+	 * @return The default command which launches the system explorer on this
+	 *         system, or an empty string if no default exists.
+	 */
+	public static String getDefaultCommand() {
+		if (Util.isGtk()) {
+			return IDEWorkbenchMessages.ShowInSystemExplorerHandler_LinuxDefaultCommand;
+		} else if (Util.isWindows()) {
+			return IDEWorkbenchMessages.ShowInSystemExplorerHandler_Win32DefaultCommand;
+		} else if (Util.isMac()) {
+			return IDEWorkbenchMessages.ShowInSystemExplorerHandler_MacOSXDefaultCommand;
+		}
+
+		// if all else fails, return empty default
+		return ""; //$NON-NLS-1$
+	}
+}
diff --git a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties
index f710cc1..cdc9bff 100644
--- a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties
+++ b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties
@@ -488,6 +488,11 @@
 IDEWorkspacePreference_openReferencedProjects = Open referenced projects when a project is opened
 IDEWorkspacePreference_closeUnrelatedProjectsToolTip = Close unrelated projects without prompt
 IDEWorkspacePreference_workspaceName=Wor&kspace name (shown in window title):
+IDEWorkbenchPreference_workbenchSystemExplorer=Command for launching system explorer.
+
+ShowInSystemExplorerHandler_LinuxDefaultCommand=gdbus call -e -d org.freedesktop.FileManager1 -o /org/freedesktop/FileManager1 -m org.freedesktop.FileManager1.ShowItems ['file://${selected_resource_loc}'] ''
+ShowInSystemExplorerHandler_Win32DefaultCommand=explorer /E,/select=${selected_resource_loc} 
+ShowInSystemExplorerHandler_MacOSXDefaultCommand=open -R '${selected_resource_loc}'
 
 # --- Linked Resources ---
 LinkedResourcesPreference_explanation = Path variables specify locations in the file system. The locations of linked resources\nmay be specified relative to these path variables.