diff --git a/python/ipnb/ipnb.iml b/python/ipnb/ipnb.iml
index bd96a0aa04a7..4fd2c2691fe1 100644
--- a/python/ipnb/ipnb.iml
+++ b/python/ipnb/ipnb.iml
@@ -25,18 +25,5 @@
");
+ escaped = !escaped;
+ start = end + 3;
+ end = StringUtil.indexOf(source, "```", end + 1);
+ }
+ result.append(source.substring(start));
+
+ return result.toString();
+ }
+
+ @NotNull
+ private static String replaceLinks(@NotNull String source) {
+ final Pattern inlineLink = Pattern.compile("(\\[(.*?)\\]\\([ \\t]*(.*?)>?[ \\t]*(([\'\"])(.*?)\\5)?\\))", 32);
+ final Matcher matcher = inlineLink.matcher(source);
+ final StringBuffer sb = new StringBuffer();
+ while (matcher.find()) {
+ String linkText = matcher.group(2);
+ String url = matcher.group(3);
+ String title = matcher.group(6);
+ StringBuilder link = new StringBuilder();
+ link.append("").append(linkText);
+ link.append("");
+ matcher.appendReplacement(sb, link.toString());
+ }
+ matcher.appendTail(sb);
+
+ source = sb.toString();
+ return source;
+ }
+
+ private static void initHyperlinkListener(@NotNull final WebEngine engine) {
+ engine.getLoadWorker().stateProperty().addListener((ov, oldState, newState) -> {
+ if (newState == Worker.State.SUCCEEDED) {
+ final EventListener listener = new HyperlinkListener(engine);
+ addListenerToAllHyperlinkItems(engine, listener);
+ }
+ });
+ }
+
+ private static void addListenerToAllHyperlinkItems(WebEngine engine, EventListener listener) {
+ final Document doc = engine.getDocument();
+ if (doc != null) {
+ final NodeList nodeList = doc.getElementsByTagName("a");
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ ((EventTarget)nodeList.item(i)).addEventListener("click", listener, false);
+ }
+ }
+ }
+
+ private static class HyperlinkListener implements EventListener {
+ @NotNull private final WebEngine myEngine;
+
+ public HyperlinkListener(@NotNull final WebEngine engine) {
+ myEngine = engine;
+ }
+
+ @Override
+ public void handleEvent(Event ev) {
+ String domEventType = ev.getType();
+ if (domEventType.equals("click")) {
+ myEngine.setJavaScriptEnabled(true);
+ myEngine.getLoadWorker().cancel();
+ ev.preventDefault();
+
+ UIUtil.invokeLaterIfNeeded(() -> {
+
+ final String href = ((Element)ev.getTarget()).getAttribute("href");
+ if (href == null) return;
+ final URI address;
+ try {
+ address = new URI(href);
+ BrowserUtil.browse(address);
+ }
+ catch (URISyntaxException e) {
+ LOG.warn(e.getMessage());
+ }
+ });
+
+ }
+ }
+ }
+
+ private static void adjustHeight(final WebView webView, final JFXPanel javafxPanel, String source) {
+ try {
+ Object result = webView.getEngine().executeScript("document.getElementById(\"mydiv\").offsetHeight");
+ if (result instanceof Integer) {
+ final int fontSize = EditorColorsManager.getInstance().getGlobalScheme().getEditorFontSize();
+ double x = (double)source.length() * 8 / (int)result;
+ final double height = (source.length() * fontSize) / x + 20;
+ final int width = (int)(webView.getWidth() == 0 ? 1500 : webView.getWidth());
+ final Dimension size = new Dimension(width, (int)height);
+
+ UIUtil.invokeLaterIfNeeded(new Runnable() {
+ @Override
+ public void run() {
+ javafxPanel.setPreferredSize(size);
+ javafxPanel.revalidate();
+ javafxPanel.repaint();
+ }
+ });
+ }
+ }
+ catch (JSException ignore) {
+ }
+ }
+
+ private static void updateLaf(boolean isDarcula, BorderPane pane, WebEngine engine, JFXPanel jfxPanel) {
+ if (isDarcula) {
+ updateLafDarcula(pane, engine, jfxPanel);
+ }
+ }
+
+ private static void updateLafDarcula(BorderPane pane, WebEngine engine, JFXPanel jfxPanel) {
+ Platform.runLater(() -> {
+ ourStyleUrl = IpnbFileType.class.getResource("/style/javaFXBrowserDarcula.css");
+ engine.setUserStyleSheetLocation(ourStyleUrl.toExternalForm());
+ pane.setStyle("-fx-background-color: #313335");
+ jfxPanel.getScene().getStylesheets().add(ourStyleUrl.toExternalForm());
+ engine.reload();
+ });
+ }
+}
diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/IpnbUtils.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/IpnbUtils.java
index c21a79384f6a..c097a0845ee8 100644
--- a/python/ipnb/src/org/jetbrains/plugins/ipnb/IpnbUtils.java
+++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/IpnbUtils.java
@@ -1,68 +1,17 @@
package org.jetbrains.plugins.ipnb;
-import com.intellij.ide.BrowserUtil;
-import com.intellij.ide.ui.LafManager;
-import com.intellij.ide.ui.laf.darcula.DarculaLookAndFeelInfo;
-import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.editor.colors.EditorColorsManager;
-import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.util.containers.ContainerUtil;
-import com.intellij.util.text.MarkdownUtil;
-import com.intellij.util.ui.UIUtil;
+import com.intellij.ui.JBColor;
import javafx.application.Platform;
-import javafx.concurrent.Worker;
-import javafx.embed.swing.JFXPanel;
-import javafx.event.EventHandler;
-import javafx.scene.Scene;
-import javafx.scene.layout.BorderPane;
-import javafx.scene.web.WebEngine;
-import javafx.scene.web.WebEvent;
-import javafx.scene.web.WebView;
-import netscape.javascript.JSException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
-import org.w3c.dom.events.Event;
-import org.w3c.dom.events.EventListener;
-import org.w3c.dom.events.EventTarget;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
-import java.awt.event.MouseWheelEvent;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
public class IpnbUtils {
- private static final Logger LOG = Logger.getInstance(IpnbUtils.class);
- private static final String ourPrefix = "";
-
- private static final String ourPostfix = "";
- private static URL ourStyleUrl;
+ private static int hasFx = 0;
public static JComponent createLatexPane(@NotNull final String source, int width) {
final JComponent panel = createHtmlPanel(source, width);
@@ -80,189 +29,33 @@ public class IpnbUtils {
return panel;
}
+ public static boolean hasFx() {
+ if (hasFx == 0) {
+ try {
+ Platform.setImplicitExit(false);
+ hasFx = 1;
+ }
+ catch (NoClassDefFoundError e) {
+ hasFx = 2;
+ }
+ }
+ return hasFx == 1;
+ }
+
public static JComponent createHtmlPanel(@NotNull final String source, int width) {
- Platform.setImplicitExit(false);
-
- final JFXPanel javafxPanel = new JFXPanel(){
- @Override
- protected void processMouseWheelEvent(MouseWheelEvent e) {
- final Container parent = getParent();
- final MouseEvent parentEvent = SwingUtilities.convertMouseEvent(this, e, parent);
- parent.dispatchEvent(parentEvent);
- }
- };
- javafxPanel.setBackground(IpnbEditorUtil.getBackground());
-
- Platform.runLater(() -> {
- final WebView webView = new WebView();
- webView.setOnDragDetected(new EventHandler() {
- @Override
- public void handle(javafx.scene.input.MouseEvent event) {
- }
- });
- final WebEngine engine = webView.getEngine();
- initHyperlinkListener(engine);
- engine.setOnStatusChanged(new EventHandler>() {
- public void handle(WebEvent status) {
- adjustHeight(webView, javafxPanel, source);
- }
- });
-
- final String prefix = String.format(ourPrefix, EditorColorsManager.getInstance().getGlobalScheme().getEditorFontSize());
- engine.loadContent(prefix + convertToHtml(source) + ourPostfix);
- final BorderPane pane = new BorderPane(webView);
- final Scene scene = new Scene(pane, width != 0 ? width : 20, 20);
- javafxPanel.setScene(scene);
- Platform.runLater(() -> adjustHeight(webView, javafxPanel, source));
- updateLaf(LafManager.getInstance().getCurrentLookAndFeel() instanceof DarculaLookAndFeelInfo,
- pane, engine, javafxPanel);
- });
-
- return javafxPanel;
- }
-
- private static String convertToHtml(@NotNull String source) {
- source = StringUtil.replace(source, "class=\"alert alert-success\"", "class=\"alert-success\"");
- source = StringUtil.replace(source, "class=\"alert alert-error\"", "class=\"alert-error\"");
- ArrayList lines = ContainerUtil.newArrayList(source.split("\n|\r|\r\n"));
-
- MarkdownUtil.replaceHeaders(lines);
- source = StringUtil.join(lines, "\n");
- final StringBuilder result = new StringBuilder();
-
- source = replaceLinks(source);
-
- boolean escaped = false;
- int start = 0;
- int end = StringUtil.indexOf(source, "```");
- while (end > 0) {
- result.append(source.substring(start, end));
- result.append(escaped? " " : "");
- escaped = !escaped;
- start = end + 3;
- end = StringUtil.indexOf(source, "```", end + 1);
+ if (hasFx()) {
+ return IpnbJfxUtils.createHtmlPanel(source, width);
}
- result.append(source.substring(start));
-
- return result.toString();
+ return createNonJfxPanel(source);
}
- @NotNull
- private static String replaceLinks(@NotNull String source) {
- final Pattern inlineLink = Pattern.compile("(\\[(.*?)\\]\\([ \\t]*(.*?)>?[ \\t]*(([\'\"])(.*?)\\5)?\\))", 32);
- final Matcher matcher = inlineLink.matcher(source);
- final StringBuffer sb = new StringBuffer();
- while (matcher.find()) {
- String linkText = matcher.group(2);
- String url = matcher.group(3);
- String title = matcher.group(6);
- StringBuilder link = new StringBuilder();
- link.append("").append(linkText);
- link.append("");
- matcher.appendReplacement(sb, link.toString());
- }
- matcher.appendTail(sb);
-
- source = sb.toString();
- return source;
+ public static JComponent createNonJfxPanel(@NotNull final String source) {
+ final JTextArea textArea = new JTextArea(source);
+ textArea.setLineWrap(true);
+ textArea.setEditable(false);
+ textArea.setBorder(BorderFactory.createLineBorder(JBColor.lightGray));
+ textArea.setBackground(IpnbEditorUtil.getBackground());
+ return textArea;
}
- private static void initHyperlinkListener(@NotNull final WebEngine engine) {
- engine.getLoadWorker().stateProperty().addListener((ov, oldState, newState) -> {
- if (newState == Worker.State.SUCCEEDED) {
- final EventListener listener = new HyperlinkListener(engine);
- addListenerToAllHyperlinkItems(engine, listener);
- }
- });
- }
-
- private static void addListenerToAllHyperlinkItems(WebEngine engine, EventListener listener) {
- final Document doc = engine.getDocument();
- if (doc != null) {
- final NodeList nodeList = doc.getElementsByTagName("a");
- for (int i = 0; i < nodeList.getLength(); i++) {
- ((EventTarget)nodeList.item(i)).addEventListener("click", listener, false);
- }
- }
- }
-
- private static class HyperlinkListener implements EventListener {
- @NotNull private final WebEngine myEngine;
-
- public HyperlinkListener(@NotNull final WebEngine engine) {
- myEngine = engine;
- }
-
- @Override
- public void handleEvent(Event ev) {
- String domEventType = ev.getType();
- if (domEventType.equals("click")) {
- myEngine.setJavaScriptEnabled(true);
- myEngine.getLoadWorker().cancel();
- ev.preventDefault();
-
- UIUtil.invokeLaterIfNeeded(() -> {
-
- final String href = ((Element)ev.getTarget()).getAttribute("href");
- if (href == null) return;
- final URI address;
- try {
- address = new URI(href);
- BrowserUtil.browse(address);
- }
- catch (URISyntaxException e) {
- LOG.warn(e.getMessage());
- }
- });
-
- }
- }
- }
-
- private static void adjustHeight(final WebView webView, final JFXPanel javafxPanel, String source) {
- try {
- Object result = webView.getEngine().executeScript("document.getElementById(\"mydiv\").offsetHeight");
- if (result instanceof Integer) {
- final int fontSize = EditorColorsManager.getInstance().getGlobalScheme().getEditorFontSize();
- double x = (double)source.length() * 8 / (int)result;
- final double height = (source.length() * fontSize) / x + 20;
- final int width = (int)(webView.getWidth() == 0 ? 1500 : webView.getWidth());
- final Dimension size = new Dimension(width, (int)height);
-
- UIUtil.invokeLaterIfNeeded(new Runnable() {
- @Override
- public void run() {
- javafxPanel.setPreferredSize(size);
- javafxPanel.revalidate();
- javafxPanel.repaint();
- }
- });
- }
- }
- catch (JSException ignore) {
- }
- }
-
- private static void updateLaf(boolean isDarcula, BorderPane pane, WebEngine engine, JFXPanel jfxPanel) {
- if (isDarcula) {
- updateLafDarcula(pane, engine, jfxPanel);
- }
- }
-
- private static void updateLafDarcula(BorderPane pane, WebEngine engine, JFXPanel jfxPanel) {
- Platform.runLater(() -> {
- ourStyleUrl = IpnbFileType.class.getResource("/style/javaFXBrowserDarcula.css");
- engine.setUserStyleSheetLocation(ourStyleUrl.toExternalForm());
- pane.setStyle("-fx-background-color: #313335");
- jfxPanel.getScene().getStylesheets().add(ourStyleUrl.toExternalForm());
- engine.reload();
- });
- }
}
diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbFilePanel.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbFilePanel.java
index 61a98e370309..8351146618bb 100644
--- a/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbFilePanel.java
+++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/editor/panels/IpnbFilePanel.java
@@ -22,6 +22,7 @@ import com.intellij.util.Alarm;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.jetbrains.plugins.ipnb.IpnbUtils;
import org.jetbrains.plugins.ipnb.editor.IpnbEditorUtil;
import org.jetbrains.plugins.ipnb.editor.IpnbFileEditor;
import org.jetbrains.plugins.ipnb.editor.actions.IpnbCutCellAction;
@@ -134,6 +135,7 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, D
}
private void layoutFile() {
+ addWarningIfNeeded();
final List cells = myIpnbFile.getCells();
for (IpnbCell cell : cells) {
addCellToPanel(cell);
@@ -157,6 +159,13 @@ public class IpnbFilePanel extends JPanel implements Scrollable, DataProvider, D
});
}
+ private void addWarningIfNeeded() {
+ if (IpnbUtils.hasFx()) return;
+ final JLabel warning = new JLabel("Switch to the bundled JDK for proper markdown cell rendering", SwingConstants.CENTER);
+ warning.setForeground(JBColor.RED);
+ add(warning);
+ }
+
private void addCellToPanel(IpnbCell cell) {
IpnbEditablePanel panel;
if (cell instanceof IpnbCodeCell) {