2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.ide.browsers.actions;
18 import com.intellij.icons.AllIcons;
19 import com.intellij.ide.IdeBundle;
20 import com.intellij.ide.browsers.*;
21 import com.intellij.ide.browsers.impl.WebBrowserServiceImpl;
22 import com.intellij.internal.statistic.UsageTrigger;
23 import com.intellij.openapi.actionSystem.*;
24 import com.intellij.openapi.application.ApplicationManager;
25 import com.intellij.openapi.diagnostic.Logger;
26 import com.intellij.openapi.editor.Editor;
27 import com.intellij.openapi.keymap.KeymapManager;
28 import com.intellij.openapi.keymap.KeymapUtil;
29 import com.intellij.openapi.project.DumbAwareAction;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.ui.Messages;
32 import com.intellij.openapi.ui.popup.JBPopupFactory;
33 import com.intellij.openapi.vcs.vfs.ContentRevisionVirtualFile;
34 import com.intellij.openapi.vfs.VirtualFile;
35 import com.intellij.psi.PsiDocumentManager;
36 import com.intellij.psi.PsiElement;
37 import com.intellij.psi.PsiFile;
38 import com.intellij.psi.PsiManager;
39 import com.intellij.ui.ColoredListCellRenderer;
40 import com.intellij.ui.components.JBList;
41 import com.intellij.util.BitUtil;
42 import com.intellij.util.Consumer;
43 import com.intellij.util.ObjectUtils;
44 import com.intellij.util.Url;
45 import com.intellij.util.containers.ContainerUtil;
46 import com.intellij.xml.util.HtmlUtil;
47 import org.jetbrains.annotations.NotNull;
48 import org.jetbrains.annotations.Nullable;
49 import org.jetbrains.concurrency.AsyncPromise;
50 import org.jetbrains.concurrency.Promise;
53 import java.awt.event.InputEvent;
54 import java.util.Collection;
56 public abstract class BaseOpenInBrowserAction extends DumbAwareAction {
57 private static final Logger LOG = Logger.getInstance(BaseOpenInBrowserAction.class);
59 protected BaseOpenInBrowserAction(@NotNull WebBrowser browser) {
60 super(browser.getName(), null, browser.getIcon());
63 @SuppressWarnings("UnusedDeclaration")
64 protected BaseOpenInBrowserAction(@Nullable String text, @Nullable String description, @Nullable Icon icon) {
65 super(text, description, icon);
69 protected abstract WebBrowser getBrowser(@NotNull AnActionEvent event);
72 public final void update(AnActionEvent e) {
73 WebBrowser browser = getBrowser(e);
74 if (browser == null) {
75 e.getPresentation().setEnabledAndVisible(false);
79 OpenInBrowserRequest result = doUpdate(e);
84 String description = getTemplatePresentation().getText();
85 if (ActionPlaces.CONTEXT_TOOLBAR.equals(e.getPlace())) {
86 StringBuilder builder = new StringBuilder(description);
88 Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts("WebOpenInAction");
89 boolean exists = shortcuts.length > 0;
91 builder.append(KeymapUtil.getShortcutText(shortcuts[0]));
94 if (HtmlUtil.isHtmlFile(result.getFile())) {
95 builder.append(exists ? ", " : "").append("hold Shift to open URL of local file");
98 description = builder.toString();
100 e.getPresentation().setText(description);
104 public final void actionPerformed(AnActionEvent e) {
105 WebBrowser browser = getBrowser(e);
106 if (browser != null) {
107 UsageTrigger.trigger("OpenInBrowser." + browser.getName());
113 public static OpenInBrowserRequest createRequest(@NotNull DataContext context) {
114 final Editor editor = CommonDataKeys.EDITOR.getData(context);
115 if (editor != null) {
116 Project project = editor.getProject();
117 if (project != null && project.isInitialized()) {
118 PsiFile psiFile = CommonDataKeys.PSI_FILE.getData(context);
119 if (psiFile == null) {
120 psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
122 if (psiFile != null && !(psiFile.getVirtualFile() instanceof ContentRevisionVirtualFile)) {
123 return new OpenInBrowserRequest(psiFile) {
124 private PsiElement element;
128 public PsiElement getElement() {
129 if (element == null) {
130 element = getFile().findElementAt(editor.getCaretModel().getOffset());
132 return ObjectUtils.chooseNotNull(element, getFile());
139 PsiFile psiFile = CommonDataKeys.PSI_FILE.getData(context);
140 VirtualFile virtualFile = CommonDataKeys.VIRTUAL_FILE.getData(context);
141 Project project = CommonDataKeys.PROJECT.getData(context);
142 if (virtualFile != null && !virtualFile.isDirectory() && virtualFile.isValid() && project != null && project.isInitialized()) {
143 psiFile = PsiManager.getInstance(project).findFile(virtualFile);
146 if (psiFile != null && !(psiFile.getVirtualFile() instanceof ContentRevisionVirtualFile)) {
147 return OpenInBrowserRequest.create(psiFile);
154 public static OpenInBrowserRequest doUpdate(@NotNull AnActionEvent event) {
155 OpenInBrowserRequest request = createRequest(event.getDataContext());
156 boolean applicable = request != null && WebBrowserServiceImpl.getProvider(request) != null;
157 event.getPresentation().setEnabledAndVisible(applicable);
158 return applicable ? request : null;
161 public static void open(@NotNull AnActionEvent event, @Nullable WebBrowser browser) {
162 open(createRequest(event.getDataContext()), BitUtil.isSet(event.getModifiers(), InputEvent.SHIFT_MASK), browser);
165 public static void open(@Nullable final OpenInBrowserRequest request, boolean preferLocalUrl, @Nullable final WebBrowser browser) {
166 if (request == null) {
171 Collection<Url> urls = WebBrowserService.getInstance().getUrlsToOpen(request, preferLocalUrl);
172 if (!urls.isEmpty()) {
174 .done(new Consumer<Url>() {
176 public void consume(Url url) {
177 ApplicationManager.getApplication().saveAll();
178 BrowserLauncher.getInstance().browse(url.toExternalForm(), browser, request.getProject());
183 catch (WebBrowserUrlProvider.BrowserException e1) {
184 Messages.showErrorDialog(e1.getMessage(), IdeBundle.message("browser.error"));
186 catch (Exception e1) {
192 private static Promise<Url> chooseUrl(@NotNull Collection<Url> urls) {
193 if (urls.size() == 1) {
194 return Promise.resolve(ContainerUtil.getFirstItem(urls));
197 final JBList list = new JBList(urls);
198 list.setCellRenderer(new ColoredListCellRenderer() {
200 protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
201 // todo icons looks good, but is it really suitable for all URLs providers?
202 setIcon(AllIcons.Nodes.Servlet);
203 append(((Url)value).toDecodedForm());
207 final AsyncPromise<Url> result = new AsyncPromise<Url>();
208 JBPopupFactory.getInstance()
209 .createListPopupBuilder(list)
210 .setTitle("Choose Url")
211 .setItemChoosenCallback(new Runnable() {
214 Url value = (Url)list.getSelectedValue();
216 result.setError("selected value is null");
219 result.setResult(value);
224 .showInFocusCenter();