blob: 136f4b166e1a0caf260c19535b79491ca2220f19 [file] [log] [blame]
/*
* 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;
}
}
}