/* * Copyright 2000-2014 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 * * 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. */ package com.intellij.ide.actions; import com.intellij.execution.ExecutionException; import com.intellij.execution.util.ExecUtil; import com.intellij.notification.Notification; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.ApplicationBundle; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.AppUIUtil; import com.intellij.util.PlatformUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.io.File; import java.io.IOException; import java.util.Locale; import static com.intellij.util.containers.ContainerUtil.newHashMap; import static java.util.Arrays.asList; public class CreateDesktopEntryAction extends DumbAwareAction { private static final Logger LOG = Logger.getInstance("#com.intellij.ide.actions.CreateDesktopEntryAction"); public static boolean isAvailable() { return SystemInfo.isUnix && SystemInfo.hasXdgOpen(); } @Override public void update(final AnActionEvent event) { final boolean enabled = isAvailable(); final Presentation presentation = event.getPresentation(); presentation.setEnabled(enabled); presentation.setVisible(enabled); } @Override public void actionPerformed(final AnActionEvent event) { if (!isAvailable()) return; final Project project = event.getProject(); final CreateDesktopEntryDialog dialog = new CreateDesktopEntryDialog(project); dialog.show(); if (!dialog.isOK()) { return; } final boolean globalEntry = dialog.myGlobalEntryCheckBox.isSelected(); ProgressManager.getInstance().run(new Task.Backgroundable(project, event.getPresentation().getText()) { @Override public void run(@NotNull final ProgressIndicator indicator) { createDesktopEntry(getProject(), indicator, globalEntry); } }); } public static void createDesktopEntry(@Nullable final Project project, @NotNull final ProgressIndicator indicator, final boolean globalEntry) { if (!isAvailable()) return; final double step = (1.0 - indicator.getFraction()) / 3.0; try { indicator.setText(ApplicationBundle.message("desktop.entry.checking")); check(); indicator.setFraction(indicator.getFraction() + step); indicator.setText(ApplicationBundle.message("desktop.entry.preparing")); final File entry = prepare(); indicator.setFraction(indicator.getFraction() + step); indicator.setText(ApplicationBundle.message("desktop.entry.installing")); install(entry, globalEntry); indicator.setFraction(indicator.getFraction() + step); final String message = ApplicationBundle.message("desktop.entry.success", ApplicationNamesInfo.getInstance().getProductName()); if (ApplicationManager.getApplication() != null) { Notifications.Bus .notify(new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, "Desktop entry created", message, NotificationType.INFORMATION)); } } catch (Exception e) { if (ApplicationManager.getApplication() == null) { throw new RuntimeException(e); } final String message = e.getMessage(); if (!StringUtil.isEmptyOrSpaces(message)) { LOG.warn(e); Notifications.Bus.notify( new Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID, "Failed to create desktop entry", message, NotificationType.ERROR), project ); } else { LOG.error(e); } } } private static void check() throws ExecutionException, InterruptedException { final int result = ExecUtil.execAndGetResult("which", "xdg-desktop-menu"); if (result != 0) throw new RuntimeException(ApplicationBundle.message("desktop.entry.xdg.missing")); } private static File prepare() throws IOException { final String homePath = PathManager.getHomePath(); assert new File(homePath).isDirectory() : "Invalid home path: '" + homePath + "'"; final String binPath = homePath + "/bin"; assert new File(binPath).isDirectory() : "Invalid bin path: '" + binPath + "'"; String name = ApplicationNamesInfo.getInstance().getFullProductName(); if (PlatformUtils.isIdeaCommunity()) name += " Community Edition"; final String iconPath = AppUIUtil.findIcon(binPath); if (iconPath == null) { throw new RuntimeException(ApplicationBundle.message("desktop.entry.icon.missing", binPath)); } final String execPath = findScript(binPath); if (execPath == null) { throw new RuntimeException(ApplicationBundle.message("desktop.entry.script.missing", binPath)); } final String wmClass = AppUIUtil.getFrameClass(); final String content = ExecUtil.loadTemplate(CreateDesktopEntryAction.class.getClassLoader(), "entry.desktop", newHashMap(asList("$NAME$", "$SCRIPT$", "$ICON$", "$WM_CLASS$"), asList(name, execPath, iconPath, wmClass))); final String entryName = wmClass + ".desktop"; final File entryFile = new File(FileUtil.getTempDirectory(), entryName); FileUtil.writeToFile(entryFile, content); entryFile.deleteOnExit(); return entryFile; } @Nullable private static String findScript(String binPath) { String productName = ApplicationNamesInfo.getInstance().getProductName(); String execPath = binPath + '/' + productName + ".sh"; if (new File(execPath).canExecute()) return execPath; execPath = binPath + '/' + productName.toLowerCase(Locale.US) + ".sh"; if (new File(execPath).canExecute()) return execPath; String scriptName = ApplicationNamesInfo.getInstance().getScriptName(); execPath = binPath + '/' + scriptName + ".sh"; if (new File(execPath).canExecute()) return execPath; return null; } private static void install(File entryFile, boolean globalEntry) throws IOException, ExecutionException, InterruptedException { if (globalEntry) { File script = ExecUtil.createTempExecutableScript( "sudo", ".sh", "#!/bin/sh\n" + "xdg-desktop-menu install --mode system \"" + entryFile.getAbsolutePath() + "\"\n" + "RV=$?\n" + "xdg-desktop-menu forceupdate --mode system\n" + "exit $RV\n"); script.deleteOnExit(); String prompt = ApplicationBundle.message("desktop.entry.sudo.prompt"); int result = ExecUtil.sudoAndGetOutput(asList(script.getPath()), prompt, null).getExitCode(); if (result != 0) throw new RuntimeException("'" + script.getAbsolutePath() + "' : " + result); } else { int result = ExecUtil.execAndGetResult("xdg-desktop-menu", "install", "--mode", "user", entryFile.getAbsolutePath()); if (result != 0) throw new RuntimeException("'" + entryFile.getAbsolutePath() + "' : " + result); ExecUtil.execAndGetResult("xdg-desktop-menu", "forceupdate", "--mode", "user"); } } public static class CreateDesktopEntryDialog extends DialogWrapper { private JPanel myContentPane; private JLabel myLabel; private JCheckBox myGlobalEntryCheckBox; public CreateDesktopEntryDialog(final Project project) { super(project); init(); setTitle("Create Desktop Entry"); myLabel.setText(myLabel.getText().replace("$APP_NAME$", ApplicationNamesInfo.getInstance().getProductName())); } @Override protected JComponent createCenterPanel() { return myContentPane; } } }