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