blob: 7ebf2a57a99818a08105e2fcc5703c0f4eaac88e [file] [log] [blame]
/*
* Copyright 2000-2013 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.util;
import com.intellij.CommonBundle;
import com.intellij.ide.BrowserUtil;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManagerCore;
import com.intellij.openapi.actionSystem.KeyboardShortcut;
import com.intellij.openapi.actionSystem.Shortcut;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.extensions.PluginDescriptor;
import com.intellij.openapi.keymap.Keymap;
import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.keymap.impl.DefaultKeymap;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.ResourceUtil;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.StringReader;
import java.net.URL;
/**
* @author dsl
* @author Konstantin Bulenkov
*/
public class TipUIUtil {
@NonNls private static final String SHORTCUT_ENTITY = "&shortcut:";
private TipUIUtil() {
}
@NotNull
public static String getPoweredByText(@NotNull TipAndTrickBean tip) {
PluginDescriptor descriptor = tip.getPluginDescriptor();
return descriptor instanceof IdeaPluginDescriptor &&
!PluginManagerCore.CORE_PLUGIN_ID.equals(descriptor.getPluginId().getIdString()) ?
((IdeaPluginDescriptor)descriptor).getName() : "";
}
public static void openTipInBrowser(String tipFileName, JEditorPane browser, Class providerClass) {
TipAndTrickBean tip = TipAndTrickBean.findByFileName(tipFileName);
if (tip == null && StringUtil.isNotEmpty(tipFileName)) {
tip = new TipAndTrickBean();
tip.fileName = tipFileName;
}
openTipInBrowser(tip, browser);
}
public static void openTipInBrowser(@Nullable TipAndTrickBean tip, JEditorPane browser) {
if (tip == null) return;
try {
PluginDescriptor pluginDescriptor = tip.getPluginDescriptor();
ClassLoader tipLoader = pluginDescriptor == null ? TipUIUtil.class.getClassLoader() :
ObjectUtils.notNull(pluginDescriptor.getPluginClassLoader(), TipUIUtil.class.getClassLoader());
URL url = ResourceUtil.getResource(tipLoader, "/tips/", tip.fileName);
if (url == null) {
setCantReadText(browser, tip);
return;
}
StringBuffer text = new StringBuffer(ResourceUtil.loadText(url));
updateShortcuts(text);
updateImages(text, tipLoader);
String replaced = text.toString().replace("&productName;", ApplicationNamesInfo.getInstance().getFullProductName());
String major = ApplicationInfo.getInstance().getMajorVersion();
replaced = replaced.replace("&majorVersion;", major);
String minor = ApplicationInfo.getInstance().getMinorVersion();
replaced = replaced.replace("&minorVersion;", minor);
replaced = replaced.replace("&majorMinorVersion;", major + ("0".equals(minor) ? "" : ("." + minor)));
replaced = replaced.replace("&settingsPath;", CommonBundle.settingsActionPath());
if (UIUtil.isUnderDarcula()) {
replaced = replaced.replace("css/tips.css", "css/tips_darcula.css");
}
browser.read(new StringReader(replaced), url);
}
catch (IOException e) {
setCantReadText(browser, tip);
}
}
private static void setCantReadText(JEditorPane browser, TipAndTrickBean bean) {
try {
String plugin = getPoweredByText(bean);
String product = ApplicationNamesInfo.getInstance().getFullProductName();
if (!plugin.isEmpty()) {
product += " and " + plugin + " plugin";
}
String message = IdeBundle.message("error.unable.to.read.tip.of.the.day", bean.fileName, product);
browser.read(new StringReader(message), null);
}
catch (IOException ignored) {
}
}
private static void updateImages(StringBuffer text, ClassLoader tipLoader) {
final boolean dark = UIUtil.isUnderDarcula();
final boolean retina = UIUtil.isRetina();
// if (!dark && !retina) {
// return;
// }
String suffix = "";
if (retina) suffix += "@2x";
if (dark) suffix += "_dark";
int index = text.indexOf("<img", 0);
while (index != -1) {
final int end = text.indexOf(">", index + 1);
if (end == -1) return;
final String img = text.substring(index, end + 1).replace('\r', ' ').replace('\n',' ');
final int srcIndex = img.indexOf("src=");
final int endIndex = img.indexOf(".png", srcIndex);
if (endIndex != -1) {
String path = img.substring(srcIndex + 5, endIndex);
if (!path.endsWith("_dark") && !path.endsWith("@2x")) {
path += suffix + ".png";
URL url = ResourceUtil.getResource(tipLoader, "/tips/", path);
if (url != null) {
String newImgTag = "<img src=\"" + path + "\" ";
if (retina) {
try {
final BufferedImage image = ImageIO.read(url.openStream());
final int w = image.getWidth() / 2;
final int h = image.getHeight() / 2;
newImgTag += "width=\"" + w + "\" height=\"" + h + "\"";
} catch (Exception ignore) {
newImgTag += "width=\"400\" height=\"200\"";
}
}
newImgTag += "/>";
text.replace(index, end + 1, newImgTag);
}
}
}
index = text.indexOf("<img", index + 1);
}
}
private static void updateShortcuts(StringBuffer text) {
int lastIndex = 0;
while(true) {
lastIndex = text.indexOf(SHORTCUT_ENTITY, lastIndex);
if (lastIndex < 0) return;
final int actionIdStart = lastIndex + SHORTCUT_ENTITY.length();
int actionIdEnd = text.indexOf(";", actionIdStart);
if (actionIdEnd < 0) {
return;
}
final String actionId = text.substring(actionIdStart, actionIdEnd);
String shortcutText = getShortcutText(actionId, KeymapManager.getInstance().getActiveKeymap());
if (shortcutText == null) {
Keymap defKeymap = KeymapManager.getInstance().getKeymap(DefaultKeymap.getInstance().getDefaultKeymapName());
if (defKeymap != null) {
shortcutText = getShortcutText(actionId, defKeymap);
if (shortcutText != null) {
shortcutText += " in default keymap";
}
}
}
if (shortcutText == null) {
shortcutText = "<no shortcut for action " + actionId + ">";
}
text.replace(lastIndex, actionIdEnd + 1, shortcutText);
lastIndex += shortcutText.length();
}
}
@Nullable
private static String getShortcutText(String actionId, Keymap keymap) {
for (final Shortcut shortcut : keymap.getShortcuts(actionId)) {
if (shortcut instanceof KeyboardShortcut) {
return KeymapUtil.getShortcutText(shortcut);
}
}
return null;
}
@NotNull
public static JEditorPane createTipBrowser() {
JEditorPane browser = new JEditorPane();
browser.setEditable(false);
browser.setBackground(UIUtil.getTextFieldBackground());
browser.addHyperlinkListener(
new HyperlinkListener() {
public void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
BrowserUtil.browse(e.getURL());
}
}
}
);
URL resource = ResourceUtil.getResource(TipUIUtil.class, "/tips/css/", UIUtil.isUnderDarcula() ? "tips_darcula.css" : "tips.css");
final StyleSheet styleSheet = UIUtil.loadStyleSheet(resource);
HTMLEditorKit kit = new HTMLEditorKit() {
@Override
public StyleSheet getStyleSheet() {
return styleSheet != null ? styleSheet : super.getStyleSheet();
}
};
browser.setEditorKit(kit);
return browser;
}
}