Fix problem with hint migration from v3EAP to v3
[idea/community.git] / python / educational-core / student / src / com / jetbrains / edu / learning / ui / StudyHint.kt
1 package com.jetbrains.edu.learning.ui
2
3 import com.intellij.icons.AllIcons
4 import com.intellij.ide.browsers.WebBrowserManager
5 import com.intellij.openapi.Disposable
6 import com.intellij.openapi.actionSystem.*
7 import com.intellij.openapi.application.ApplicationManager
8 import com.intellij.openapi.editor.Document
9 import com.intellij.openapi.editor.Editor
10 import com.intellij.openapi.editor.EditorFactory
11 import com.intellij.openapi.editor.ex.EditorEx
12 import com.intellij.openapi.editor.ex.FocusChangeListener
13 import com.intellij.openapi.project.Project
14 import com.intellij.openapi.util.Disposer
15 import com.jetbrains.edu.learning.StudyTaskManager
16 import com.jetbrains.edu.learning.StudyUtils
17 import com.jetbrains.edu.learning.core.EduNames
18 import com.jetbrains.edu.learning.courseFormat.AnswerPlaceholder
19
20 class StudyHint(private val myPlaceholder: AnswerPlaceholder?, project: Project) {
21   val studyToolWindow: StudyToolWindow
22   private var myShownHintNumber = 0
23   private var isEditingMode = false
24   private val newHintDefaultText = "Edit this hint"
25
26   init {
27     val taskManager = StudyTaskManager.getInstance(project)
28     if (StudyUtils.hasJavaFx() && taskManager.shouldUseJavaFx()) {
29       studyToolWindow = StudyJavaFxToolWindow()
30     }
31     else {
32       studyToolWindow = StudySwingToolWindow()
33     }
34     studyToolWindow.init(project, false)
35     
36     if (myPlaceholder == null) {
37       studyToolWindow.setText(OUR_WARNING_MESSAGE)
38     }
39     
40     val course = taskManager.course
41     if (course != null) {
42       val courseMode = course.courseMode
43       val group = DefaultActionGroup()
44       val hints = myPlaceholder?.hints
45       if (hints != null) {
46         if (courseMode == EduNames.STUDY) {
47           if (hints.size > 1) {
48             group.addAll(GoBackward(), GoForward())
49           }
50         }
51         else {
52           group.addAll(GoBackward(), GoForward(), Separator.getInstance(), EditHint(), AddHint(), RemoveHint())
53         }
54         studyToolWindow.setActionToolbar(group)
55         if (!hints.isEmpty()) {
56           studyToolWindow.setText(hints[myShownHintNumber])
57         }
58         else {
59           studyToolWindow.setText("No hints are provided")
60         }
61       }
62     }
63   }
64
65   private inner class GoForward : AnAction("Next Hint", "Next Hint", AllIcons.Actions.Forward) {
66
67
68     override fun actionPerformed(e: AnActionEvent) {
69       studyToolWindow.setText(myPlaceholder!!.hints[++myShownHintNumber])
70     }
71
72     override fun update(e: AnActionEvent) {
73       e.presentation.isEnabled = !isEditingMode && myPlaceholder != null && myShownHintNumber + 1 < myPlaceholder.hints.size
74     }
75   }
76
77   private inner class GoBackward : AnAction("Previous Hint", "Previous Hint", AllIcons.Actions.Back) {
78
79     override fun actionPerformed(e: AnActionEvent) {
80       studyToolWindow.setText(myPlaceholder!!.hints[--myShownHintNumber])
81     }
82
83     override fun update(e: AnActionEvent) {
84       e.presentation.isEnabled = !isEditingMode && myShownHintNumber - 1 >= 0
85     }
86   }
87
88   private inner class EditHint : ToggleAction("Edit Hint", "Edit Hint", AllIcons.Modules.Edit) {
89
90     private var currentDocument: Document? = null
91
92     override fun isSelected(e: AnActionEvent): Boolean {
93       e.project ?: return false
94       return isEditingMode
95     }
96
97     override fun setSelected(e: AnActionEvent, state: Boolean) {
98       val project = e.project ?: return
99       e.presentation.text = if (state) "Save Hint" else "Edit Hint"
100       doOnSelection(state, project)
101     }
102
103     fun doOnSelection(state: Boolean, project: Project) {
104       if (state) {
105         isEditingMode = true
106         val factory = EditorFactory.getInstance()
107         currentDocument = factory.createDocument(myPlaceholder!!.hints[myShownHintNumber])
108         WebBrowserManager.getInstance().isShowBrowserHover = false
109         if (currentDocument != null) {
110           val createdEditor = factory.createEditor(currentDocument as Document, project) as EditorEx
111           Disposer.register(project, Disposable { factory.releaseEditor(createdEditor) })
112           val editorComponent = createdEditor.component
113           studyToolWindow.setTopComponent(editorComponent)
114           studyToolWindow.repaint()
115
116           createdEditor.addFocusListener(object: FocusChangeListener {
117             override fun focusGained(editor: Editor?) {
118               if (createdEditor.document.text == newHintDefaultText) {
119                 ApplicationManager.getApplication().runWriteAction { createdEditor.document.setText("") }
120               }
121             }
122
123             override fun focusLost(editor: Editor?) {
124             }
125
126           })
127         }
128       }
129       else {
130         isEditingMode = false
131         myPlaceholder!!.setHintByIndex(myShownHintNumber, currentDocument!!.text)
132         studyToolWindow.setText(myPlaceholder.hints[myShownHintNumber])
133         studyToolWindow.setDefaultTopComponent()
134       }
135     }
136
137     override fun update(e: AnActionEvent) {
138       e.presentation.isEnabled = myPlaceholder?.hints?.isEmpty() == false
139     }
140   }
141
142   private inner class AddHint : AnAction("Add Hint", "Add Hint", AllIcons.General.Add) {
143
144     override fun actionPerformed(e: AnActionEvent) {
145       myPlaceholder!!.addHint(newHintDefaultText)
146       myShownHintNumber++
147       studyToolWindow.setText(newHintDefaultText)
148     }
149
150     override fun update(e: AnActionEvent?) {
151       e?.presentation?.isEnabled = !isEditingMode && myPlaceholder != null && !myPlaceholder.hints.isEmpty()
152     }
153   }
154
155   private inner class RemoveHint : AnAction("Remove Hint", "Remove Hint", AllIcons.General.Remove) {
156
157     override fun actionPerformed(e: AnActionEvent) {
158       myPlaceholder!!.removeHint(myShownHintNumber)
159       myShownHintNumber = if (myPlaceholder.hints.size == 1) 0 else if (myShownHintNumber + 1 < myPlaceholder.hints.size) myShownHintNumber + 1 else myShownHintNumber - 1
160       studyToolWindow.setText(myPlaceholder.hints[myShownHintNumber])
161     }
162
163     override fun update(e: AnActionEvent) {
164       e.presentation.isEnabled = myPlaceholder != null && myPlaceholder.hints.size > 1 && !isEditingMode
165     }
166   }
167
168   companion object {
169     private val OUR_WARNING_MESSAGE = "Put the caret in the answer placeholder to get hint"
170   }
171 }