src/Tools/jEdit/src/graphview_dockable.scala
changeset 50452 bfb5964e3041
parent 50446 8dc05db0bf69
child 50501 6f41f1646617
equal deleted inserted replaced
50451:2af559170d07 50452:bfb5964e3041
     1 /*  Title:      Tools/jEdit/src/graphview_dockable.scala
     1 /*  Title:      Tools/jEdit/src/graphview_dockable.scala
     2     Author:     Markus Kaiser, TU Muenchen
       
     3     Author:     Makarius
     2     Author:     Makarius
     4 
     3 
     5 Dockable window for graphview.
     4 Stateless dockable window for graphview.
     6 */
     5 */
     7 
     6 
     8 package isabelle.jedit
     7 package isabelle.jedit
     9 
     8 
    10 
     9 
    11 import isabelle._
    10 import isabelle._
    12 
    11 
    13 import java.awt.BorderLayout
    12 import javax.swing.JComponent
    14 import javax.swing.{JPanel, JComponent}
    13 import java.awt.event.{WindowFocusListener, WindowEvent}
    15 
       
    16 import scala.actors.Actor._
       
    17 
    14 
    18 import org.gjt.sp.jedit.View
    15 import org.gjt.sp.jedit.View
       
    16 
       
    17 import scala.swing.TextArea
       
    18 
       
    19 
       
    20 object Graphview_Dockable
       
    21 {
       
    22   /* implicit arguments -- owned by Swing thread */
       
    23 
       
    24   private var implicit_snapshot = Document.State.init.snapshot()
       
    25 
       
    26   private val no_graph: Exn.Result[graphview.Model.Graph] = Exn.Exn(ERROR("No graph"))
       
    27   private var implicit_graph = no_graph
       
    28 
       
    29   private def set_implicit(snapshot: Document.Snapshot, graph: Exn.Result[graphview.Model.Graph])
       
    30   {
       
    31     Swing_Thread.require()
       
    32 
       
    33     implicit_snapshot = snapshot
       
    34     implicit_graph = graph
       
    35   }
       
    36 
       
    37   private def reset_implicit(): Unit =
       
    38     set_implicit(Document.State.init.snapshot(), no_graph)
       
    39 
       
    40   def apply(view: View, snapshot: Document.Snapshot, graph: Exn.Result[graphview.Model.Graph])
       
    41   {
       
    42     set_implicit(snapshot, graph)
       
    43     view.getDockableWindowManager.floatDockableWindow("isabelle-graphview")
       
    44   }
       
    45 }
    19 
    46 
    20 
    47 
    21 class Graphview_Dockable(view: View, position: String) extends Dockable(view, position)
    48 class Graphview_Dockable(view: View, position: String) extends Dockable(view, position)
    22 {
    49 {
    23   Swing_Thread.require()
    50   Swing_Thread.require()
    24 
    51 
       
    52   private val snapshot = Graphview_Dockable.implicit_snapshot
       
    53   private val graph = Graphview_Dockable.implicit_graph
    25 
    54 
    26   /* component state -- owned by Swing thread */
    55   private val window_focus_listener =
       
    56     new WindowFocusListener {
       
    57       def windowGainedFocus(e: WindowEvent) { Graphview_Dockable.set_implicit(snapshot, graph) }
       
    58       def windowLostFocus(e: WindowEvent) { Graphview_Dockable.reset_implicit() }
       
    59     }
    27 
    60 
    28   private val do_update = true  // FIXME
    61   val graphview =
    29 
    62     graph match {
    30   private var current_snapshot = Document.State.init.snapshot()
    63       case Exn.Res(proper_graph) =>
    31   private var current_state = Command.empty.init_state
    64         new isabelle.graphview.Main_Panel(proper_graph) {
    32   private var current_graph: XML.Body = Nil
    65           override def make_tooltip(parent: JComponent, x: Int, y: Int, body: XML.Body): String =
    33 
    66           {
    34 
    67             val rendering = Rendering(snapshot, PIDE.options.value)
    35   /* GUI components */
    68             new Pretty_Tooltip(view, parent, rendering, x, y, body)
    36 
    69             null
    37   private val graphview = new JPanel
    70           }
    38 
       
    39   // FIXME mutable GUI content
       
    40   private def set_graphview(snapshot: Document.Snapshot, graph_xml: XML.Body)
       
    41   {
       
    42     val graph = isabelle.graphview.Model.decode_graph(graph_xml).transitive_reduction_acyclic
       
    43 
       
    44     graphview.removeAll()
       
    45     graphview.setLayout(new BorderLayout)
       
    46     val panel =
       
    47       new isabelle.graphview.Main_Panel(graph) {
       
    48         override def make_tooltip(parent: JComponent, x: Int, y: Int, body: XML.Body): String =
       
    49         {
       
    50           val rendering = Rendering(snapshot, PIDE.options.value)
       
    51           new Pretty_Tooltip(view, parent, rendering, x, y, body)
       
    52           null
       
    53         }
    71         }
    54       }
    72       case Exn.Exn(exn) => new TextArea(Exn.message(exn))
    55     graphview.add(panel.peer, BorderLayout.CENTER)
    73     }
    56     graphview.revalidate()
       
    57   }
       
    58 
       
    59   set_graphview(current_snapshot, current_graph)
       
    60   set_content(graphview)
    74   set_content(graphview)
    61 
       
    62 
       
    63   private def handle_update(follow: Boolean, restriction: Option[Set[Command]])
       
    64   {
       
    65     Swing_Thread.require()
       
    66 
       
    67     val (new_snapshot, new_state) =
       
    68       Document_View(view.getTextArea) match {
       
    69         case Some(doc_view) =>
       
    70           val snapshot = doc_view.model.snapshot()
       
    71           if (follow && !snapshot.is_outdated) {
       
    72             snapshot.node.command_at(doc_view.text_area.getCaretPosition).map(_._1) match {
       
    73               case Some(cmd) =>
       
    74                 (snapshot, snapshot.state.command_state(snapshot.version, cmd))
       
    75               case None =>
       
    76                 (Document.State.init.snapshot(), Command.empty.init_state)
       
    77             }
       
    78           }
       
    79           else (current_snapshot, current_state)
       
    80         case None => (current_snapshot, current_state)
       
    81       }
       
    82 
       
    83     val new_graph =
       
    84       if (!restriction.isDefined || restriction.get.contains(new_state.command)) {
       
    85         new_state.markup.cumulate[Option[XML.Body]](
       
    86           new_state.command.range, None, Some(Set(Markup.GRAPHVIEW)),
       
    87         {
       
    88           case (_, Text.Info(_, XML.Elem(Markup(Markup.GRAPHVIEW, _), graph))) =>
       
    89             Some(graph)
       
    90         }).filter(_.info.isDefined) match {
       
    91           case Text.Info(_, Some(graph)) #:: _ => graph
       
    92           case _ => Nil
       
    93         }
       
    94       }
       
    95       else current_graph
       
    96 
       
    97     if (new_graph != current_graph) set_graphview(new_snapshot, new_graph)
       
    98 
       
    99     current_snapshot = new_snapshot
       
   100     current_state = new_state
       
   101     current_graph = new_graph
       
   102   }
       
   103 
       
   104 
       
   105   /* main actor */
       
   106 
       
   107   private val main_actor = actor {
       
   108     loop {
       
   109       react {
       
   110         case _: Session.Global_Options =>  // FIXME
       
   111 
       
   112         case changed: Session.Commands_Changed =>
       
   113           Swing_Thread.later { handle_update(do_update, Some(changed.commands)) }
       
   114 
       
   115         case Session.Caret_Focus =>
       
   116           Swing_Thread.later { handle_update(do_update, None) }
       
   117 
       
   118         case bad =>
       
   119           java.lang.System.err.println("Graphview_Dockable: ignoring bad message " + bad)
       
   120       }
       
   121     }
       
   122   }
       
   123 
    75 
   124   override def init()
    76   override def init()
   125   {
    77   {
   126     Swing_Thread.require()
    78     Swing_Thread.require()
   127 
    79     JEdit_Lib.parent_window(this).map(_.addWindowFocusListener(window_focus_listener))
   128     PIDE.session.global_options += main_actor
       
   129     PIDE.session.commands_changed += main_actor
       
   130     PIDE.session.caret_focus += main_actor
       
   131     handle_update(do_update, None)
       
   132   }
    80   }
   133 
    81 
   134   override def exit()
    82   override def exit()
   135   {
    83   {
   136     Swing_Thread.require()
    84     Swing_Thread.require()
   137 
    85     JEdit_Lib.parent_window(this).map(_.removeWindowFocusListener(window_focus_listener))
   138     PIDE.session.global_options -= main_actor
       
   139     PIDE.session.commands_changed -= main_actor
       
   140     PIDE.session.caret_focus -= main_actor
       
   141   }
    86   }
   142 }
    87 }