src/Tools/jEdit/src/theories_dockable.scala
author wenzelm
Wed Jul 31 21:13:05 2013 +0200 (2013-07-31)
changeset 52816 c608e0ade554
parent 52815 eaad5fe7bb1b
child 52817 408fb2e563df
permissions -rw-r--r--
home-grown mouse handling to pretend that the painted checkbox is actually a Swing component;
tuned signature;
     1 /*  Title:      Tools/jEdit/src/theories_dockable.scala
     2     Author:     Makarius
     3 
     4 Dockable window for theories managed by prover.
     5 */
     6 
     7 package isabelle.jedit
     8 
     9 
    10 import isabelle._
    11 
    12 import scala.actors.Actor._
    13 import scala.swing.{FlowPanel, Button, TextArea, Label, ListView, Alignment,
    14   ScrollPane, Component, CheckBox, BorderPanel}
    15 import scala.swing.event.{ButtonClicked, MouseClicked}
    16 
    17 import java.lang.System
    18 import java.awt.{BorderLayout, Graphics2D, Insets, Point, Dimension}
    19 import javax.swing.{JList, BorderFactory}
    20 import javax.swing.border.{BevelBorder, SoftBevelBorder}
    21 
    22 import org.gjt.sp.jedit.{View, jEdit}
    23 
    24 
    25 class Theories_Dockable(view: View, position: String) extends Dockable(view, position)
    26 {
    27   /* status */
    28 
    29   private val status = new ListView(Nil: List[Document.Node.Name]) {
    30     listenTo(mouse.clicks)
    31     reactions += {
    32       case MouseClicked(_, point, _, clicks, _) =>
    33         val index = peer.locationToIndex(point)
    34         if (index >= 0 && Node_Renderer_Component != null) {
    35           Node_Renderer_Component.checkbox_geometry match {
    36             case Some((loc, size)) =>
    37               val loc0 = peer.indexToLocation(index)
    38               val in_checkbox =
    39                 loc0.x + loc.x <= point.x && point.x < loc0.x + size.width &&
    40                 loc0.y + loc.y <= point.y && point.y < loc0.y + size.height
    41 
    42               if (clicks == 1 && in_checkbox) {
    43                 for {
    44                   buffer <- JEdit_Lib.jedit_buffer(listData(index).node)
    45                   model <- PIDE.document_model(buffer)
    46                 } model.node_required = !model.node_required
    47               }
    48 
    49               if (clicks == 2 && !in_checkbox)
    50                 Hyperlink(listData(index).node).follow(view)
    51 
    52             case None =>
    53           }
    54         }
    55     }
    56   }
    57   status.peer.setLayoutOrientation(JList.HORIZONTAL_WRAP)
    58   status.peer.setVisibleRowCount(0)
    59   status.selection.intervalMode = ListView.IntervalMode.Single
    60 
    61   set_content(new ScrollPane(status))
    62 
    63 
    64   /* controls */
    65 
    66   def phase_text(phase: Session.Phase): String =
    67     "Prover: " + Library.lowercase(phase.toString)
    68 
    69   private val session_phase = new Label(phase_text(PIDE.session.phase))
    70   session_phase.border = new SoftBevelBorder(BevelBorder.LOWERED)
    71   session_phase.tooltip = "Status of prover session"
    72 
    73   private def handle_phase(phase: Session.Phase)
    74   {
    75     Swing_Thread.later { session_phase.text = " " + phase_text(phase) + " " }
    76   }
    77 
    78   private val continuous_checking = new CheckBox("Continuous checking") {
    79     tooltip = "Continuous checking of proof document (visible and required parts)"
    80     reactions += { case ButtonClicked(_) => Isabelle.continuous_checking = selected }
    81     def load() { selected = Isabelle.continuous_checking }
    82     load()
    83   }
    84 
    85   private val logic = Isabelle_Logic.logic_selector(true)
    86 
    87   private val controls =
    88     new FlowPanel(FlowPanel.Alignment.Right)(continuous_checking, session_phase, logic)
    89   add(controls.peer, BorderLayout.NORTH)
    90 
    91 
    92   /* component state -- owned by Swing thread */
    93 
    94   private var nodes_status: Map[Document.Node.Name, Protocol.Node_Status] = Map.empty
    95   private var nodes_required: Set[Document.Node.Name] = Set.empty
    96 
    97   private def update_nodes_required()
    98   {
    99     nodes_required = Set.empty
   100     for {
   101       buffer <- JEdit_Lib.jedit_buffers
   102       model <- PIDE.document_model(buffer)
   103       if model.node_required
   104     } nodes_required += model.name
   105   }
   106 
   107   private object Node_Renderer_Component extends BorderPanel
   108   {
   109     opaque = false
   110     border = BorderFactory.createEmptyBorder(2, 2, 2, 2)
   111 
   112     var node_name = Document.Node.Name.empty
   113 
   114     var checkbox_geometry: Option[(Point, Dimension)] = None
   115     val checkbox = new CheckBox {
   116       override def paintComponent(gfx: Graphics2D)
   117       {
   118         super.paintComponent(gfx)
   119         if (location != null && size != null)
   120           checkbox_geometry = Some((location, size))
   121       }
   122     }
   123 
   124     val label = new Label {
   125       opaque = false
   126       xAlignment = Alignment.Leading
   127 
   128       override def paintComponent(gfx: Graphics2D)
   129       {
   130         val size = peer.getSize()
   131         val insets = border.getBorderInsets(peer)
   132         val w = size.width - insets.left - insets.right
   133         val h = size.height - insets.top - insets.bottom
   134 
   135         nodes_status.get(node_name) match {
   136           case Some(st) if st.total > 0 =>
   137             val colors = List(
   138               (st.unprocessed, PIDE.options.color_value("unprocessed1_color")),
   139               (st.running, PIDE.options.color_value("running_color")),
   140               (st.warned, PIDE.options.color_value("warning_color")),
   141               (st.failed, PIDE.options.color_value("error_color")))
   142 
   143             var end = size.width - insets.right
   144             for { (n, color) <- colors }
   145             {
   146               gfx.setColor(color)
   147               val v = (n * (w - colors.length) / st.total) max (if (n > 0) 4 else 0)
   148               gfx.fillRect(end - v, insets.top, v, h)
   149               end = end - v - 1
   150             }
   151           case _ =>
   152             gfx.setColor(PIDE.options.color_value("unprocessed1_color"))
   153             gfx.fillRect(insets.left, insets.top, w, h)
   154         }
   155         super.paintComponent(gfx)
   156       }
   157     }
   158 
   159     layout(checkbox) = BorderPanel.Position.West
   160     layout(label) = BorderPanel.Position.Center
   161   }
   162 
   163   private class Node_Renderer extends ListView.Renderer[Document.Node.Name]
   164   {
   165     def componentFor(list: ListView[_], isSelected: Boolean, focused: Boolean,
   166       name: Document.Node.Name, index: Int): Component =
   167     {
   168       val component = Node_Renderer_Component
   169       component.node_name = name
   170       component.checkbox.selected = nodes_required.contains(name)
   171       component.label.text = name.theory
   172       component
   173     }
   174   }
   175   status.renderer = new Node_Renderer
   176 
   177   private def handle_update(restriction: Option[Set[Document.Node.Name]] = None)
   178   {
   179     Swing_Thread.now {
   180       val snapshot = PIDE.session.snapshot()
   181 
   182       val iterator =
   183         restriction match {
   184           case Some(names) => names.iterator.map(name => (name, snapshot.version.nodes(name)))
   185           case None => snapshot.version.nodes.entries
   186         }
   187       val nodes_status1 =
   188         (nodes_status /: iterator)({ case (status, (name, node)) =>
   189             if (PIDE.thy_load.loaded_theories(name.theory)) status
   190             else status + (name -> Protocol.node_status(snapshot.state, snapshot.version, node)) })
   191 
   192       if (nodes_status != nodes_status1) {
   193         nodes_status = nodes_status1
   194         status.listData =
   195           snapshot.version.nodes.topological_order.filter(
   196             (name: Document.Node.Name) => nodes_status.isDefinedAt(name))
   197       }
   198     }
   199   }
   200 
   201 
   202   /* main actor */
   203 
   204   private val main_actor = actor {
   205     loop {
   206       react {
   207         case phase: Session.Phase => handle_phase(phase)
   208 
   209         case _: Session.Global_Options =>
   210           Swing_Thread.later {
   211             continuous_checking.load()
   212             logic.load ()
   213             update_nodes_required()
   214             status.repaint()
   215           }
   216 
   217         case changed: Session.Commands_Changed => handle_update(Some(changed.nodes))
   218 
   219         case bad => System.err.println("Theories_Dockable: ignoring bad message " + bad)
   220       }
   221     }
   222   }
   223 
   224   override def init()
   225   {
   226     PIDE.session.phase_changed += main_actor; handle_phase(PIDE.session.phase)
   227     PIDE.session.global_options += main_actor
   228     PIDE.session.commands_changed += main_actor; handle_update()
   229   }
   230 
   231   override def exit()
   232   {
   233     PIDE.session.phase_changed -= main_actor
   234     PIDE.session.global_options -= main_actor
   235     PIDE.session.commands_changed -= main_actor
   236   }
   237 }