int offset = document.getLineStartOffset(line) + start;
addChildWithName(placeholder, OFFSET, offset);
addChildWithName(placeholder, "useLength", "false");
+ addHints(placeholder);
}
}
}
import org.jetbrains.annotations.NotNull;
import java.io.*;
+import java.util.ArrayList;
import java.util.Map;
public class CCFromCourseArchive extends DumbAwareAction {
answerPlaceholder.init();
final VirtualFile hints = project.getBaseDir().findChild(EduNames.HINTS);
if (hints != null) {
- final String hintFile = answerPlaceholder.getHint();
- final VirtualFile virtualFile = hints.findChild(hintFile);
- if (virtualFile != null) {
- final Document hintDocument = FileDocumentManager.getInstance().getDocument(virtualFile);
- if (hintDocument != null) {
- final String hintText = hintDocument.getText();
- answerPlaceholder.setHint(hintText);
- }
+ final ArrayList<String> result = new ArrayList<>();
+ for (String hint : answerPlaceholder.getHints()) {
+ final VirtualFile virtualFile = hints.findChild(hint);
+ if (virtualFile != null) {
+ final Document hintDocument = FileDocumentManager.getInstance().getDocument(virtualFile);
+ if (hintDocument != null) {
+ final String hintText = hintDocument.getText();
+ result.add(hintText);
+ }
+ }
}
+ answerPlaceholder.setHints(result);
}
document.replaceString(offset, offset + answerPlaceholder.getRealLength(), answerPlaceholder.getPossibleAnswer());
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
+import java.util.ArrayList;
+import java.util.List;
public class CCCreateAnswerPlaceholderDialog extends DialogWrapper {
setTitle(ourTitle);
myAnswerPlaceholder = answerPlaceholder;
myPanel = new CCCreateAnswerPlaceholderPanel();
- if (answerPlaceholder.getHint() != null) {
+ if (answerPlaceholder.getHints() != null) {
setHintText(answerPlaceholder);
}
myProject = project;
String answerPlaceholderTaskText = answerPlaceholder.getTaskText();
myPanel.setAnswerPlaceholderText(answerPlaceholderTaskText != null ? answerPlaceholderTaskText : "");
- String hintName = answerPlaceholder.getHint();
- myPanel.setHintText(hintName != null ? hintName : "");
+ List<String> hintName = answerPlaceholder.getHints();
+ myPanel.setHintText(!hintName.isEmpty() ? hintName.get(0) : "");
init();
initValidation();
}
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
private void setHintText(AnswerPlaceholder answerPlaceholder) {
- String hintText = answerPlaceholder.getHint();
+ List<String> hintText = answerPlaceholder.getHints();
- myPanel.setHintText(hintText);
+ myPanel.setHintText(hintText.isEmpty()? "" : hintText.get(0));
}
@Override
String answerPlaceholderText = myPanel.getAnswerPlaceholderText();
myAnswerPlaceholder.setTaskText(StringUtil.notNullize(answerPlaceholderText));
myAnswerPlaceholder.setLength(StringUtil.notNullize(answerPlaceholderText).length());
- myAnswerPlaceholder.setHint(myPanel.getHintText());
+ myAnswerPlaceholder.setHints(
+ new ArrayList<String>() {{
+ add(myPanel.getHintText());
+ }});
super.doOKAction();
}
@Nullable
@Override
public ValidationInfo doValidate() {
- return myAnswerPlaceholder.getHint() != null ? null : new ValidationInfo("Type hint");
+ return myAnswerPlaceholder.getHints() != null ? null : new ValidationInfo("Type hint");
}
@Nullable
import org.jdom.Attribute;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;
+import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.lang.reflect.Type;
public static final String MY_LINE = "myLine";
public static final String MY_START = "myStart";
public static final String MY_LENGTH = "myLength";
+ public static final String HINTS = "hints";
+ public static final String HINT = "hint";
public static final String AUTHOR_TITLED = "Author";
public static final String FIRST_NAME = "first_name";
public static final String SECOND_NAME = "second_name";
taskStatus = addStatus(outputter, placeholderTextToStatus, taskStatus, placeholder);
addOffset(document, placeholder);
addInitialState(document, placeholder);
+ addHints(placeholder);
}
}
if (taskStatus != null) {
addChildWithName(placeholder, OFFSET, offset);
}
+ public static void addHints(@NotNull Element placeholder) throws StudyUnrecognizedFormatException {
+ final String hint = getChildWithName(placeholder, HINT).getAttribute(VALUE).getValue();
+ Element listElement = new Element(LIST);
+ final Element hintElement = new Element(OPTION);
+ hintElement.setAttribute(VALUE, hint);
+ listElement.setContent(hintElement);
+ addChildWithName(placeholder, HINTS, listElement);
+ }
+
public static int getAsInt(Element element, String name) throws StudyUnrecognizedFormatException {
return Integer.valueOf(getChildWithName(element, name).getAttributeValue(VALUE));
}
package com.jetbrains.edu.learning.actions;
-import com.intellij.codeInsight.documentation.DocumentationComponent;
import com.intellij.codeInsight.documentation.DocumentationManager;
+import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.KeyboardShortcut;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.Disposer;
-import com.intellij.psi.PsiElement;
+import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
+import com.intellij.ui.popup.PopupPositionManager;
import com.jetbrains.edu.learning.StudyState;
import com.jetbrains.edu.learning.StudyTaskManager;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.statistics.EduUsagesCollector;
+import com.jetbrains.edu.learning.ui.StudyHint;
+import com.jetbrains.edu.learning.ui.StudyToolWindow;
import icons.InteractiveLearningIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
+import java.awt.*;
+import java.util.LinkedList;
public class StudyShowHintAction extends StudyActionWithShortcut {
public static final String ACTION_ID = "ShowHintAction";
return;
}
EduUsagesCollector.hintShown();
- String hintText = ourWarningMessage;
+ LinkedList<String> hints = new LinkedList<>();
if (answerPlaceholder != null) {
- String hint = answerPlaceholder.getHint();
- hintText = hint.isEmpty() ? HINT_NOT_AVAILABLE : hint;
+ hints.addAll(answerPlaceholder.getHints());
+ //final ArrayList<String> strings = new ArrayList<>();
+ //strings.add(answerPlaceholder.getHints());
+ //strings.add("test");
+ //hints.addAll(strings);
}
- PsiElement element = file.findElementAt(offset);
- DocumentationManager documentationManager = DocumentationManager.getInstance(project);
- DocumentationComponent component = new DocumentationComponent(documentationManager);
- component.setData(element != null ? element : file, element != null ? hintText : ourWarningMessage, true, null);
- showHintPopUp(project, editor, component);
+ else {
+ hints.add(ourWarningMessage);
+ }
+ final StudyToolWindow hintComponent = new StudyHint(answerPlaceholder, project).getStudyToolWindow();
+
+ showHintPopUp(project, studyState, editor, hintComponent);
}
- private static void showHintPopUp(Project project, Editor editor, DocumentationComponent component) {
- final JBPopup popup =
- JBPopupFactory.getInstance().createComponentPopupBuilder(component, component)
+ private static void showHintPopUp(Project project, StudyState studyState, Editor editor, StudyToolWindow hintComponent) {
+ final JBPopup popup =
+ JBPopupFactory.getInstance().createComponentPopupBuilder(hintComponent, hintComponent)
.setDimensionServiceKey(project, DocumentationManager.JAVADOC_LOCATION_AND_SIZE, false)
.setResizable(true)
.setMovable(true)
.setRequestFocus(true)
+ .setTitle(studyState.getTask().getName())
.createPopup();
- component.setHint(popup);
- popup.showInBestPositionFor(editor);
- Disposer.dispose(component);
+ Disposer.register(popup, hintComponent);
+
+ final Component focusOwner = IdeFocusManager.getInstance(project).getFocusOwner();
+ DataContext dataContext = DataManager.getInstance().getDataContext(focusOwner);
+ PopupPositionManager.positionPopupInBestPosition(popup, editor, dataContext);
}
@Override
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import com.intellij.util.xmlb.annotations.Transient;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Implementation of windows which user should type in
*/
public class AnswerPlaceholder {
- @Expose private String hint = "";
+ @Expose private List<String> myHints = new ArrayList<String>();
@SerializedName("possible_answer")
@Expose private String possibleAnswer = "";
this.length = length;
}
- public String getHint() {
- return hint;
+ @NotNull
+ public List<String> getHints() {
+ return myHints;
}
- public void setHint(@Nullable final String hint) {
- this.hint = hint;
+ public void setHints(@Nullable final List<String> hints) {
+ myHints = hints;
}
public String getPossibleAnswer() {
answerPlaceholderCopy.setLength(answerPlaceholder.getLength());
answerPlaceholderCopy.setPossibleAnswer(answerPlaceholder.getPossibleAnswer());
answerPlaceholderCopy.setIndex(answerPlaceholder.getIndex());
- answerPlaceholderCopy.setHint(answerPlaceholder.getHint());
+ answerPlaceholderCopy.setHints(answerPlaceholder.getHints());
final AnswerPlaceholder.MyInitialState state = answerPlaceholder.getInitialState();
if (state != null) {
answerPlaceholderCopy.setInitialState(new AnswerPlaceholder.MyInitialState(state.getOffset(), state.getLength()));
--- /dev/null
+package com.jetbrains.edu.learning.ui
+
+import com.intellij.icons.AllIcons
+import com.intellij.ide.browsers.WebBrowserManager
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.actionSystem.*
+import com.intellij.openapi.editor.Document
+import com.intellij.openapi.editor.EditorFactory
+import com.intellij.openapi.editor.ex.EditorEx
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.util.Disposer
+import com.jetbrains.edu.learning.StudyTaskManager
+import com.jetbrains.edu.learning.StudyUtils
+import com.jetbrains.edu.learning.core.EduNames
+import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder
+import java.util.*
+
+class StudyHint(private val myPlaceholder: AnswerPlaceholder?, project: Project) {
+ val studyToolWindow: StudyToolWindow
+ private val myHints = LinkedList<String>()
+ private var myShownHintNumber = 0
+ private var isEditingMode = false
+
+ init {
+ if (myPlaceholder == null) {
+ myHints.add(OUR_WARNING_MESSAGE)
+ }
+ else {
+ myHints.addAll(myPlaceholder.hints)
+ }
+ val taskManager = StudyTaskManager.getInstance(project)
+ if (StudyUtils.hasJavaFx() && taskManager.shouldUseJavaFx()) {
+ studyToolWindow = StudyJavaFxToolWindow()
+ }
+ else {
+ studyToolWindow = StudySwingToolWindow()
+ }
+ studyToolWindow.init(project, false)
+ val course = taskManager.course
+ if (course != null) {
+ val courseMode = course.courseMode
+ val group = DefaultActionGroup()
+ group.addAll(GoBackward(), GoForward())
+ if (EduNames.STUDY != courseMode) {
+ group.addAll(Separator.getInstance(), EditHint(), AddHint(), RemoveHint())
+ }
+ studyToolWindow.setActionToolbar(group)
+ if (!myHints.isEmpty()) {
+ studyToolWindow.setText(myHints[myShownHintNumber])
+ }
+ else {
+ studyToolWindow.setText("No hints are provided")
+ }
+ }
+ }
+
+ private inner class GoForward : AnAction(AllIcons.Actions.Forward) {
+
+ override fun actionPerformed(e: AnActionEvent) {
+ studyToolWindow.setText(myHints[++myShownHintNumber])
+ }
+
+ override fun update(e: AnActionEvent) {
+ e.presentation.isEnabled = !isEditingMode && myShownHintNumber + 1 < myHints.size
+ }
+ }
+
+ private inner class GoBackward : AnAction(AllIcons.Actions.Back) {
+
+ override fun actionPerformed(e: AnActionEvent) {
+ studyToolWindow.setText(myHints[--myShownHintNumber])
+ }
+
+ override fun update(e: AnActionEvent) {
+ e.presentation.isEnabled = !isEditingMode && myShownHintNumber - 1 >= 0
+ }
+ }
+
+ private inner class EditHint : ToggleAction("Edit Hint", "Edit Hint", AllIcons.Modules.Edit) {
+
+ private var currentDocument: Document? = null
+
+ override fun isSelected(e: AnActionEvent): Boolean {
+ e.project ?: return false
+ return isEditingMode
+ }
+
+ override fun setSelected(e: AnActionEvent, state: Boolean) {
+ val project = e.project ?: return
+
+ doOnSelection(state, project)
+ }
+
+ fun doOnSelection(state: Boolean, project: Project) {
+ if (state) {
+ isEditingMode = true
+ val factory = EditorFactory.getInstance()
+ currentDocument = factory.createDocument(myHints[myShownHintNumber])
+ WebBrowserManager.getInstance().isShowBrowserHover = false
+ if (currentDocument != null) {
+ val createdEditor = factory.createEditor(currentDocument as Document, project) as EditorEx
+ Disposer.register(project, Disposable { factory.releaseEditor(createdEditor) })
+ val editorComponent = createdEditor.component
+ studyToolWindow.setTopComponent(editorComponent)
+ studyToolWindow.repaint()
+ }
+ }
+ else {
+ isEditingMode = false
+ myHints[myShownHintNumber] = currentDocument!!.text
+ val hints = myPlaceholder!!.hints
+ hints[myShownHintNumber] = currentDocument!!.text
+ studyToolWindow.setText(myHints[myShownHintNumber])
+ studyToolWindow.setDefaultTopComponent()
+ }
+ }
+
+ override fun update(e: AnActionEvent) {
+ e.presentation.isEnabled = !myHints.isEmpty() && myPlaceholder != null
+ }
+ }
+
+ private inner class AddHint : AnAction(AllIcons.General.Add) {
+
+ override fun actionPerformed(e: AnActionEvent) {
+ val project = e.project ?: return
+ val newHint = "New hint"
+ myHints.add(newHint)
+ myPlaceholder!!.hints.add(newHint)
+ myShownHintNumber++
+ studyToolWindow.setText(newHint)
+ val actions = studyToolWindow.getActions(true)
+ for (action in actions) {
+ if (action is EditHint) {
+ action.doOnSelection(true, project)
+ action.isSelected(e)
+ return
+ }
+ }
+ }
+
+ override fun update(e: AnActionEvent?) {
+ e?.presentation?.isEnabled = !isEditingMode && !myHints.isEmpty()
+ }
+ }
+
+ private inner class RemoveHint : AnAction(AllIcons.Actions.Cancel) {
+
+ override fun actionPerformed(e: AnActionEvent) {
+ myHints.removeAt(myShownHintNumber)
+ myPlaceholder!!.hints.removeAt(myShownHintNumber)
+ myShownHintNumber = if (myHints.size == 1) 0 else if (myShownHintNumber + 1 < myHints.size) myShownHintNumber + 1 else myShownHintNumber - 1
+ studyToolWindow.setText(myHints[myShownHintNumber])
+ }
+
+ override fun update(e: AnActionEvent) {
+ e.presentation.isEnabled = myHints.size > 1 && !isEditingMode
+ }
+ }
+
+ companion object {
+
+ private val OUR_WARNING_MESSAGE = "Put the caret in the answer placeholder to get hint"
+ }
+}
mySplitPane = new OnePixelSplitter(myVertical = true);
}
- public void init(Project project) {
+ public void init(@NotNull final Project project, final boolean isToolwindow) {
String taskText = StudyUtils.getTaskText(project);
if (taskText == null) return;
- JPanel toolbarPanel = createToolbarPanel(getActionGroup(project));
- setToolbar(toolbarPanel);
+ final DefaultActionGroup group = getActionGroup(project);
+ setActionToolbar(group);
final JPanel panel = new JPanel(new BorderLayout());
final Course course = StudyTaskManager.getInstance(project).getCourse();
- if (course != null && course.isAdaptive()) {
+ if (isToolwindow && course != null && course.isAdaptive()) {
panel.add(new StepicAdaptiveReactionsPanel(project), BorderLayout.NORTH);
}
+
JComponent taskInfoPanel = createTaskInfoPanel(project);
panel.add(taskInfoPanel, BorderLayout.CENTER);
+
final JPanel courseProgress = createCourseProgress(project);
- if (course != null && !course.isAdaptive() && EduNames.STUDY.equals(course.getCourseMode())) {
+ if (isToolwindow && course != null && !course.isAdaptive() && EduNames.STUDY.equals(course.getCourseMode())) {
panel.add(courseProgress, BorderLayout.SOUTH);
}
myCardLayout.show(myContentPanel, TASK_INFO_ID);
setContent(mySplitPane);
+
+ if (isToolwindow) {
+ StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project);
+ if (configurator != null) {
+ final FileEditorManagerListener listener = configurator.getFileEditorManagerListener(project, this);
+ project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, listener);
+ }
- StudyPluginConfigurator configurator = StudyUtils.getConfigurator(project);
- if (configurator != null) {
- final FileEditorManagerListener listener = configurator.getFileEditorManagerListener(project, this);
- project.getMessageBus().connect().subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, listener);
- }
-
- if (StudyTaskManager.getInstance(project).isTurnEditingMode() ||
- StudyTaskManager.getInstance(project).getToolWindowMode() == StudyToolWindowMode.EDITING) {
- TaskFile file = StudyUtils.getSelectedTaskFile(project);
- if (file != null) {
- VirtualFile taskDir = file.getTask().getTaskDir(project);
- setTaskText(taskText, taskDir, project);
+ if (StudyTaskManager.getInstance(project).isTurnEditingMode() ||
+ StudyTaskManager.getInstance(project).getToolWindowMode() == StudyToolWindowMode.EDITING) {
+ TaskFile file = StudyUtils.getSelectedTaskFile(project);
+ if (file != null) {
+ VirtualFile taskDir = file.getTask().getTaskDir(project);
+ setTaskText(taskText, taskDir, project);
+ }
+ }
+ else {
+ setTaskText(taskText, null, project);
}
}
- else {
- setTaskText(taskText, null, project);
- }
+ }
+
+ public void setTopComponent(@NotNull final JComponent component) {
+ mySplitPane.setFirstComponent(component);
+ }
+
+ public void setDefaultTopComponent() {
+ mySplitPane.setFirstComponent(myContentPanel);
+ }
+
+ public void setActionToolbar(DefaultActionGroup group) {
+ JPanel toolbarPanel = createToolbarPanel(group);
+ setToolbar(toolbarPanel);
}
private void addAdditionalPanels(Project project) {
else {
studyToolWindow = new StudySwingToolWindow();
}
- studyToolWindow.init(project);
+ studyToolWindow.init(project, true);
final ContentManager contentManager = toolWindow.getContentManager();
final Content content = contentManager.getFactory().createContent(studyToolWindow, null, false);
contentManager.addContent(content);