src/Tools/jEdit/src/jedit/document_view.scala
changeset 39740 0294948ba962
parent 39737 8c312f064223
child 39741 62b91eb2d39a
equal deleted inserted replaced
39739:c7f0fa0593f0 39740:0294948ba962
    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 }