| author | wenzelm |
| Wed, 04 Sep 2013 17:35:47 +0200 | |
| changeset 53405 | ed2b48af04d9 |
| parent 53177 | dcac8d837b9c |
| child 53711 | 8ce7795256e1 |
| permissions | -rw-r--r-- |
| 51533 | 1 |
/* Title: Tools/jEdit/src/timing_dockable.scala |
2 |
Author: Makarius |
|
3 |
||
4 |
Dockable window for timing information. |
|
5 |
*/ |
|
6 |
||
7 |
package isabelle.jedit |
|
8 |
||
9 |
||
10 |
import isabelle._ |
|
11 |
||
12 |
import scala.actors.Actor._ |
|
13 |
import scala.swing.{FlowPanel, Label, ListView, Alignment, ScrollPane, Component, TextField}
|
|
| 51538 | 14 |
import scala.swing.event.{MouseClicked, ValueChanged}
|
| 51533 | 15 |
|
16 |
import java.lang.System |
|
| 51536 | 17 |
import java.awt.{BorderLayout, Graphics2D, Insets, Color}
|
| 51533 | 18 |
import javax.swing.{JList, BorderFactory}
|
19 |
import javax.swing.border.{BevelBorder, SoftBevelBorder}
|
|
20 |
||
21 |
import org.gjt.sp.jedit.{View, jEdit}
|
|
22 |
||
23 |
||
24 |
class Timing_Dockable(view: View, position: String) extends Dockable(view, position) |
|
25 |
{
|
|
26 |
/* entry */ |
|
27 |
||
28 |
private object Entry |
|
29 |
{
|
|
30 |
object Ordering extends scala.math.Ordering[Entry] |
|
31 |
{
|
|
32 |
def compare(entry1: Entry, entry2: Entry): Int = |
|
33 |
entry2.timing compare entry1.timing |
|
34 |
} |
|
35 |
||
36 |
object Renderer_Component extends Label |
|
37 |
{
|
|
38 |
opaque = false |
|
39 |
xAlignment = Alignment.Leading |
|
40 |
border = BorderFactory.createEmptyBorder(2, 2, 2, 2) |
|
| 51536 | 41 |
|
42 |
var entry: Entry = null |
|
43 |
override def paintComponent(gfx: Graphics2D) |
|
44 |
{
|
|
45 |
def paint_rectangle(color: Color) |
|
46 |
{
|
|
47 |
val size = peer.getSize() |
|
48 |
val insets = border.getBorderInsets(peer) |
|
49 |
val x = insets.left |
|
50 |
val y = insets.top |
|
51 |
val w = size.width - x - insets.right |
|
52 |
val h = size.height - y - insets.bottom |
|
53 |
gfx.setColor(color) |
|
54 |
gfx.fillRect(x, y, w, h) |
|
55 |
} |
|
56 |
||
57 |
entry match {
|
|
58 |
case theory_entry: Theory_Entry if theory_entry.current => |
|
59 |
paint_rectangle(view.getTextArea.getPainter.getSelectionColor) |
|
60 |
case _: Command_Entry => |
|
61 |
paint_rectangle(view.getTextArea.getPainter.getMultipleSelectionColor) |
|
62 |
case _ => |
|
63 |
} |
|
64 |
super.paintComponent(gfx) |
|
65 |
} |
|
| 51533 | 66 |
} |
67 |
||
68 |
class Renderer extends ListView.Renderer[Entry] |
|
69 |
{
|
|
70 |
def componentFor(list: ListView[_], isSelected: Boolean, focused: Boolean, |
|
71 |
entry: Entry, index: Int): Component = |
|
72 |
{
|
|
73 |
val component = Renderer_Component |
|
| 51536 | 74 |
component.entry = entry |
| 51534 | 75 |
component.text = entry.print |
| 51533 | 76 |
component |
77 |
} |
|
78 |
} |
|
79 |
} |
|
80 |
||
| 51534 | 81 |
private abstract class Entry |
82 |
{
|
|
83 |
def timing: Double |
|
84 |
def print: String |
|
85 |
def follow(snapshot: Document.Snapshot) |
|
86 |
} |
|
87 |
||
| 51536 | 88 |
private case class Theory_Entry(name: Document.Node.Name, timing: Double, current: Boolean) |
89 |
extends Entry |
|
| 51533 | 90 |
{
|
| 51534 | 91 |
def print: String = Time.print_seconds(timing) + "s theory " + quote(name.theory) |
| 52980 | 92 |
def follow(snapshot: Document.Snapshot) { PIDE.editor.goto(view, name.node) }
|
| 51534 | 93 |
} |
94 |
||
95 |
private case class Command_Entry(command: Command, timing: Double) extends Entry |
|
96 |
{
|
|
97 |
def print: String = " " + Time.print_seconds(timing) + "s command " + quote(command.name) |
|
| 51533 | 98 |
def follow(snapshot: Document.Snapshot) |
| 52980 | 99 |
{ PIDE.editor.hyperlink_command(snapshot, command).foreach(_.follow(view)) }
|
| 51533 | 100 |
} |
101 |
||
102 |
||
103 |
/* timing view */ |
|
104 |
||
105 |
private val timing_view = new ListView(Nil: List[Entry]) {
|
|
106 |
listenTo(mouse.clicks) |
|
107 |
reactions += {
|
|
108 |
case MouseClicked(_, point, _, clicks, _) if clicks == 2 => |
|
109 |
val index = peer.locationToIndex(point) |
|
110 |
if (index >= 0) listData(index).follow(PIDE.session.snapshot()) |
|
111 |
} |
|
112 |
} |
|
113 |
timing_view.peer.setLayoutOrientation(JList.VERTICAL_WRAP) |
|
114 |
timing_view.peer.setVisibleRowCount(0) |
|
115 |
timing_view.selection.intervalMode = ListView.IntervalMode.Single |
|
116 |
timing_view.renderer = new Entry.Renderer |
|
117 |
||
118 |
set_content(new ScrollPane(timing_view)) |
|
119 |
||
120 |
||
121 |
/* timing threshold */ |
|
122 |
||
123 |
private var timing_threshold = PIDE.options.real("jedit_timing_threshold")
|
|
124 |
||
| 51549 | 125 |
private val threshold_tooltip = "Threshold for timing display (seconds)" |
126 |
||
127 |
private val threshold_label = new Label("Threshold: ") {
|
|
128 |
tooltip = threshold_tooltip |
|
129 |
} |
|
| 51533 | 130 |
|
131 |
private val threshold_value = new TextField(Time.print_seconds(timing_threshold)) {
|
|
132 |
reactions += {
|
|
| 51538 | 133 |
case _: ValueChanged => |
| 51533 | 134 |
text match {
|
135 |
case Properties.Value.Double(x) if x >= 0.0 => timing_threshold = x |
|
136 |
case _ => |
|
137 |
} |
|
138 |
handle_update() |
|
139 |
} |
|
| 51549 | 140 |
tooltip = threshold_tooltip |
| 51538 | 141 |
verifier = ((s: String) => |
142 |
s match { case Properties.Value.Double(x) => x >= 0.0 case _ => false })
|
|
| 51533 | 143 |
} |
144 |
||
145 |
private val controls = new FlowPanel(FlowPanel.Alignment.Right)(threshold_label, threshold_value) |
|
146 |
add(controls.peer, BorderLayout.NORTH) |
|
147 |
||
148 |
||
149 |
/* component state -- owned by Swing thread */ |
|
150 |
||
151 |
private var nodes_timing = Map.empty[Document.Node.Name, Protocol.Node_Timing] |
|
| 51534 | 152 |
|
153 |
private def make_entries(): List[Entry] = |
|
154 |
{
|
|
155 |
Swing_Thread.require() |
|
156 |
||
157 |
val name = |
|
158 |
Document_View(view.getTextArea) match {
|
|
159 |
case None => Document.Node.Name.empty |
|
| 52973 | 160 |
case Some(doc_view) => doc_view.model.node_name |
| 51534 | 161 |
} |
| 52888 | 162 |
val timing = nodes_timing.getOrElse(name, Protocol.empty_node_timing) |
| 51534 | 163 |
|
164 |
val theories = |
|
| 51536 | 165 |
(for ((node_name, node_timing) <- nodes_timing.toList if !node_timing.commands.isEmpty) |
166 |
yield Theory_Entry(node_name, node_timing.total, false)).sorted(Entry.Ordering) |
|
| 51534 | 167 |
val commands = |
168 |
(for ((command, command_timing) <- timing.commands.toList) |
|
169 |
yield Command_Entry(command, command_timing)).sorted(Entry.Ordering) |
|
170 |
||
| 51536 | 171 |
theories.flatMap(entry => |
172 |
if (entry.name == name) entry.copy(current = true) :: commands |
|
173 |
else List(entry)) |
|
| 51534 | 174 |
} |
| 51533 | 175 |
|
176 |
private def handle_update(restriction: Option[Set[Document.Node.Name]] = None) |
|
177 |
{
|
|
|
53177
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
178 |
Swing_Thread.require() |
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
179 |
|
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
180 |
val snapshot = PIDE.session.snapshot() |
| 51533 | 181 |
|
|
53177
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
182 |
val iterator = |
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
183 |
restriction match {
|
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
184 |
case Some(names) => names.iterator.map(name => (name, snapshot.version.nodes(name))) |
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
185 |
case None => snapshot.version.nodes.entries |
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
186 |
} |
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
187 |
val nodes_timing1 = |
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
188 |
(nodes_timing /: iterator)({ case (timing1, (name, node)) =>
|
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
189 |
if (PIDE.thy_load.loaded_theories(name.theory)) timing1 |
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
190 |
else {
|
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
191 |
val node_timing = |
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
192 |
Protocol.node_timing(snapshot.state, snapshot.version, node, timing_threshold) |
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
193 |
timing1 + (name -> node_timing) |
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
194 |
} |
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
195 |
}) |
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
196 |
nodes_timing = nodes_timing1 |
| 51533 | 197 |
|
|
53177
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
198 |
val entries = make_entries() |
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
199 |
if (timing_view.listData.toList != entries) timing_view.listData = entries |
| 51533 | 200 |
} |
201 |
||
202 |
||
203 |
/* main actor */ |
|
204 |
||
205 |
private val main_actor = actor {
|
|
206 |
loop {
|
|
207 |
react {
|
|
|
53177
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
208 |
case changed: Session.Commands_Changed => |
|
dcac8d837b9c
more uniform treatment of Swing_Thread context switch: prefer asynchronous Swing_Thread.later from actor;
wenzelm
parents:
52980
diff
changeset
|
209 |
Swing_Thread.later { handle_update(Some(changed.nodes)) }
|
| 51533 | 210 |
|
211 |
case bad => System.err.println("Timing_Dockable: ignoring bad message " + bad)
|
|
212 |
} |
|
213 |
} |
|
214 |
} |
|
215 |
||
216 |
override def init() |
|
217 |
{
|
|
218 |
PIDE.session.commands_changed += main_actor |
|
219 |
handle_update() |
|
220 |
} |
|
221 |
||
222 |
override def exit() |
|
223 |
{
|
|
224 |
PIDE.session.commands_changed -= main_actor |
|
225 |
} |
|
226 |
} |