src/Tools/jEdit/src/theories_dockable.scala
author wenzelm
Mon Jul 29 20:46:21 2013 +0200 (2013-07-29)
changeset 52779 82707f95a783
parent 52769 0827b6f5de44
child 52802 0b98561d0790
permissions -rw-r--r--
NEWS;
tuned description;
     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, ScrollPane, Component,
    14   BoxPanel, Orientation, RadioButton, ButtonGroup}
    15 import scala.swing.event.{ButtonClicked, MouseClicked}
    16 
    17 import java.lang.System
    18 import java.awt.{BorderLayout, Graphics2D, Insets, Color}
    19 import javax.swing.{JList, BorderFactory}
    20 import javax.swing.border.{BevelBorder, SoftBevelBorder, LineBorder}
    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, _) if clicks == 2 =>
    33         val index = peer.locationToIndex(point)
    34         if (index >= 0) Hyperlink(listData(index).node).follow(view)
    35     }
    36   }
    37   status.peer.setLayoutOrientation(JList.HORIZONTAL_WRAP)
    38   status.peer.setVisibleRowCount(0)
    39   status.selection.intervalMode = ListView.IntervalMode.Single
    40 
    41   set_content(new ScrollPane(status))
    42 
    43 
    44   /* controls */
    45 
    46   def phase_text(phase: Session.Phase): String =
    47     "Prover: " + Library.lowercase(phase.toString)
    48 
    49   private val session_phase = new Label(phase_text(PIDE.session.phase))
    50   session_phase.border = new SoftBevelBorder(BevelBorder.LOWERED)
    51   session_phase.tooltip = "Status of prover session"
    52 
    53   private def handle_phase(phase: Session.Phase)
    54   {
    55     Swing_Thread.later { session_phase.text = " " + phase_text(phase) + " " }
    56   }
    57 
    58   private val execution_range = new BoxPanel(Orientation.Horizontal) {
    59     private def button(range: PIDE.Execution_Range.Value, tip: String): RadioButton =
    60       new RadioButton(range.toString) {
    61         tooltip = tip
    62         reactions += { case ButtonClicked(_) => PIDE.update_execution_range(range) }
    63       }
    64     private val label =
    65       new Label("Range:") { tooltip = "Execution range of continuous document processing" }
    66     private val b1 = button(PIDE.Execution_Range.ALL, "Check all theories (potentially slow)")
    67     private val b2 = button(PIDE.Execution_Range.NONE, "Check nothing")
    68     private val b3 = button(PIDE.Execution_Range.VISIBLE, "Check visible parts of theories")
    69     private val group = new ButtonGroup(b1, b2, b3)
    70     contents ++= List(label, b1, b2, b3)
    71     border = new LineBorder(Color.GRAY)
    72 
    73     def load()
    74     {
    75       PIDE.execution_range() match {
    76         case PIDE.Execution_Range.ALL => group.select(b1)
    77         case PIDE.Execution_Range.NONE => group.select(b2)
    78         case PIDE.Execution_Range.VISIBLE => group.select(b3)
    79       }
    80     }
    81     load()
    82   }
    83 
    84   private val logic = Isabelle_Logic.logic_selector(true)
    85 
    86   private val controls =
    87     new FlowPanel(FlowPanel.Alignment.Right)(execution_range, session_phase, logic)
    88   add(controls.peer, BorderLayout.NORTH)
    89 
    90 
    91   /* component state -- owned by Swing thread */
    92 
    93   private var nodes_status: Map[Document.Node.Name, Protocol.Node_Status] = Map.empty
    94 
    95   private object Node_Renderer_Component extends Label
    96   {
    97     opaque = false
    98     xAlignment = Alignment.Leading
    99     border = BorderFactory.createEmptyBorder(2, 2, 2, 2)
   100 
   101     var node_name = Document.Node.Name.empty
   102     override def paintComponent(gfx: Graphics2D)
   103     {
   104       nodes_status.get(node_name) match {
   105         case Some(st) if st.total > 0 =>
   106           val colors = List(
   107             (st.unprocessed, PIDE.options.color_value("unprocessed1_color")),
   108             (st.running, PIDE.options.color_value("running_color")),
   109             (st.warned, PIDE.options.color_value("warning_color")),
   110             (st.failed, PIDE.options.color_value("error_color")))
   111 
   112           val size = peer.getSize()
   113           val insets = border.getBorderInsets(peer)
   114           val w = size.width - insets.left - insets.right
   115           val h = size.height - insets.top - insets.bottom
   116           var end = size.width - insets.right
   117 
   118           for { (n, color) <- colors }
   119           {
   120             gfx.setColor(color)
   121             val v = (n * (w - colors.length) / st.total) max (if (n > 0) 4 else 0)
   122             gfx.fillRect(end - v, insets.top, v, h)
   123             end = end - v - 1
   124           }
   125         case _ =>
   126       }
   127       super.paintComponent(gfx)
   128     }
   129   }
   130 
   131   private class Node_Renderer extends ListView.Renderer[Document.Node.Name]
   132   {
   133     def componentFor(list: ListView[_], isSelected: Boolean, focused: Boolean,
   134       name: Document.Node.Name, index: Int): Component =
   135     {
   136       val component = Node_Renderer_Component
   137       component.node_name = name
   138       component.text = name.theory
   139       component
   140     }
   141   }
   142   status.renderer = new Node_Renderer
   143 
   144   private def handle_update(restriction: Option[Set[Document.Node.Name]] = None)
   145   {
   146     Swing_Thread.now {
   147       val snapshot = PIDE.session.snapshot()
   148 
   149       val iterator =
   150         restriction match {
   151           case Some(names) => names.iterator.map(name => (name, snapshot.version.nodes(name)))
   152           case None => snapshot.version.nodes.entries
   153         }
   154       val nodes_status1 =
   155         (nodes_status /: iterator)({ case (status, (name, node)) =>
   156             if (PIDE.thy_load.loaded_theories(name.theory)) status
   157             else status + (name -> Protocol.node_status(snapshot.state, snapshot.version, node)) })
   158 
   159       if (nodes_status != nodes_status1) {
   160         nodes_status = nodes_status1
   161         status.listData =
   162           snapshot.version.nodes.topological_order.filter(
   163             (name: Document.Node.Name) => nodes_status.isDefinedAt(name))
   164       }
   165     }
   166   }
   167 
   168 
   169   /* main actor */
   170 
   171   private val main_actor = actor {
   172     loop {
   173       react {
   174         case phase: Session.Phase => handle_phase(phase)
   175 
   176         case _: Session.Global_Options =>
   177           Swing_Thread.later {
   178             execution_range.load()
   179             logic.load ()
   180           }
   181 
   182         case changed: Session.Commands_Changed => handle_update(Some(changed.nodes))
   183 
   184         case bad => System.err.println("Theories_Dockable: ignoring bad message " + bad)
   185       }
   186     }
   187   }
   188 
   189   override def init()
   190   {
   191     PIDE.session.phase_changed += main_actor; handle_phase(PIDE.session.phase)
   192     PIDE.session.global_options += main_actor
   193     PIDE.session.commands_changed += main_actor; handle_update()
   194   }
   195 
   196   override def exit()
   197   {
   198     PIDE.session.phase_changed -= main_actor
   199     PIDE.session.global_options -= main_actor
   200     PIDE.session.commands_changed -= main_actor
   201   }
   202 }