src/Tools/jEdit/src/theories_dockable.scala
author wenzelm
Mon Jul 29 12:50:16 2013 +0200 (2013-07-29)
changeset 52759 a20631db9c8a
parent 51535 f2f480bc4223
child 52763 3b5f4f2ff108
permissions -rw-r--r--
support declarative editor_execution_range, instead of old-style check/cancel buttons;
     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): RadioButton =
    60       new RadioButton(range.toString) {
    61         reactions += { case ButtonClicked(_) => PIDE.update_execution_range(range) }
    62       }
    63     private val label = new Label("Range:") { tooltip = "range of continuous document processing" }
    64     private val b1 = button(PIDE.Execution_Range.ALL)
    65     private val b2 = button(PIDE.Execution_Range.NONE)
    66     private val b3 = button(PIDE.Execution_Range.VISIBLE)
    67     private val group = new ButtonGroup(b1, b2, b3)
    68     contents ++= List(label, b1, b2, b3)
    69     border = new LineBorder(Color.GRAY)
    70 
    71     def load()
    72     {
    73       PIDE.execution_range() match {
    74         case PIDE.Execution_Range.ALL => group.select(b1)
    75         case PIDE.Execution_Range.NONE => group.select(b2)
    76         case PIDE.Execution_Range.VISIBLE => group.select(b3)
    77       }
    78     }
    79   }
    80 
    81   private val logic = Isabelle_Logic.logic_selector(true)
    82 
    83   private val controls =
    84     new FlowPanel(FlowPanel.Alignment.Right)(execution_range, session_phase, logic)
    85   add(controls.peer, BorderLayout.NORTH)
    86 
    87 
    88   /* component state -- owned by Swing thread */
    89 
    90   private var nodes_status: Map[Document.Node.Name, Protocol.Node_Status] = Map.empty
    91 
    92   private object Node_Renderer_Component extends Label
    93   {
    94     opaque = false
    95     xAlignment = Alignment.Leading
    96     border = BorderFactory.createEmptyBorder(2, 2, 2, 2)
    97 
    98     var node_name = Document.Node.Name.empty
    99     override def paintComponent(gfx: Graphics2D)
   100     {
   101       nodes_status.get(node_name) match {
   102         case Some(st) if st.total > 0 =>
   103           val colors = List(
   104             (st.unprocessed, PIDE.options.color_value("unprocessed1_color")),
   105             (st.running, PIDE.options.color_value("running_color")),
   106             (st.warned, PIDE.options.color_value("warning_color")),
   107             (st.failed, PIDE.options.color_value("error_color")))
   108 
   109           val size = peer.getSize()
   110           val insets = border.getBorderInsets(peer)
   111           val w = size.width - insets.left - insets.right
   112           val h = size.height - insets.top - insets.bottom
   113           var end = size.width - insets.right
   114 
   115           for { (n, color) <- colors }
   116           {
   117             gfx.setColor(color)
   118             val v = (n * (w - colors.length) / st.total) max (if (n > 0) 4 else 0)
   119             gfx.fillRect(end - v, insets.top, v, h)
   120             end = end - v - 1
   121           }
   122         case _ =>
   123       }
   124       super.paintComponent(gfx)
   125     }
   126   }
   127 
   128   private class Node_Renderer extends ListView.Renderer[Document.Node.Name]
   129   {
   130     def componentFor(list: ListView[_], isSelected: Boolean, focused: Boolean,
   131       name: Document.Node.Name, index: Int): Component =
   132     {
   133       val component = Node_Renderer_Component
   134       component.node_name = name
   135       component.text = name.theory
   136       component
   137     }
   138   }
   139   status.renderer = new Node_Renderer
   140 
   141   private def handle_update(restriction: Option[Set[Document.Node.Name]] = None)
   142   {
   143     Swing_Thread.now {
   144       val snapshot = PIDE.session.snapshot()
   145 
   146       val iterator =
   147         restriction match {
   148           case Some(names) => names.iterator.map(name => (name, snapshot.version.nodes(name)))
   149           case None => snapshot.version.nodes.entries
   150         }
   151       val nodes_status1 =
   152         (nodes_status /: iterator)({ case (status, (name, node)) =>
   153             if (PIDE.thy_load.loaded_theories(name.theory)) status
   154             else status + (name -> Protocol.node_status(snapshot.state, snapshot.version, node)) })
   155 
   156       if (nodes_status != nodes_status1) {
   157         nodes_status = nodes_status1
   158         status.listData =
   159           snapshot.version.nodes.topological_order.filter(
   160             (name: Document.Node.Name) => nodes_status.isDefinedAt(name))
   161       }
   162     }
   163   }
   164 
   165 
   166   /* main actor */
   167 
   168   private val main_actor = actor {
   169     loop {
   170       react {
   171         case phase: Session.Phase => handle_phase(phase)
   172 
   173         case _: Session.Global_Options =>
   174           Swing_Thread.later {
   175             execution_range.load()
   176             logic.load ()
   177           }
   178 
   179         case changed: Session.Commands_Changed => handle_update(Some(changed.nodes))
   180 
   181         case bad => System.err.println("Theories_Dockable: ignoring bad message " + bad)
   182       }
   183     }
   184   }
   185 
   186   override def init()
   187   {
   188     PIDE.session.phase_changed += main_actor; handle_phase(PIDE.session.phase)
   189     PIDE.session.global_options += main_actor
   190     PIDE.session.commands_changed += main_actor; handle_update()
   191   }
   192 
   193   override def exit()
   194   {
   195     PIDE.session.phase_changed -= main_actor
   196     PIDE.session.global_options -= main_actor
   197     PIDE.session.commands_changed -= main_actor
   198   }
   199 }