10 |
10 |
11 import isabelle._ |
11 import isabelle._ |
12 |
12 |
13 import scala.actors.Actor._ |
13 import scala.actors.Actor._ |
14 |
14 |
|
15 import java.awt.{BorderLayout, Graphics, Color, Dimension, Graphics2D, Point} |
15 import java.awt.event.{MouseAdapter, MouseMotionAdapter, MouseEvent, FocusAdapter, FocusEvent} |
16 import java.awt.event.{MouseAdapter, MouseMotionAdapter, MouseEvent, FocusAdapter, FocusEvent} |
16 import java.awt.{BorderLayout, Graphics, Color, Dimension, Graphics2D} |
17 import javax.swing.{JPanel, ToolTipManager, Popup, PopupFactory, SwingUtilities} |
17 import javax.swing.{JPanel, ToolTipManager} |
|
18 import javax.swing.event.{CaretListener, CaretEvent} |
18 import javax.swing.event.{CaretListener, CaretEvent} |
19 |
19 |
20 import org.gjt.sp.jedit.{jEdit, OperatingSystem} |
20 import org.gjt.sp.jedit.{jEdit, OperatingSystem} |
21 import org.gjt.sp.jedit.gui.RolloverButton |
21 import org.gjt.sp.jedit.gui.RolloverButton |
22 import org.gjt.sp.jedit.options.GutterOptionPane |
22 import org.gjt.sp.jedit.options.GutterOptionPane |
107 model.buffer.getLineOfOffset(range.start), |
107 model.buffer.getLineOfOffset(range.start), |
108 model.buffer.getLineOfOffset(range.stop)) |
108 model.buffer.getLineOfOffset(range.stop)) |
109 } |
109 } |
110 |
110 |
111 |
111 |
112 /* commands_changed_actor */ |
112 /* HTML popups */ |
113 |
113 |
114 private val commands_changed_actor = actor { |
114 private var html_popup: Option[Popup] = None |
115 loop { |
115 |
116 react { |
116 private def exit_popup() { html_popup.map(_.hide) } |
117 case Session.Commands_Changed(changed) => |
117 |
118 val buffer = model.buffer |
118 private val html_panel = |
119 Isabelle.swing_buffer_lock(buffer) { |
119 new HTML_Panel(Isabelle.system, Isabelle.font_family(), scala.math.round(Isabelle.font_size())) { |
120 val snapshot = model.snapshot() |
120 } |
121 |
121 |
122 if (changed.exists(snapshot.node.commands.contains)) |
122 private def html_panel_resize() |
123 overview.repaint() |
123 { |
124 |
124 Swing_Thread.now { |
125 val visible_range = screen_lines_range() |
125 html_panel.resize(Isabelle.font_family(), scala.math.round(Isabelle.font_size())) |
126 val visible_cmds = snapshot.node.command_range(snapshot.revert(visible_range)).map(_._1) |
126 } |
127 if (visible_cmds.exists(changed)) { |
127 } |
128 for { |
128 |
129 line <- 0 until text_area.getVisibleLines |
129 private def init_popup(snapshot: Document.Snapshot, x: Int, y: Int) |
130 val start = text_area.getScreenLineStartOffset(line) if start >= 0 |
130 { |
131 val end = text_area.getScreenLineEndOffset(line) if end >= 0 |
131 exit_popup() |
132 val range = proper_line_range(start, end) |
132 val offset = text_area.xyToOffset(x, y) |
133 val line_cmds = snapshot.node.command_range(snapshot.revert(range)).map(_._1) |
133 val p = new Point(x, y); SwingUtilities.convertPointToScreen(p, text_area.getPainter) |
134 if line_cmds.exists(changed) |
134 |
135 } text_area.invalidateScreenLineRange(line, line) |
135 // FIXME snapshot.cumulate |
136 |
136 snapshot.select_markup(Text.Range(offset, offset + 1))(Isabelle_Markup.popup) match { |
137 // FIXME danger of deadlock!? |
137 case Text.Info(_, Some(msg)) #:: _ => |
138 // FIXME potentially slow!? |
138 val popup = PopupFactory.getSharedInstance().getPopup(text_area, html_panel, p.x, p.y + 60) |
139 model.buffer.propertiesChanged() |
139 html_panel.render_sync(List(msg)) |
140 } |
140 popup.show |
141 } |
141 html_popup = Some(popup) |
142 |
142 case _ => |
143 case bad => System.err.println("command_change_actor: ignoring bad message " + bad) |
|
144 } |
|
145 } |
143 } |
146 } |
144 } |
147 |
145 |
148 |
146 |
149 /* subexpression highlighting */ |
147 /* subexpression highlighting */ |
158 } |
156 } |
159 } |
157 } |
160 |
158 |
161 private var highlight_range: Option[(Text.Range, Color)] = None |
159 private var highlight_range: Option[(Text.Range, Color)] = None |
162 |
160 |
|
161 |
|
162 /* CONTROL-mouse management */ |
|
163 |
|
164 private def exit_control() |
|
165 { |
|
166 exit_popup() |
|
167 highlight_range = None |
|
168 } |
|
169 |
163 private val focus_listener = new FocusAdapter { |
170 private val focus_listener = new FocusAdapter { |
164 override def focusLost(e: FocusEvent) { highlight_range = None } |
171 override def focusLost(e: FocusEvent) { exit_control() } |
165 } |
172 } |
166 |
173 |
167 private val mouse_motion_listener = new MouseMotionAdapter { |
174 private val mouse_motion_listener = new MouseMotionAdapter { |
168 override def mouseMoved(e: MouseEvent) { |
175 override def mouseMoved(e: MouseEvent) { |
169 val control = if (OperatingSystem.isMacOS()) e.isMetaDown else e.isControlDown |
176 val control = if (OperatingSystem.isMacOS()) e.isMetaDown else e.isControlDown |
170 if (!model.buffer.isLoaded) highlight_range = None |
177 val x = e.getX() |
|
178 val y = e.getY() |
|
179 |
|
180 if (!model.buffer.isLoaded) exit_control() |
171 else |
181 else |
172 Isabelle.swing_buffer_lock(model.buffer) { |
182 Isabelle.swing_buffer_lock(model.buffer) { |
|
183 val snapshot = model.snapshot |
|
184 |
|
185 if (control) init_popup(snapshot, x, y) |
|
186 |
173 highlight_range map { case (range, _) => invalidate_line_range(range) } |
187 highlight_range map { case (range, _) => invalidate_line_range(range) } |
174 highlight_range = |
188 highlight_range = if (control) subexp_range(snapshot, x, y) else None |
175 if (control) subexp_range(model.snapshot(), e.getX(), e.getY()) else None |
|
176 highlight_range map { case (range, _) => invalidate_line_range(range) } |
189 highlight_range map { case (range, _) => invalidate_line_range(range) } |
177 } |
190 } |
178 } |
191 } |
179 } |
192 } |
180 |
193 |
294 if (physical_lines(i) != -1) { |
307 if (physical_lines(i) != -1) { |
295 val line_range = proper_line_range(start(i), end(i)) |
308 val line_range = proper_line_range(start(i), end(i)) |
296 |
309 |
297 // gutter icons |
310 // gutter icons |
298 val icons = |
311 val icons = |
299 (for (Text.Info(_, Some(icon)) <- |
312 (for (Text.Info(_, Some(icon)) <- // FIXME snapshot.cumulate |
300 snapshot.select_markup(line_range)(Isabelle_Markup.gutter_message).iterator) |
313 snapshot.select_markup(line_range)(Isabelle_Markup.gutter_message).iterator) |
301 yield icon).toList.sortWith(_ >= _) |
314 yield icon).toList.sortWith(_ >= _) |
302 icons match { |
315 icons match { |
303 case icon :: _ => |
316 case icon :: _ => |
304 val icn = icon.icon |
317 val icn = icon.icon |
398 private def y_to_line(y: Int): Int = |
411 private def y_to_line(y: Int): Int = |
399 (y * (text_area.getBuffer.getLineCount max text_area.getVisibleLines)) / getHeight |
412 (y * (text_area.getBuffer.getLineCount max text_area.getVisibleLines)) / getHeight |
400 } |
413 } |
401 |
414 |
402 |
415 |
|
416 /* main actor */ |
|
417 |
|
418 private val main_actor = actor { |
|
419 loop { |
|
420 react { |
|
421 case Session.Commands_Changed(changed) => |
|
422 val buffer = model.buffer |
|
423 Isabelle.swing_buffer_lock(buffer) { |
|
424 val snapshot = model.snapshot() |
|
425 |
|
426 if (changed.exists(snapshot.node.commands.contains)) |
|
427 overview.repaint() |
|
428 |
|
429 val visible_range = screen_lines_range() |
|
430 val visible_cmds = snapshot.node.command_range(snapshot.revert(visible_range)).map(_._1) |
|
431 if (visible_cmds.exists(changed)) { |
|
432 for { |
|
433 line <- 0 until text_area.getVisibleLines |
|
434 val start = text_area.getScreenLineStartOffset(line) if start >= 0 |
|
435 val end = text_area.getScreenLineEndOffset(line) if end >= 0 |
|
436 val range = proper_line_range(start, end) |
|
437 val line_cmds = snapshot.node.command_range(snapshot.revert(range)).map(_._1) |
|
438 if line_cmds.exists(changed) |
|
439 } text_area.invalidateScreenLineRange(line, line) |
|
440 |
|
441 // FIXME danger of deadlock!? |
|
442 // FIXME potentially slow!? |
|
443 model.buffer.propertiesChanged() |
|
444 } |
|
445 } |
|
446 |
|
447 case Session.Global_Settings => html_panel_resize() |
|
448 |
|
449 case bad => System.err.println("command_change_actor: ignoring bad message " + bad) |
|
450 } |
|
451 } |
|
452 } |
|
453 |
|
454 |
403 /* activation */ |
455 /* activation */ |
404 |
456 |
405 private def activate() |
457 private def activate() |
406 { |
458 { |
407 text_area.getPainter. |
459 text_area.getPainter. |
409 text_area.getGutter.addExtension(gutter_extension) |
461 text_area.getGutter.addExtension(gutter_extension) |
410 text_area.addFocusListener(focus_listener) |
462 text_area.addFocusListener(focus_listener) |
411 text_area.getPainter.addMouseMotionListener(mouse_motion_listener) |
463 text_area.getPainter.addMouseMotionListener(mouse_motion_listener) |
412 text_area.addCaretListener(caret_listener) |
464 text_area.addCaretListener(caret_listener) |
413 text_area.addLeftOfScrollBar(overview) |
465 text_area.addLeftOfScrollBar(overview) |
414 session.commands_changed += commands_changed_actor |
466 session.commands_changed += main_actor |
|
467 session.global_settings += main_actor |
415 } |
468 } |
416 |
469 |
417 private def deactivate() |
470 private def deactivate() |
418 { |
471 { |
419 session.commands_changed -= commands_changed_actor |
472 session.commands_changed -= main_actor |
|
473 session.global_settings -= main_actor |
420 text_area.removeFocusListener(focus_listener) |
474 text_area.removeFocusListener(focus_listener) |
421 text_area.getPainter.removeMouseMotionListener(mouse_motion_listener) |
475 text_area.getPainter.removeMouseMotionListener(mouse_motion_listener) |
422 text_area.removeCaretListener(caret_listener) |
476 text_area.removeCaretListener(caret_listener) |
423 text_area.removeLeftOfScrollBar(overview) |
477 text_area.removeLeftOfScrollBar(overview) |
424 text_area.getGutter.removeExtension(gutter_extension) |
478 text_area.getGutter.removeExtension(gutter_extension) |
425 text_area.getPainter.removeExtension(text_area_extension) |
479 text_area.getPainter.removeExtension(text_area_extension) |
|
480 exit_popup() |
426 } |
481 } |
427 } |
482 } |