9dd7d85dbd7dacce0a5ee7f470c2ca87e2055693
[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   
22   companion object {
23     private val OUR_WARNING_MESSAGE = "Put the caret in the answer placeholder to get hint"
24     private val HINTS_NOT_AVAILABLE = "There is no hint for this answer placeholder"
25   }
26   
27   val studyToolWindow: StudyToolWindow
28   private var myShownHintNumber = 0
29   private var isEditingMode = false
30   private val newHintDefaultText = "Edit this hint"
31
32   init {
33     val taskManager = StudyTaskManager.getInstance(project)
34     if (StudyUtils.hasJavaFx() && taskManager.shouldUseJavaFx()) {
35       studyToolWindow = StudyJavaFxToolWindow()
36     }
37     else {
38       studyToolWindow = StudySwingToolWindow()
39     }
40     studyToolWindow.init(project, false)
41     
42     if (myPlaceholder == null) {
43       studyToolWindow.setText(OUR_WARNING_MESSAGE)
44     }
45     
46     val course = taskManager.course
47     if (course != null) {
48       val courseMode = course.courseMode
49       val group = DefaultActionGroup()
50       val hints = myPlaceholder?.hints
51       if (hints != null) {
52         if (courseMode == EduNames.STUDY) {
53           if (hints.size > 1) {
54             group.addAll(GoBackward(), GoForward())
55           }
56         }
57         else {
58           group.addAll(GoBackward(), GoForward(), Separator.getInstance(), EditHint(), AddHint(), RemoveHint())
59         }
60         studyToolWindow.setActionToolbar(group)
61         setHintText(hints)
62       }
63     }
64   }
65
66   private fun setHintText(hints: List<String>) {
67     if (!hints.isEmpty()) {
68       studyToolWindow.setText(hints[myShownHintNumber])
69     }
70     else {
71       myShownHintNumber = -1
72       studyToolWindow.setText(HINTS_NOT_AVAILABLE)
73     }
74   }
75
76   private inner class GoForward : AnAction("Next Hint", "Next Hint", AllIcons.Actions.Forward) {
77
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 && myPlaceholder != null && myShownHintNumber + 1 < myPlaceholder.hints.size
85     }
86   }
87
88   private inner class GoBackward : AnAction("Previous Hint", "Previous Hint", AllIcons.Actions.Back) {
89
90     override fun actionPerformed(e: AnActionEvent) {
91       studyToolWindow.setText(myPlaceholder!!.hints[--myShownHintNumber])
92     }
93
94     override fun update(e: AnActionEvent) {
95       e.presentation.isEnabled = !isEditingMode && myShownHintNumber - 1 >= 0
96     }
97   }
98
99   private inner class EditHint : ToggleAction("Edit Hint", "Edit Hint", AllIcons.Modules.Edit) {
100
101     private var currentDocument: Document? = null
102
103     override fun isSelected(e: AnActionEvent): Boolean {
104       e.project ?: return false
105       return isEditingMode
106     }
107
108     override fun setSelected(e: AnActionEvent, state: Boolean) {
109       val project = e.project ?: return
110       e.presentation.text = if (state) "Save Hint" else "Edit Hint"
111       doOnSelection(state, project)
112     }
113
114     fun doOnSelection(state: Boolean, project: Project) {
115       if (state) {
116         isEditingMode = true
117         val factory = EditorFactory.getInstance()
118         val initialText = if (myShownHintNumber < myPlaceholder!!.hints.size) myPlaceholder.hints[myShownHintNumber] else ""
119         currentDocument = factory.createDocument(initialText)
120         WebBrowserManager.getInstance().isShowBrowserHover = false
121         if (currentDocument != null) {
122           val createdEditor = factory.createEditor(currentDocument as Document, project) as EditorEx
123           Disposer.register(project, Disposable { factory.releaseEditor(createdEditor) })
124           val editorComponent = createdEditor.component
125           studyToolWindow.setTopComponent(editorComponent)
126           studyToolWindow.repaint()
127
128           createdEditor.addFocusListener(object: FocusChangeListener {
129             override fun focusGained(editor: Editor?) {
130               if (createdEditor.document.text == newHintDefaultText) {
131                 ApplicationManager.getApplication().runWriteAction { createdEditor.document.setText("") }
132               }
133             }
134
135             override fun focusLost(editor: Editor?) {
136             }
137
138           })
139         }
140       }
141       else {
142         isEditingMode = false
143         if (myShownHintNumber < myPlaceholder!!.hints.size) {
144           myPlaceholder.setHintByIndex(myShownHintNumber, currentDocument!!.text)
145         }
146         else {
147           myPlaceholder.addHint(currentDocument!!.text)
148         }
149         setHintText(myPlaceholder.hints)
150         studyToolWindow.setDefaultTopComponent()
151       }
152     }
153
154     override fun update(e: AnActionEvent) {
155       e.presentation.isEnabled = myPlaceholder?.hints?.isEmpty() == false
156     }
157   }
158
159   private inner class AddHint : AnAction("Add Hint", "Add Hint", AllIcons.General.Add) {
160
161     override fun actionPerformed(e: AnActionEvent) {
162       myPlaceholder!!.addHint(newHintDefaultText)
163       myShownHintNumber++
164       studyToolWindow.setText(newHintDefaultText)
165     }
166
167     override fun update(e: AnActionEvent?) {
168       e?.presentation?.isEnabled = !isEditingMode && myPlaceholder != null
169     }
170   }
171
172   private inner class RemoveHint : AnAction("Remove Hint", "Remove Hint", AllIcons.General.Remove) {
173
174     override fun actionPerformed(e: AnActionEvent) {
175       myPlaceholder!!.removeHint(myShownHintNumber)
176       myShownHintNumber += if (myShownHintNumber < myPlaceholder.hints.size) 0 else -1
177       
178       setHintText(myPlaceholder.hints)
179     }
180
181     override fun update(e: AnActionEvent) {
182       e.presentation.isEnabled = myPlaceholder != null && myPlaceholder.hints.size > 0 && !isEditingMode
183     }
184   }
185 }