Use javafx panel in study tool window

This commit is contained in:
Valentina Kiryushkina
2016-02-03 21:39:29 +03:00
parent facd2674f7
commit c28d98c0e3
14 changed files with 957 additions and 135 deletions
@@ -32,7 +32,7 @@
<extensionPoints>
<extensionPoint qualifiedName="Edu.StudyExecutor" beanClass="com.intellij.lang.LanguageExtensionPoint"/>
<extensionPoint qualifiedName="Edu.StudyLanguageManager" beanClass="com.intellij.lang.LanguageExtensionPoint"/>
<extensionPoint qualifiedName="Edu.studyActionProvider" interface="com.jetbrains.edu.learning.StudyActionProvider"/>
<extensionPoint qualifiedName="Edu.studyToolWindowConfigurator" interface="com.jetbrains.edu.learning.StudyToolWindowConfigurator"/>
</extensionPoints>
<actions>
@@ -0,0 +1,3 @@
browser.action.back=Click to go back
browser.action.forward=Click to go forward
browser.action.open.link=Click to open link in browser
@@ -0,0 +1,374 @@
::-webkit-scrollbar-corner {
background-color: #3c3f41;
}
body {
background-color: #3c3f41;
font-size: 12pt;
font-family: Serif;
font-weight: normal;
margin-left: 0;
margin-right: 0;
color: #dddddd
}
.progress-bar > .track {
-fx-text-box-border: #bbbbbb;
-fx-control-inner-background: #bbbbbb;
}
p img {
background-color: #bbbbbb;
}
textarea {
background-color: #3c3f41;
color: #dddddd
}
p {
margin-top: 15
}
h1 {
font-size: x-large;
font-weight: bold;
margin-top: 10;
margin-bottom: 10
}
h2 {
font-size: large;
font-weight: bold;
margin-top: 10;
margin-bottom: 10
}
h3 {
font-size: medium;
font-weight: bold;
margin-top: 10;
margin-bottom: 10
}
h4 {
font-size: small;
font-weight: bold;
margin-top: 10;
margin-bottom: 10
}
h5 {
font-size: x-small;
font-weight: bold;
margin-top: 10;
margin-bottom: 10
}
h6 {
font-size: xx-small;
font-weight: bold;
margin-top: 10;
margin-bottom: 10
}
li p {
margin-top: 0;
margin-bottom: 0
}
td p {
margin-top: 0
}
menu li p {
margin-top: 0;
margin-bottom: 0
}
menu li {
margin-left: 0;
margin-right: 0;
margin-top: 0;
margin-bottom: 0
}
menu {
margin-left-ltr: 40;
margin-right-rtl: 40;
margin-top: 10;
margin-bottom: 10
}
dir li p {
margin-top: 0;
margin-bottom: 0
}
dir li {
margin-left: 0;
margin-right: 0;
margin-top: 0;
margin-bottom: 0
}
dir {
margin-left-ltr: 40;
margin-right-rtl: 40;
margin-top: 10;
margin-bottom: 10
}
dd {
margin-left-ltr: 40;
margin-right-rtl: 40;
margin-top: 0;
margin-bottom: 0
}
dd p {
margin-left: 0;
margin-rigth: 0;
margin-top: 0;
margin-bottom: 0
}
dt {
margin-top: 0;
margin-bottom: 0
}
dl {
margin-left: 0;
margin-top: 10;
margin-bottom: 10
}
ol li {
margin-left: 0;
margin-right: 0;
margin-top: 0;
margin-bottom: 0
}
ol {
margin-top: 10;
margin-bottom: 10;
margin-left-ltr: 50;
margin-right-rtl: 50;
list-style-type: decimal
}
ol li p {
margin-top: 0;
margin-bottom: 0
}
ul li {
margin-left: 0;
margin-right: 0;
margin-top: 0;
margin-bottom: 0
}
ul {
margin-top: 10;
margin-bottom: 10;
margin-left-ltr: 50;
margin-right-rtl: 50;
list-style-type: disc;
-bullet-gap: 10
}
ul li ul li {
margin-left: 0;
margin-right: 0;
margin-top: 0;
margin-bottom: 0
}
ul li ul {
list-style-type: circle;
margin-left-ltr: 25;
margin-right-rtl: 25;
}
ul li ul li ul li {
margin-left: 0;
margin-right: 0;
margin-top: 0;
margin-bottom: 0
}
ul li ul li ul {
list-style-type: square;
margin-left-ltr: 25;
margin-right-rtl: 25
}
ul li menu {
list-style-type: circle;
margin-left-ltr: 25;
margin-right-rtl: 25;
}
ul li p {
margin-top: 0;
margin-bottom: 0
}
a {
color: #589df6;
text-decoration: underline
}
address {
color: #589df6;
font-style: italic
}
big {
font-size: x-large
}
small {
font-size: x-small
}
samp {
font-size: small;
font-family: Monospaced
}
cite {
font-style: italic
}
code {
font-size: small;
font-family: Monospaced
}
dfn {
font-style: italic
}
em {
font-style: italic
}
i {
font-style: italic
}
b {
font-weight: bold
}
kbd {
font-size: small;
font-family: Monospaced
}
s {
text-decoration: line-through
}
strike {
text-decoration: line-through
}
strong {
font-weight: bold
}
sub {
vertical-align: sub
}
sup {
vertical-align: sup
}
tt {
font-family: Monospaced
}
u {
text-decoration: underline
}
var {
font-weight: bold;
font-style: italic
}
table {
border-color: Gray;
border-style: outset
}
tr {
text-align: left
}
td {
border-color: Gray;
border-style: inset;
padding-left: 3;
padding-right: 3;
padding-top: 3;
padding-bottom: 3
}
th {
text-align: center;
font-weight: bold;
border-color: Gray;
border-style: inset;
padding-left: 3;
padding-right: 3;
padding-top: 3;
padding-bottom: 3
}
blockquote {
margin-top: 5;
margin-bottom: 5;
margin-left: 35;
margin-right: 35
}
center {
text-align: center
}
pre {
margin-top: 5;
margin-bottom: 5;
font-family: Monospaced
}
pre p {
margin-top: 0
}
caption {
caption-side: top;
text-align: center
}
table {
border: none;
}
td {
border: none;
}
nobr {
white-space: nowrap
}
@@ -0,0 +1,56 @@
.scroll-bar{
-fx-background-color: #3c3f41;
-fx-background-radius: 2em;
}
.scroll-bar:horizontal .track,
.scroll-bar:vertical .track {
-fx-background-color: #3c3f41;
-fx-border-color:transparent;
-fx-background-radius: 2em;
}
.scroll-bar:vertical .track-background,
.scroll-bar:horizontal .track-background {
-fx-background-color: #3c3f41;
}
.scroll-bar:horizontal .thumb {
-fx-background-color: #bbbbbb;
-fx-background-insets: 4 0 4 0;
-fx-background-radius: 2em;
-fx-opacity: 0.2;
}
.scroll-bar:vertical .thumb {
-fx-background-color: #bbbbbb;
-fx-background-insets: 0 4 0 4;
-fx-background-radius: 2em;
-fx-opacity: 0.2;
}
.scroll-bar:vertical:corner-present {
-fx-shape: " ";
-fx-width: 0;
}
.scroll-bar:horizontal:corner-present {
-fx-shape: " ";
-fx-width: 0;
}
.scroll-bar-corner {
-fx-background-color: #3c3f41;
}
.scroll-bar:horizontal .increment-arrow {
-fx-shape: " "
}
.scroll-bar:vertical .increment-arrow {
-fx-shape: " "
}
.scroll-bar:horizontal .decrement-arrow {
-fx-shape: " "
}
.scroll-bar:vertical .decrement-arrow {
-fx-shape: " "
}
@@ -0,0 +1,51 @@
.scroll-bar{
-fx-background-color: #ffffff;
-fx-background-radius: 2em;
}
.scroll-bar:horizontal .track,
.scroll-bar:vertical .track {
-fx-background-color: #ffffff;
-fx-border-color:transparent;
-fx-background-radius: 2em;
}
.scroll-bar:vertical .track-background,
.scroll-bar:horizontal .track-background {
-fx-background-color: #ffffff;
}
.scroll-bar:horizontal .thumb {
-fx-background-color: #dddddd;
-fx-background-insets: 4 0 4 0;
-fx-background-radius: 2em;
}
.scroll-bar:vertical .thumb {
-fx-background-color: #dddddd;
-fx-background-insets: 0 4 0 4;
-fx-background-radius: 2em;
}
.scroll-bar:vertical:corner-present {
-fx-shape: " ";
-fx-width: 0;
}
.scroll-bar:horizontal:corner-present {
-fx-shape: " ";
-fx-width: 0;
}
.scroll-bar-corner {
-fx-background-color: #ffffff;
}
.scroll-bar:horizontal .increment-arrow {
-fx-shape: " "
}
.scroll-bar:vertical .increment-arrow {
-fx-shape: " "
}
.scroll-bar:horizontal .decrement-arrow {
-fx-shape: " "
}
.scroll-bar:vertical .decrement-arrow {
-fx-shape: " "
}
@@ -1,15 +0,0 @@
package com.jetbrains.edu.learning;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface StudyActionProvider {
ExtensionPointName<StudyActionProvider> EP_NAME = ExtensionPointName.create("Edu.studyActionProvider");
@Nullable
DefaultActionGroup getActionGroup(@NotNull Project project);
}
@@ -0,0 +1,34 @@
package com.jetbrains.edu.learning;
import com.intellij.CommonBundle;
import com.intellij.reference.SoftReference;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.PropertyKey;
import java.lang.ref.Reference;
import java.util.ResourceBundle;
public class StudyBundle {
public static String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, @NotNull Object... params) {
return CommonBundle.message(getBundle(), key, params);
}
private static Reference<ResourceBundle> ourBundle;
@NonNls
private static final String BUNDLE = "StudyBundle";
private StudyBundle() {
}
// Cached loading
private static ResourceBundle getBundle() {
ResourceBundle bundle = SoftReference.dereference(ourBundle);
if (bundle == null) {
bundle = ResourceBundle.getBundle(BUNDLE);
ourBundle = new SoftReference<>(bundle);
}
return bundle;
}
}
@@ -35,6 +35,7 @@ import com.jetbrains.edu.learning.actions.*;
import com.jetbrains.edu.learning.editor.StudyEditorFactoryListener;
import com.jetbrains.edu.learning.ui.StudyProgressToolWindowFactory;
import com.jetbrains.edu.learning.ui.StudyToolWindowFactory;
import javafx.application.Platform;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -60,6 +61,7 @@ public class StudyProjectComponent implements ProjectComponent {
@Override
public void projectOpened() {
final Course course = StudyTaskManager.getInstance(myProject).getCourse();
Platform.setImplicitExit(false);
if (course != null && !course.isUpToDate()) {
course.setUpToDate(true);
updateCourse();
@@ -0,0 +1,21 @@
package com.jetbrains.edu.learning;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
public interface StudyToolWindowConfigurator {
ExtensionPointName<StudyToolWindowConfigurator> EP_NAME = ExtensionPointName.create("Edu.studyToolWindowConfigurator");
@NotNull
DefaultActionGroup getActionGroup(Project project);
HashMap<String, JPanel> getAdditionalPanels(Project project);
boolean accept(@NotNull final Project project);
}
@@ -0,0 +1,260 @@
package com.jetbrains.edu.learning.ui;
import com.intellij.icons.AllIcons;
import com.intellij.ide.BrowserUtil;
import com.intellij.ide.ui.LafManager;
import com.intellij.ide.ui.LafManagerListener;
import com.intellij.ide.ui.laf.darcula.DarculaLookAndFeelInfo;
import com.intellij.openapi.application.ApplicationManager;
import com.jetbrains.edu.learning.StudyBundle;
import javafx.application.Platform;
import javafx.concurrent.Worker;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebHistory;
import javafx.scene.web.WebView;
import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
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.net.URL;
class StudyBrowserWindow extends JFrame {
private static final String EVENT_TYPE_CLICK = "click";
private JFXPanel myPanel;
private WebView myWebComponent;
private StackPane myPane;
private WebEngine myEngine;
private ProgressBar myProgressBar;
private boolean refInNewBrowser;
private boolean showProgress = true;
public StudyBrowserWindow() {
setSize(new Dimension(900, 800));
setLayout(new BorderLayout());
setPanel(new JFXPanel());
setTitle("Study Browser");
LafManager.getInstance().addLafManagerListener(new CheckIOLafManagerListener());
initComponents();
}
public void setShowProgress(boolean showProgress) {
this.showProgress = showProgress;
}
public void openLinkInNewWindow(boolean refInNewBrowser) {
this.refInNewBrowser = refInNewBrowser;
}
private void updateLaf(boolean isDarcula) {
if (isDarcula) {
updateLafDarcula();
}
else {
updateIntellijAndGTKLaf();
}
}
private void updateIntellijAndGTKLaf() {
Platform.runLater(() -> {
final URL scrollBarStyleUrl = getClass().getResource("/style/javaFXBrowserScrollBar.css");
myPane.getStylesheets().add(scrollBarStyleUrl.toExternalForm());
myEngine.setUserStyleSheetLocation(null);
myEngine.reload();
});
}
private void updateLafDarcula() {
Platform.runLater(() -> {
final URL engineStyleUrl = getClass().getResource("/style/javaFXBrowserDarcula.css");
final URL scrollBarStyleUrl = getClass().getResource("/style/javaFXBrowserDarculaScrollBar.css");
myEngine.setUserStyleSheetLocation(engineStyleUrl.toExternalForm());
myPane.getStylesheets().add(scrollBarStyleUrl.toExternalForm());
myPane.setStyle("-fx-background-color: #3c3f41");
myPanel.getScene().getStylesheets().add(engineStyleUrl.toExternalForm());
});
}
private void initComponents() {
Platform.runLater(() -> {
myPane = new StackPane();
myWebComponent = new WebView();
myEngine = myWebComponent.getEngine();
if (showProgress) {
myProgressBar = makeProgressBarWithListener();
myWebComponent.setVisible(false);
myPane.getChildren().addAll(myWebComponent, myProgressBar);
}
else {
myPane.getChildren().add(myWebComponent);
}
if (refInNewBrowser) {
initHyperlinkListener();
}
Scene scene = new Scene(myPane);
myPanel.setScene(scene);
myPanel.setVisible(true);
updateLaf(LafManager.getInstance().getCurrentLookAndFeel() instanceof DarculaLookAndFeelInfo);
});
add(myPanel, BorderLayout.CENTER);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
}
public void load(@NotNull final String url) {
Platform.runLater(() -> {
updateLookWithProgressBarIfNeeded();
myEngine.load(url);
});
}
public void loadContent(@NotNull final String content) {
Platform.runLater(() -> {
updateLookWithProgressBarIfNeeded();
myEngine.loadContent(content);
});
}
private void updateLookWithProgressBarIfNeeded() {
if (showProgress) {
myProgressBar.setVisible(true);
myWebComponent.setVisible(false);
}
}
private void initHyperlinkListener() {
myEngine.getLoadWorker().stateProperty().addListener((ov, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
final EventListener listener = makeHyperLinkListener();
addListenerToAllHyperlinkItems(listener);
}
});
}
private void addListenerToAllHyperlinkItems(EventListener listener) {
final Document doc = myEngine.getDocument();
final NodeList nodeList = doc.getElementsByTagName("a");
for (int i = 0; i < nodeList.getLength(); i++) {
((EventTarget)nodeList.item(i)).addEventListener(EVENT_TYPE_CLICK, listener, false);
}
}
@NotNull
private EventListener makeHyperLinkListener() {
return ev -> {
String domEventType = ev.getType();
if (domEventType.equals(EVENT_TYPE_CLICK)) {
myEngine.setJavaScriptEnabled(true);
myEngine.getLoadWorker().cancel();
ev.preventDefault();
ApplicationManager.getApplication().invokeLater(() -> {
final String href = ((Element)ev.getTarget()).getAttribute("href");
final StudyBrowserWindow checkIOBrowserWindow = new StudyBrowserWindow();
checkIOBrowserWindow.addBackAndOpenButtons();
checkIOBrowserWindow.openLinkInNewWindow(false);
checkIOBrowserWindow.setShowProgress(true);
checkIOBrowserWindow.load(href);
checkIOBrowserWindow.setVisible(true);
});
}
};
}
public void addBackAndOpenButtons() {
ApplicationManager.getApplication().invokeLater(() -> {
final JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
final JButton backButton = makeGoButton(StudyBundle.message("browser.action.back"), AllIcons.Actions.Back, -1);
final JButton forwardButton = makeGoButton(StudyBundle.message("browser.action.forward"), AllIcons.Actions.Forward, 1);
final JButton openInBrowser = new JButton(AllIcons.Actions.Browser_externalJavaDoc);
openInBrowser.addActionListener(e -> BrowserUtil.browse(myEngine.getLocation()));
openInBrowser.setToolTipText(StudyBundle.message("browser.action.open.link"));
addButtonsAvailabilityListeners(backButton, forwardButton);
panel.setMaximumSize(new Dimension(40, getPanel().getHeight()));
panel.add(backButton);
panel.add(forwardButton);
panel.add(openInBrowser);
add(panel, BorderLayout.PAGE_START);
});
}
private void addButtonsAvailabilityListeners(JButton backButton, JButton forwardButton) {
Platform.runLater(() -> myEngine.getLoadWorker().stateProperty().addListener((ov, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
final WebHistory history = myEngine.getHistory();
boolean isGoBackAvailable = history.getCurrentIndex() > 0;
boolean isGoForwardAvailable = history.getCurrentIndex() < history.getEntries().size() - 1;
ApplicationManager.getApplication().invokeLater(() -> {
backButton.setEnabled(isGoBackAvailable);
forwardButton.setEnabled(isGoForwardAvailable);
});
}
}));
}
private JButton makeGoButton(@NotNull final String toolTipText, @NotNull final Icon icon, final int direction) {
final JButton button = new JButton(icon);
button.setEnabled(false);
button.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 1) {
Platform.runLater(() -> myEngine.getHistory().go(direction));
}
}
});
button.setToolTipText(toolTipText);
return button;
}
private ProgressBar makeProgressBarWithListener() {
final ProgressBar progress = new ProgressBar();
progress.progressProperty().bind(myWebComponent.getEngine().getLoadWorker().progressProperty());
myWebComponent.getEngine().getLoadWorker().stateProperty().addListener(
(ov, oldState, newState) -> {
if (myWebComponent.getEngine().getLocation().contains("http") && newState == Worker.State.SUCCEEDED) {
myProgressBar.setVisible(false);
myWebComponent.setVisible(true);
}
});
return progress;
}
public JFXPanel getPanel() {
return myPanel;
}
private void setPanel(JFXPanel panel) {
myPanel = panel;
}
private class CheckIOLafManagerListener implements LafManagerListener {
@Override
public void lookAndFeelChanged(LafManager manager) {
updateLaf(manager.getCurrentLookAndFeel() instanceof DarculaLookAndFeelInfo);
}
}
}
@@ -21,8 +21,6 @@ import com.intellij.openapi.actionSystem.ActionToolbar;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
@@ -30,40 +28,78 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.SimpleToolWindowPanel;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.BrowserHyperlinkListener;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.JBCardLayout;
import com.intellij.ui.OnePixelSplitter;
import com.intellij.util.containers.HashMap;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import com.jetbrains.edu.EduNames;
import com.jetbrains.edu.courseFormat.Course;
import com.jetbrains.edu.courseFormat.Task;
import com.jetbrains.edu.courseFormat.TaskFile;
import com.jetbrains.edu.learning.StudyActionProvider;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.StudyToolWindowConfigurator;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.actions.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import java.awt.*;
import java.io.IOException;
import java.util.Map;
public class StudyToolWindow extends SimpleToolWindowPanel implements DataProvider, Disposable {
private static final String EMPTY_TASK_TEXT = "Please, open any task to see task description";
private static final Logger LOG = Logger.getInstance(StudyToolWindow.class);
private static final String EMPTY_TASK_TEXT = "Please, open any task to see task description";
private static final String TASK_INFO_ID = "taskInfo";
private final JBCardLayout myCardLayout;
private final JPanel myContentPanel;
private final OnePixelSplitter mySplitPane;
private StudyBrowserWindow myBrowserWindow;
public StudyToolWindow(final Project project) {
super(true, true);
myCardLayout = new JBCardLayout();
myContentPanel = new JPanel(myCardLayout);
mySplitPane = new OnePixelSplitter(myVertical=true);
String taskText = getTaskText(project);
if (taskText == null) return;
JPanel toolbarPanel = createToolbarPanel(project);
setToolbar(toolbarPanel);
final JTextPane taskTextPane = createTaskTextPane();
myContentPanel.add(TASK_INFO_ID, createTaskInfoPanel(taskText));
mySplitPane.setFirstComponent(myContentPanel);
addAdditionalPanels(project);
myCardLayout.show(myContentPanel, TASK_INFO_ID);
setContent(mySplitPane);
final FileEditorManagerListener listener = new StudyFileEditorManagerListener(project, this);
project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, listener);
}
private void addAdditionalPanels(Project project) {
StudyToolWindowConfigurator configurator = getStudyToolWindowConfigurator(project);
assert configurator != null;
HashMap<String, JPanel> panels = configurator.getAdditionalPanels(project);
for (Map.Entry<String, JPanel> entry: panels.entrySet()) {
myContentPanel.add(entry.getKey(), entry.getValue());
}
}
public void dispose() {
}
public void showPanelById(@NotNull final String panelId) {
myCardLayout.swipe(myContentPanel, panelId, JBCardLayout.SwipeDirection.AUTO);
}
public void setButtomComponent(JComponent component) {
mySplitPane.setSecondComponent(component);
}
private static String getTaskText(@NotNull final Project project) {
VirtualFile[] files = FileEditorManager.getInstance(project).getSelectedFiles();
TaskFile taskFile = null;
for (VirtualFile file : files) {
@@ -73,48 +109,25 @@ public class StudyToolWindow extends SimpleToolWindowPanel implements DataProvid
}
}
if (taskFile == null) {
taskTextPane.setText(EMPTY_TASK_TEXT);
setContent(taskTextPane);
return;
return EMPTY_TASK_TEXT;
}
final Task task = taskFile.getTask();
if (task != null) {
final String taskText = getTaskText(task, task.getTaskDir(project));
if (taskText == null) {
return;
}
JBScrollPane scrollPane = new JBScrollPane(taskTextPane);
taskTextPane.setText(taskText);
taskTextPane.addHyperlinkListener(BrowserHyperlinkListener.INSTANCE);
setContent(scrollPane);
final FileEditorManagerListener listener = new StudyFileEditorManagerListener(project, taskTextPane);
project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, listener);
return getTaskTextFromTask(task, task.getTaskDir(project));
}
return null;
}
@NotNull
private static JTextPane createTaskTextPane() {
final JTextPane taskTextPane = new JTextPane();
taskTextPane.setContentType(new HTMLEditorKit().getContentType());
final EditorColorsScheme editorColorsScheme = EditorColorsManager.getInstance().getGlobalScheme();
int fontSize = editorColorsScheme.getEditorFontSize();
final String fontName = editorColorsScheme.getEditorFontName();
final Font font = new Font(fontName, Font.PLAIN, fontSize);
String bodyRule = "body { font-family: " + font.getFamily() + "; " +
"font-size: " + font.getSize() + "pt; }";
((HTMLDocument)taskTextPane.getDocument()).getStyleSheet().addRule(bodyRule);
taskTextPane.setEditable(false);
if (!UIUtil.isUnderDarcula()) {
taskTextPane.setBackground(EditorColorsManager.getInstance().getGlobalScheme().getDefaultBackground());
}
taskTextPane.setBorder(new EmptyBorder(15, 20, 0, 100));
return taskTextPane;
}
public void dispose() {
private JPanel createTaskInfoPanel(String taskText) {
myBrowserWindow = new StudyBrowserWindow();
myBrowserWindow.setShowProgress(false);
myBrowserWindow.openLinkInNewWindow(true);
myBrowserWindow.addBackAndOpenButtons();
myBrowserWindow.loadContent(taskText);
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
panel.add(myBrowserWindow.getPanel());
return panel;
}
private static JPanel createToolbarPanel(@NotNull final Project project) {
@@ -125,18 +138,14 @@ public class StudyToolWindow extends SimpleToolWindowPanel implements DataProvid
}
private static DefaultActionGroup getActionGroup(@NotNull final Project project) {
StudyActionProvider[] extensions = StudyActionProvider.EP_NAME.getExtensions();
DefaultActionGroup actionGroup = null;
Course course = StudyTaskManager.getInstance(project).getCourse();
if (course == null) {
return createDefaultActionGroup(project);
}
for (StudyActionProvider actionProvider: extensions) {
actionGroup = actionProvider.getActionGroup(project);
if (actionGroup != null) break;
}
StudyToolWindowConfigurator configurator = getStudyToolWindowConfigurator(project);
assert configurator != null;
return actionGroup == null ? createDefaultActionGroup(project) : actionGroup;
return configurator.getActionGroup(project);
}
@NotNull
@@ -152,14 +161,51 @@ public class StudyToolWindow extends SimpleToolWindowPanel implements DataProvid
return group;
}
static class StudyFileEditorManagerListener implements FileEditorManagerListener {
private static final Logger LOG = Logger.getInstance(StudyFileEditorManagerListener.class);
private Project myProject;
private JTextPane myTaskTextPane;
private void setTaskText(String text) {
myBrowserWindow.loadContent(text);
}
StudyFileEditorManagerListener(@NotNull final Project project, JTextPane taskTextPane) {
@Nullable
private static String getTaskTextFromTask(@Nullable final Task task, @Nullable final VirtualFile taskDirectory) {
if (task == null) {
return null;
}
String text = task.getText();
if (text != null) {
return text;
}
if (taskDirectory != null) {
VirtualFile taskTextFile = taskDirectory.findChild(EduNames.TASK_HTML);
if (taskTextFile != null) {
try {
return FileUtil.loadTextAndClose(taskTextFile.getInputStream());
}
catch (IOException e) {
LOG.info(e);
}
}
}
return null;
}
@Nullable
private static StudyToolWindowConfigurator getStudyToolWindowConfigurator(@NotNull Project project) {
StudyToolWindowConfigurator[] extensions = StudyToolWindowConfigurator.EP_NAME.getExtensions();
for (StudyToolWindowConfigurator extension: extensions) {
if (extension.accept(project)) {
return extension;
}
}
return null;
}
static class StudyFileEditorManagerListener implements FileEditorManagerListener {
private Project myProject;
private StudyToolWindow myStudyToolWindow;
StudyFileEditorManagerListener(@NotNull final Project project, StudyToolWindow studyToolWindow) {
myProject = project;
myTaskTextPane = taskTextPane;
myStudyToolWindow = studyToolWindow;
}
@Override
@@ -170,7 +216,7 @@ public class StudyToolWindow extends SimpleToolWindowPanel implements DataProvid
@Override
public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
myTaskTextPane.setText(EMPTY_TASK_TEXT);
myStudyToolWindow.setTaskText(EMPTY_TASK_TEXT);
}
@Override
@@ -194,35 +240,14 @@ public class StudyToolWindow extends SimpleToolWindowPanel implements DataProvid
}
private void setTaskText(@Nullable final Task task, @Nullable final VirtualFile taskDirectory) {
String text = getTaskText(task, taskDirectory);
String text = getTaskTextFromTask(task, taskDirectory);
if (text == null) {
myTaskTextPane.setText(EMPTY_TASK_TEXT);
myStudyToolWindow.setTaskText(EMPTY_TASK_TEXT);
return;
}
myTaskTextPane.setText(text);
myStudyToolWindow.setTaskText(text);
}
}
@Nullable
private static String getTaskText(@Nullable final Task task, @Nullable final VirtualFile taskDirectory) {
if (task == null) {
return null;
}
String text = task.getText();
if (text != null) {
return text;
}
if (taskDirectory != null) {
VirtualFile taskTextFile = taskDirectory.findChild(EduNames.TASK_HTML);
if (taskTextFile != null) {
try {
return FileUtil.loadTextAndClose(taskTextFile.getInputStream());
}
catch (IOException e) {
LOG.info(e);
}
}
}
return null;
}
}
@@ -32,7 +32,7 @@
<extensions defaultExtensionNs="Edu">
<StudyExecutor implementationClass="com.jetbrains.edu.learning.PyStudyExecutor" language="Python"/>
<StudyLanguageManager implementationClass="com.jetbrains.edu.learning.PyStudyLanguageManager" language="Python"/>
<studyActionProvider implementation="com.jetbrains.edu.learning.PyStudyActionProvider" />
<studyToolWindowConfigurator implementation="com.jetbrains.edu.learning.PyStudyToolWindowConfigurator" />
</extensions>
<actions>
<action id="WelcomeScreen.PythonIntro" class="com.jetbrains.edu.learning.actions.PyStudyIntroductionCourseAction"
@@ -1,29 +0,0 @@
package com.jetbrains.edu.learning;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.project.Project;
import com.jetbrains.edu.courseFormat.Course;
import com.jetbrains.edu.learning.actions.*;
import org.jetbrains.annotations.NotNull;
public class PyStudyActionProvider implements StudyActionProvider {
@Override
public DefaultActionGroup getActionGroup(@NotNull Project project) {
Course course = StudyTaskManager.getInstance(project).getCourse();
if (course != null && course.getLanguage().equals("Python") && course.getCourseType().equals("PyCharm")) {
final DefaultActionGroup group = new DefaultActionGroup();
group.add(StudyCheckAction.createCheckAction(course));
group.add(new StudyPreviousStudyTaskAction());
group.add(new StudyNextStudyTaskAction());
group.add(new StudyRefreshTaskFileAction());
group.add(new StudyShowHintAction());
group.add(new StudyRunAction());
group.add(new StudyEditInputAction());
return group;
}
return null;
}
}
@@ -0,0 +1,40 @@
package com.jetbrains.edu.learning;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.project.Project;
import com.intellij.util.containers.HashMap;
import com.jetbrains.edu.courseFormat.Course;
import com.jetbrains.edu.learning.actions.*;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
public class PyStudyToolWindowConfigurator implements StudyToolWindowConfigurator {
@NotNull
@Override
public DefaultActionGroup getActionGroup(Project project) {
Course course = StudyTaskManager.getInstance(project).getCourse();
final DefaultActionGroup group = new DefaultActionGroup();
group.add(StudyCheckAction.createCheckAction(course));
group.add(new StudyPreviousStudyTaskAction());
group.add(new StudyNextStudyTaskAction());
group.add(new StudyRefreshTaskFileAction());
group.add(new StudyShowHintAction());
group.add(new StudyRunAction());
group.add(new StudyEditInputAction());
return group;
}
@Override
public HashMap<String, JPanel> getAdditionalPanels(Project project) {
return new HashMap<>();
}
@Override
public boolean accept(@NotNull Project project) {
Course course = StudyTaskManager.getInstance(project).getCourse();
return course != null && course.getLanguage().equals("Python") && course.getCourseType().equals("PyCharm");
}
}