From 7c97044b3d767978f55ebfe63c2f92e252bd7c8f Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 5 Apr 2012 19:03:08 +0200 Subject: [PATCH] IDEA-84112 (check for available terminal emulators; deprecate isKDE/isGNOME as unreliable) --- .../com/intellij/execution/util/ExecUtil.java | 99 +++++++++++++++++-- .../openapi/fileTypes/NativeFileType.java | 33 +++---- .../ide/actions/CreateDesktopEntryAction.java | 4 +- .../com/intellij/openapi/util/SystemInfo.java | 9 +- .../plugins/groovy/mvc/MvcFramework.java | 22 ++--- .../groovy/mvc/MvcRunTargetDialog.java | 8 +- 6 files changed, 127 insertions(+), 48 deletions(-) diff --git a/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java b/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java index 52b65fbfed43..0c899705c85c 100644 --- a/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java +++ b/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java @@ -19,6 +19,7 @@ import com.intellij.execution.ExecutionException; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.process.CapturingProcessHandler; import com.intellij.execution.process.ProcessOutput; +import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import org.jetbrains.annotations.NotNull; @@ -30,6 +31,46 @@ import java.util.List; import java.util.Map; public class ExecUtil { + private static final NotNullLazyValue hasGkSudo = new NotNullLazyValue() { + @NotNull + @Override + protected Boolean compute() { + return new File("/usr/bin/gksudo").canExecute(); + } + }; + + private static final NotNullLazyValue hasKdeSudo = new NotNullLazyValue() { + @NotNull + @Override + protected Boolean compute() { + return new File("/usr/bin/kdesudo").canExecute(); + } + }; + + private static final NotNullLazyValue hasGnomeTerminal = new NotNullLazyValue() { + @NotNull + @Override + protected Boolean compute() { + return new File("/usr/bin/gnome-terminal").canExecute(); + } + }; + + private static final NotNullLazyValue hasKdeTerminal = new NotNullLazyValue() { + @NotNull + @Override + protected Boolean compute() { + return new File("/usr/bin/konsole").canExecute(); + } + }; + + private static final NotNullLazyValue hasXTerm = new NotNullLazyValue() { + @NotNull + @Override + protected Boolean compute() { + return new File("/usr/bin/xterm").canExecute(); + } + }; + private ExecUtil() { } public static int execAndGetResult(final String... command) throws ExecutionException, InterruptedException { @@ -87,8 +128,9 @@ public class ExecUtil { return "/usr/bin/open"; } - public static ProcessOutput execAndGetOutput(@NotNull final List command, @Nullable final String workDir) throws ExecutionException { - assert command.size() > 0 : command; + public static ProcessOutput execAndGetOutput(@NotNull final List command, + @Nullable final String workDir) throws ExecutionException { + assert command.size() > 0; final GeneralCommandLine commandLine = new GeneralCommandLine(command); commandLine.setWorkDirectory(workDir); final Process process = commandLine.createProcess(); @@ -124,13 +166,13 @@ public class ExecUtil { final String script = "do shell script \"" + scriptPath + "\" with administrator privileges"; return execAndGetOutput(Arrays.asList(getOsascriptPath(), "-e", script), workDir); } - else if (SystemInfo.isKDE) { + else if (hasKdeSudo.getValue()) { return execAndGetOutput(Arrays.asList("kdesudo", "--comment", prompt, scriptPath), workDir); } - else if (SystemInfo.isGnome) { + else if (hasGkSudo.getValue()) { return execAndGetOutput(Arrays.asList("gksudo", "--message", prompt, scriptPath), workDir); } - else if (SystemInfo.isUnix) { + else if (SystemInfo.isUnix && hasTerminalApp()) { final File sudo = createTempExecutableScript("sudo", ".sh", "#!/bin/sh\n" + "echo \"" + prompt + "\"\n" + @@ -140,15 +182,54 @@ public class ExecUtil { "echo\n" + "read -p \"Press Enter to close this window...\" TEMP\n" + "exit $STATUS\n"); - return execAndGetOutput(Arrays.asList("xterm", "-T", "Install", "-e", sudo.getAbsolutePath()), workDir); - } - else { - throw new UnsupportedOperationException("Unsupported OS/desktop: " + SystemInfo.OS_NAME + '/' + SystemInfo.SUN_DESKTOP); + return execAndGetOutput(getTerminalCommand("Install", sudo.getAbsolutePath()), workDir); } + + throw new UnsupportedSystemException(); } public static int sudoAndGetResult(@NotNull final String scriptPath, @NotNull final String prompt) throws IOException, ExecutionException { return sudoAndGetOutput(scriptPath, prompt).getExitCode(); } + + public static boolean hasTerminalApp() { + return SystemInfo.isWindows || SystemInfo.isMac || hasKdeTerminal.getValue() || hasGnomeTerminal.getValue() || hasXTerm.getValue(); + } + + public static List getTerminalCommand(@Nullable final String title, @NotNull final String command) { + if (SystemInfo.isWindows) { + return Arrays.asList("cmd.exe", "/c", "start", '"' + (title != null ? title : "") + '"', command); + } + else if (SystemInfo.isMac) { + return Arrays.asList(getOpenCommandPath(), "-a", "Terminal", command); // todo: title? + } + else if (hasKdeTerminal.getValue()) { + return Arrays.asList("/usr/bin/konsole", "-e", command); // todo: title? + } + else if (hasGnomeTerminal.getValue()) { + if (title != null) { + return Arrays.asList("/usr/bin/gnome-terminal", "-t", title, "-x", command); + } + else { + return Arrays.asList("/usr/bin/gnome-terminal", "-x", command); + } + } + else if (hasXTerm.getValue()) { + if (title != null) { + return Arrays.asList("/usr/bin/xterm", "-T", title, "-e", command); + } + else { + return Arrays.asList("/usr/bin/xterm", "-e", command); + } + } + + throw new UnsupportedSystemException(); + } + + public static class UnsupportedSystemException extends UnsupportedOperationException { + public UnsupportedSystemException() { + super("Unsupported OS/desktop: " + SystemInfo.OS_NAME + '/' + SystemInfo.SUN_DESKTOP); + } + } } diff --git a/platform/platform-api/src/com/intellij/openapi/fileTypes/NativeFileType.java b/platform/platform-api/src/com/intellij/openapi/fileTypes/NativeFileType.java index cf28c47f6a79..765fe5c5ae0f 100644 --- a/platform/platform-api/src/com/intellij/openapi/fileTypes/NativeFileType.java +++ b/platform/platform-api/src/com/intellij/openapi/fileTypes/NativeFileType.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,15 +15,14 @@ */ package com.intellij.openapi.fileTypes; +import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; -import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -31,11 +30,12 @@ import java.util.List; * @author yole */ public class NativeFileType implements INativeFileType { - private static final Icon ICON = IconLoader.getIcon("/fileTypes/custom.png"); - private NativeFileType() { } - public static final NativeFileType INSTANCE = new NativeFileType(); + private static final Icon ICON = IconLoader.getIcon("/fileTypes/custom.png"); + + private NativeFileType() { } + @NotNull public String getName() { return "Native"; @@ -68,7 +68,7 @@ public class NativeFileType implements INativeFileType { } @Override - public boolean openFileInAssociatedApplication(Project project, VirtualFile file) { + public boolean openFileInAssociatedApplication(final Project project, @NotNull final VirtualFile file) { return openAssociatedApplication(file); } @@ -77,8 +77,8 @@ public class NativeFileType implements INativeFileType { return true; } - public static boolean openAssociatedApplication(VirtualFile file) { - List commands = new ArrayList(); + public static boolean openAssociatedApplication(@NotNull final VirtualFile file) { + final List commands = new ArrayList(); if (SystemInfo.isWindows) { commands.add("rundll32.exe"); commands.add("url.dll,FileProtocolHandler"); @@ -86,23 +86,20 @@ public class NativeFileType implements INativeFileType { else if (SystemInfo.isMac) { commands.add("/usr/bin/open"); } - else if (SystemInfo.isKDE) { - commands.add("kfmclient"); - commands.add("exec"); - } - else if (SystemInfo.isGnome) { - commands.add("gnome-open"); + else if (SystemInfo.hasXdgOpen()) { + commands.add("xdg-open"); } else { return false; } commands.add(file.getPath()); + try { - Runtime.getRuntime().exec(ArrayUtil.toStringArray(commands)); + new GeneralCommandLine(commands).createProcess(); + return true; } - catch (IOException e) { + catch (Exception e) { return false; } - return true; } } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/CreateDesktopEntryAction.java b/platform/platform-impl/src/com/intellij/ide/actions/CreateDesktopEntryAction.java index a3b7a452409a..d1e12d19103d 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/CreateDesktopEntryAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/CreateDesktopEntryAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,7 +54,7 @@ public class CreateDesktopEntryAction extends DumbAwareAction { private static final int MIN_ICON_SIZE = 32; public static boolean isAvailable() { - return SystemInfo.isLinux || SystemInfo.isGnome || SystemInfo.isKDE; // KDE patched for FreeBSD, sure + return SystemInfo.hasXdgOpen(); } @Override diff --git a/platform/util/src/com/intellij/openapi/util/SystemInfo.java b/platform/util/src/com/intellij/openapi/util/SystemInfo.java index 5921da2f0e62..2d9f35d10cad 100644 --- a/platform/util/src/com/intellij/openapi/util/SystemInfo.java +++ b/platform/util/src/com/intellij/openapi/util/SystemInfo.java @@ -47,9 +47,10 @@ public class SystemInfo extends SystemInfoRt { public static final boolean isSolaris = _OS_NAME.startsWith("sunos"); public static final boolean isUnix = SystemInfoRt.isUnix; - private static final String _SUN_DESKTOP = SUN_DESKTOP.toLowerCase(); - public static final boolean isKDE = _SUN_DESKTOP.contains("kde"); - public static final boolean isGnome = _SUN_DESKTOP.contains("gnome"); + /** @deprecated inaccurate (to remove in IDEA 13) */ + public static final boolean isKDE = SUN_DESKTOP.toLowerCase().contains("kde"); + /** @deprecated inaccurate (to remove in IDEA 13) */ + public static final boolean isGnome = SUN_DESKTOP.toLowerCase().contains("gnome"); public static final boolean isMacSystemMenu = isMac && "true".equals(System.getProperty("apple.laf.useScreenMenuBar")); @@ -63,7 +64,7 @@ public class SystemInfo extends SystemInfoRt { public static final boolean isMacIntel64 = isMac && "x86_64".equals(OS_ARCH); /** @deprecated use {@linkplain #hasXdgOpen()} (to remove in IDEA 13) */ - public static final boolean hasXdgOpen = false; + public static final boolean hasXdgOpen = isLinux; private static final NotNullLazyValue ourHasXdgOpen = new AtomicNotNullLazyValue() { @NotNull @Override diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcFramework.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcFramework.java index 8b0510528336..4fdb8e73bc98 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcFramework.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcFramework.java @@ -1,17 +1,17 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2012 JetBrains s.r.o. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.jetbrains.plugins.groovy.mvc; @@ -115,7 +115,7 @@ public abstract class MvcFramework { } } - public boolean isInteractiveConsoleSupport(@NotNull Module module) { + public boolean isInteractiveConsoleSupported(@NotNull Module module) { return false; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTargetDialog.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTargetDialog.java index f6a802abc6c7..d1da3439d662 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTargetDialog.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunTargetDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -73,7 +73,7 @@ public class MvcRunTargetDialog extends DialogWrapper { boolean hasOneSupportedModule = false; for (Module module : ModuleManager.getInstance(myModule.getProject()).getModules()) { if (module == myModule || myFramework.hasSupport(module)) { - if (myFramework.isInteractiveConsoleSupport(module)) { + if (myFramework.isInteractiveConsoleSupported(module)) { hasOneSupportedModule = true; break; } @@ -89,7 +89,7 @@ public class MvcRunTargetDialog extends DialogWrapper { } }; - myInteractiveRunAction.setEnabled(myFramework.isInteractiveConsoleSupport(myModule)); + myInteractiveRunAction.setEnabled(myFramework.isInteractiveConsoleSupported(myModule)); return new Action[]{myInteractiveRunAction}; } @@ -120,7 +120,7 @@ public class MvcRunTargetDialog extends DialogWrapper { public void actionPerformed(ActionEvent e) { myModule = (Module)myModuleBox.getSelectedItem(); if (myInteractiveRunAction != null) { - myInteractiveRunAction.setEnabled(myFramework.isInteractiveConsoleSupport(myModule)); + myInteractiveRunAction.setEnabled(myFramework.isInteractiveConsoleSupported(myModule)); } } });