|
1 /* Title: Tools/jEdit/src/query_dockable.scala |
|
2 Author: Makarius |
|
3 |
|
4 Dockable window for query operations. |
|
5 */ |
|
6 |
|
7 package isabelle.jedit |
|
8 |
|
9 |
|
10 import isabelle._ |
|
11 |
|
12 import java.awt.event.{ComponentEvent, ComponentAdapter, KeyEvent} |
|
13 import javax.swing.{JComponent, JTextField} |
|
14 |
|
15 import scala.swing.{Button, Component, TextField, CheckBox, Label, ListView, |
|
16 ComboBox, TabbedPane, BorderPanel} |
|
17 import scala.swing.event.{SelectionChanged, ButtonClicked, Key, KeyPressed} |
|
18 |
|
19 import org.gjt.sp.jedit.View |
|
20 |
|
21 |
|
22 object Query_Dockable |
|
23 { |
|
24 private abstract class Operation(view: View) |
|
25 { |
|
26 val pretty_text_area = new Pretty_Text_Area(view) |
|
27 def query_operation: Query_Operation[View] |
|
28 def query: JComponent |
|
29 def select: Unit |
|
30 def page: TabbedPane.Page |
|
31 } |
|
32 } |
|
33 |
|
34 class Query_Dockable(view: View, position: String) extends Dockable(view, position) |
|
35 { |
|
36 /* common GUI components */ |
|
37 |
|
38 private var zoom_factor = 100 |
|
39 |
|
40 private val zoom = |
|
41 new GUI.Zoom_Box(factor => if (zoom_factor != factor) { zoom_factor = factor; handle_resize() }) |
|
42 { |
|
43 tooltip = "Zoom factor for output font size" |
|
44 } |
|
45 |
|
46 |
|
47 private def make_query(property: String, tooltip: String, apply_query: () => Unit): JTextField = |
|
48 new Completion_Popup.History_Text_Field(property) |
|
49 { |
|
50 override def processKeyEvent(evt: KeyEvent) |
|
51 { |
|
52 if (evt.getID == KeyEvent.KEY_PRESSED && evt.getKeyCode == KeyEvent.VK_ENTER) apply_query() |
|
53 super.processKeyEvent(evt) |
|
54 } |
|
55 { val max = getPreferredSize; max.width = Integer.MAX_VALUE; setMaximumSize(max) } |
|
56 setColumns(40) |
|
57 setToolTipText(tooltip) |
|
58 setFont(GUI.imitate_font(Font_Info.main_family(), getFont, 1.2)) |
|
59 } |
|
60 |
|
61 |
|
62 /* consume status */ |
|
63 |
|
64 def consume_status(process_indicator: Process_Indicator, status: Query_Operation.Status.Value) |
|
65 { |
|
66 status match { |
|
67 case Query_Operation.Status.WAITING => |
|
68 process_indicator.update("Waiting for evaluation of context ...", 5) |
|
69 case Query_Operation.Status.RUNNING => |
|
70 process_indicator.update("Running find operation ...", 15) |
|
71 case Query_Operation.Status.FINISHED => |
|
72 process_indicator.update(null, 0) |
|
73 } |
|
74 } |
|
75 |
|
76 |
|
77 /* find theorems */ |
|
78 |
|
79 private val find_theorems = new Query_Dockable.Operation(view) |
|
80 { |
|
81 /* query */ |
|
82 |
|
83 private val process_indicator = new Process_Indicator |
|
84 |
|
85 val query_operation = |
|
86 new Query_Operation(PIDE.editor, view, "find_theorems", |
|
87 consume_status(process_indicator, _), |
|
88 (snapshot, results, body) => |
|
89 pretty_text_area.update(snapshot, results, Pretty.separate(body))) |
|
90 |
|
91 private def apply_query() |
|
92 { |
|
93 query_operation.apply_query(List(limit.text, allow_dups.selected.toString, query.getText)) |
|
94 } |
|
95 |
|
96 private val query_label = new Label("Search criteria:") { |
|
97 tooltip = |
|
98 GUI.tooltip_lines( |
|
99 "Search criteria for find operation, e.g.\n\"_ = _\" \"op +\" name: Group -name: monoid") |
|
100 } |
|
101 |
|
102 val query = make_query("isabelle-find-theorems", query_label.tooltip, apply_query _) |
|
103 |
|
104 |
|
105 /* GUI page */ |
|
106 |
|
107 private val limit = new TextField(PIDE.options.int("find_theorems_limit").toString, 5) { |
|
108 tooltip = "Limit of displayed results" |
|
109 verifier = (s: String) => |
|
110 s match { case Properties.Value.Int(x) => x >= 0 case _ => false } |
|
111 listenTo(keys) |
|
112 reactions += { case KeyPressed(_, Key.Enter, 0, _) => apply_query() } |
|
113 } |
|
114 |
|
115 private val allow_dups = new CheckBox("Duplicates") { |
|
116 tooltip = "Show all versions of matching theorems" |
|
117 selected = false |
|
118 } |
|
119 |
|
120 private val apply_button = new Button("Apply") { |
|
121 tooltip = "Find theorems meeting specified criteria" |
|
122 reactions += { case ButtonClicked(_) => apply_query() } |
|
123 } |
|
124 |
|
125 private val control_panel = |
|
126 new Wrap_Panel(Wrap_Panel.Alignment.Right)( |
|
127 query_label, Component.wrap(query), limit, allow_dups, |
|
128 process_indicator.component, apply_button) |
|
129 |
|
130 def select { control_panel.contents += zoom } |
|
131 |
|
132 val page = |
|
133 new TabbedPane.Page("Find Theorems", new BorderPanel { |
|
134 add(control_panel, BorderPanel.Position.North) |
|
135 add(Component.wrap(pretty_text_area), BorderPanel.Position.Center) |
|
136 }, apply_button.tooltip) |
|
137 } |
|
138 |
|
139 |
|
140 /* find consts */ |
|
141 |
|
142 private val find_consts = new Query_Dockable.Operation(view) |
|
143 { |
|
144 /* query */ |
|
145 |
|
146 private val process_indicator = new Process_Indicator |
|
147 |
|
148 val query_operation = |
|
149 new Query_Operation(PIDE.editor, view, "find_consts", |
|
150 consume_status(process_indicator, _), |
|
151 (snapshot, results, body) => |
|
152 pretty_text_area.update(snapshot, results, Pretty.separate(body))) |
|
153 |
|
154 private def apply_query() |
|
155 { |
|
156 query_operation.apply_query(List(query.getText)) |
|
157 } |
|
158 |
|
159 private val query_label = new Label("Search criteria:") { |
|
160 tooltip = GUI.tooltip_lines("Name / type patterns for constants") |
|
161 } |
|
162 |
|
163 val query = make_query("isabelle-find-consts", query_label.tooltip, apply_query _) |
|
164 |
|
165 |
|
166 /* GUI page */ |
|
167 |
|
168 private val apply_button = new Button("Apply") { |
|
169 tooltip = "Find constants by name / type patterns" |
|
170 reactions += { case ButtonClicked(_) => apply_query() } |
|
171 } |
|
172 |
|
173 private val control_panel = |
|
174 new Wrap_Panel(Wrap_Panel.Alignment.Right)( |
|
175 query_label, Component.wrap(query), process_indicator.component, apply_button) |
|
176 |
|
177 def select { control_panel.contents += zoom } |
|
178 |
|
179 val page = |
|
180 new TabbedPane.Page("Find Constants", new BorderPanel { |
|
181 add(control_panel, BorderPanel.Position.North) |
|
182 add(Component.wrap(pretty_text_area), BorderPanel.Position.Center) |
|
183 }, apply_button.tooltip) |
|
184 } |
|
185 |
|
186 |
|
187 /* print operation */ |
|
188 |
|
189 private val print_operation = new Query_Dockable.Operation(view) |
|
190 { |
|
191 /* query */ |
|
192 |
|
193 val query_operation = |
|
194 new Query_Operation(PIDE.editor, view, "print_operation", _ => (), |
|
195 (snapshot, results, body) => |
|
196 pretty_text_area.update(snapshot, results, Pretty.separate(body))) |
|
197 |
|
198 private def apply_query() |
|
199 { |
|
200 query_operation.apply_query(List(_selector.selection.item)) |
|
201 } |
|
202 |
|
203 private val query_label = new Label("Print:") |
|
204 |
|
205 def query: JComponent = _selector.peer.asInstanceOf[JComponent] |
|
206 |
|
207 |
|
208 /* items */ |
|
209 |
|
210 case class Item(name: String, description: String) |
|
211 |
|
212 class Renderer(old_renderer: ListView.Renderer[String], items: List[Item]) |
|
213 extends ListView.Renderer[String] |
|
214 { |
|
215 def componentFor(list: ListView[_], |
|
216 selected: Boolean, focused: Boolean, item: String, index: Int) = |
|
217 { |
|
218 val component = old_renderer.componentFor(list, selected, focused, item, index) |
|
219 try { component.tooltip = items(index).description } |
|
220 catch { case _: IndexOutOfBoundsException => } |
|
221 component |
|
222 } |
|
223 } |
|
224 |
|
225 private var _selector_item: Option[String] = None |
|
226 private var _selector = new ComboBox[String](Nil) |
|
227 |
|
228 private def set_selector() |
|
229 { |
|
230 val items = Print_Operation.print_operations(PIDE.session).map(p => Item(p._1, p._2)) |
|
231 |
|
232 _selector = |
|
233 new ComboBox(items.map(_.name)) { |
|
234 _selector_item.foreach(item => selection.item = item) |
|
235 listenTo(selection) |
|
236 reactions += { |
|
237 case SelectionChanged(_) => |
|
238 _selector_item = Some(selection.item) |
|
239 apply_query() |
|
240 } |
|
241 } |
|
242 _selector.renderer = new Renderer(_selector.renderer, items) |
|
243 } |
|
244 set_selector() |
|
245 |
|
246 |
|
247 /* GUI page */ |
|
248 |
|
249 private val apply_button = new Button("Apply") { |
|
250 tooltip = "Apply to current context" |
|
251 reactions += { case ButtonClicked(_) => apply_query() } |
|
252 } |
|
253 |
|
254 private val control_panel = new Wrap_Panel(Wrap_Panel.Alignment.Right)() |
|
255 |
|
256 def select |
|
257 { |
|
258 set_selector() |
|
259 control_panel.contents.clear |
|
260 control_panel.contents ++= List(query_label, _selector, apply_button, zoom) |
|
261 } |
|
262 |
|
263 val page = |
|
264 new TabbedPane.Page("Print Context", new BorderPanel { |
|
265 add(control_panel, BorderPanel.Position.North) |
|
266 add(Component.wrap(pretty_text_area), BorderPanel.Position.Center) |
|
267 }, "Print information from context") |
|
268 } |
|
269 |
|
270 |
|
271 /* operations */ |
|
272 |
|
273 private val operations = List(find_theorems, find_consts, print_operation) |
|
274 |
|
275 private val operations_pane = new TabbedPane |
|
276 { |
|
277 pages ++= operations.map(_.page) |
|
278 listenTo(selection) |
|
279 reactions += { case SelectionChanged(_) => select_operation() } |
|
280 } |
|
281 |
|
282 private def get_operation(): Option[Query_Dockable.Operation] = |
|
283 try { Some(operations(operations_pane.selection.index)) } |
|
284 catch { case _: IndexOutOfBoundsException => None } |
|
285 |
|
286 private def select_operation() { |
|
287 for (op <- get_operation()) { op.select; op.query.requestFocus } |
|
288 operations_pane.revalidate |
|
289 } |
|
290 |
|
291 override def focusOnDefaultComponent { for (op <- get_operation()) op.query.requestFocus } |
|
292 |
|
293 select_operation() |
|
294 set_content(operations_pane) |
|
295 |
|
296 |
|
297 /* resize */ |
|
298 |
|
299 private def handle_resize(): Unit = |
|
300 Swing_Thread.require { |
|
301 for (op <- operations) { |
|
302 op.pretty_text_area.resize( |
|
303 Font_Info.main(PIDE.options.real("jedit_font_scale") * zoom_factor / 100)) |
|
304 } |
|
305 } |
|
306 |
|
307 private val delay_resize = |
|
308 Swing_Thread.delay_first(PIDE.options.seconds("editor_update_delay")) { handle_resize() } |
|
309 |
|
310 addComponentListener(new ComponentAdapter { |
|
311 override def componentResized(e: ComponentEvent) { delay_resize.invoke() } |
|
312 }) |
|
313 |
|
314 |
|
315 /* main */ |
|
316 |
|
317 private val main = |
|
318 Session.Consumer[Session.Global_Options](getClass.getName) { |
|
319 case _: Session.Global_Options => Swing_Thread.later { handle_resize() } |
|
320 } |
|
321 |
|
322 override def init() |
|
323 { |
|
324 PIDE.session.global_options += main |
|
325 handle_resize() |
|
326 operations.foreach(op => op.query_operation.activate()) |
|
327 } |
|
328 |
|
329 override def exit() |
|
330 { |
|
331 operations.foreach(op => op.query_operation.deactivate()) |
|
332 PIDE.session.global_options -= main |
|
333 delay_resize.revoke() |
|
334 } |
|
335 } |
|
336 |