| author | wenzelm | 
| Wed, 30 Dec 2015 20:12:26 +0100 | |
| changeset 61992 | 6d02bb8b5fe1 | 
| parent 60905 | eba3acb72b55 | 
| child 66205 | e9fa94f43a15 | 
| permissions | -rw-r--r-- | 
| 59392 | 1 | /* Title: Tools/Graphview/tree_panel.scala | 
| 2 | Author: Makarius | |
| 3 | ||
| 4 | Tree view on graph nodes. | |
| 5 | */ | |
| 6 | ||
| 7 | package isabelle.graphview | |
| 8 | ||
| 9 | ||
| 10 | import isabelle._ | |
| 11 | ||
| 59396 | 12 | import java.awt.{Dimension, Rectangle}
 | 
| 13 | import java.awt.event.{KeyEvent, KeyAdapter, MouseEvent, MouseAdapter}
 | |
| 59392 | 14 | import javax.swing.JTree | 
| 59396 | 15 | import javax.swing.tree.{DefaultMutableTreeNode, TreeSelectionModel, TreePath}
 | 
| 16 | import javax.swing.event.{TreeSelectionEvent, TreeSelectionListener, DocumentListener, DocumentEvent}
 | |
| 59392 | 17 | |
| 18 | import scala.util.matching.Regex | |
| 59396 | 19 | import scala.swing.{Component, ScrollPane, BorderPanel, Label, TextField, Button, CheckBox, Action}
 | 
| 59392 | 20 | |
| 21 | ||
| 59459 | 22 | class Tree_Panel(val graphview: Graphview, graph_panel: Graph_Panel) extends BorderPanel | 
| 59392 | 23 | {
 | 
| 59396 | 24 | /* main actions */ | 
| 25 | ||
| 26 | private def selection_action() | |
| 27 |   {
 | |
| 28 |     if (tree != null) {
 | |
| 59459 | 29 | graphview.current_node = None | 
| 30 | graphview.Selection.clear() | |
| 59396 | 31 | val paths = tree.getSelectionPaths | 
| 32 |       if (paths != null) {
 | |
| 33 |         for (path <- paths if path != null) {
 | |
| 34 |           path.getLastPathComponent match {
 | |
| 35 | case tree_node: DefaultMutableTreeNode => | |
| 36 |               tree_node.getUserObject match {
 | |
| 59459 | 37 | case node: Graph_Display.Node => graphview.Selection.add(node) | 
| 59396 | 38 | case _ => | 
| 39 | } | |
| 40 | case _ => | |
| 41 | } | |
| 42 | } | |
| 43 | } | |
| 59397 | 44 | graph_panel.repaint() | 
| 59396 | 45 | } | 
| 46 | } | |
| 47 | ||
| 48 | private def point_action(path: TreePath) | |
| 49 |   {
 | |
| 50 |     if (tree_pane != null && path != null) {
 | |
| 51 | val action_node = | |
| 52 |         path.getLastPathComponent match {
 | |
| 53 | case tree_node: DefaultMutableTreeNode => | |
| 54 |             tree_node.getUserObject match {
 | |
| 55 | case node: Graph_Display.Node => Some(node) | |
| 56 | case _ => None | |
| 57 | } | |
| 58 | case _ => None | |
| 59 | } | |
| 59397 | 60 | action_node.foreach(graph_panel.scroll_to_node(_)) | 
| 59459 | 61 | graphview.current_node = action_node | 
| 59397 | 62 | graph_panel.repaint() | 
| 59396 | 63 | } | 
| 64 | } | |
| 65 | ||
| 66 | ||
| 59392 | 67 | /* tree */ | 
| 68 | ||
| 69 | private var nodes = List.empty[Graph_Display.Node] | |
| 70 |   private val root = new DefaultMutableTreeNode("Nodes")
 | |
| 71 | ||
| 59396 | 72 | val tree = new JTree(root) | 
| 60292 
ba3c716144dd
cell-specific row height based on its font, e.g. relevant for DPI scaling on Windows;
 wenzelm parents: 
59459diff
changeset | 73 | tree.setRowHeight(0) | 
| 59396 | 74 | |
| 75 |   tree.addKeyListener(new KeyAdapter {
 | |
| 76 | override def keyPressed(e: KeyEvent): Unit = | |
| 77 |       if (e.getKeyCode == KeyEvent.VK_ENTER) {
 | |
| 78 | e.consume | |
| 79 | selection_action() | |
| 80 | } | |
| 81 | }) | |
| 59392 | 82 |   tree.addMouseListener(new MouseAdapter {
 | 
| 59396 | 83 | override def mousePressed(e: MouseEvent): Unit = | 
| 84 | if (e.getClickCount == 2) | |
| 85 | point_action(tree.getPathForLocation(e.getX, e.getY)) | |
| 59392 | 86 | }) | 
| 87 | ||
| 88 | private val tree_pane = new ScrollPane(Component.wrap(tree)) | |
| 89 | tree_pane.horizontalScrollBarPolicy = ScrollPane.BarPolicy.Always | |
| 90 | tree_pane.verticalScrollBarPolicy = ScrollPane.BarPolicy.Always | |
| 60849 | 91 | tree_pane.minimumSize = new Dimension(200, 100) | 
| 59392 | 92 | tree_pane.peer.getVerticalScrollBar.setUnitIncrement(10) | 
| 93 | ||
| 94 | ||
| 95 | /* controls */ | |
| 96 | ||
| 97 | private var selection_pattern: Option[Regex] = None | |
| 98 | ||
| 99 | private def selection_filter(node: Graph_Display.Node): Boolean = | |
| 100 |     selection_pattern match {
 | |
| 59402 
4de91de47a6c
proper selection of nodes via regular expression;
 wenzelm parents: 
59400diff
changeset | 101 | case None => false | 
| 59392 | 102 | case Some(re) => re.pattern.matcher(node.toString).find | 
| 103 | } | |
| 104 | ||
| 105 |   private val selection_label = new Label("Selection:") {
 | |
| 106 | tooltip = "Selection of nodes via regular expression" | |
| 107 | } | |
| 108 | ||
| 109 |   private val selection_field = new TextField(10) {
 | |
| 110 | tooltip = selection_label.tooltip | |
| 111 | } | |
| 112 | private val selection_field_foreground = selection_field.foreground | |
| 113 | ||
| 114 | private val selection_delay = | |
| 59459 | 115 |     GUI_Thread.delay_last(graphview.options.seconds("editor_input_delay")) {
 | 
| 59392 | 116 | val (pattern, ok) = | 
| 117 |         selection_field.text match {
 | |
| 118 | case null | "" => (None, true) | |
| 119 | case s => | |
| 120 | val pattern = Library.make_regex(s) | |
| 121 | (pattern, pattern.isDefined) | |
| 122 | } | |
| 123 |       if (selection_pattern != pattern) {
 | |
| 124 | selection_pattern = pattern | |
| 59402 
4de91de47a6c
proper selection of nodes via regular expression;
 wenzelm parents: 
59400diff
changeset | 125 | tree.setSelectionRows( | 
| 
4de91de47a6c
proper selection of nodes via regular expression;
 wenzelm parents: 
59400diff
changeset | 126 |           (for { (node, i) <- nodes.iterator.zipWithIndex if selection_filter(node) }
 | 
| 
4de91de47a6c
proper selection of nodes via regular expression;
 wenzelm parents: 
59400diff
changeset | 127 | yield i + 1).toArray) | 
| 
4de91de47a6c
proper selection of nodes via regular expression;
 wenzelm parents: 
59400diff
changeset | 128 | tree.repaint() | 
| 59392 | 129 | } | 
| 130 | selection_field.foreground = | |
| 59459 | 131 | if (ok) selection_field_foreground else graphview.error_color | 
| 59392 | 132 | } | 
| 133 | ||
| 134 |   selection_field.peer.getDocument.addDocumentListener(new DocumentListener {
 | |
| 135 |     def changedUpdate(e: DocumentEvent) { selection_delay.invoke() }
 | |
| 136 |     def insertUpdate(e: DocumentEvent) { selection_delay.invoke() }
 | |
| 137 |     def removeUpdate(e: DocumentEvent) { selection_delay.invoke() }
 | |
| 138 | }) | |
| 139 | ||
| 140 |   private val selection_apply = new Button {
 | |
| 59406 
283aa6225d98
proper tooltips -- override action toolTip which is empty here;
 wenzelm parents: 
59404diff
changeset | 141 |     action = Action("<html><b>Apply</b></html>") { selection_action () }
 | 
| 59392 | 142 | tooltip = "Apply tree selection to graph" | 
| 143 | } | |
| 144 | ||
| 59409 | 145 | private val controls = | 
| 146 | new Wrap_Panel(Wrap_Panel.Alignment.Right)(selection_label, selection_field, selection_apply) | |
| 59392 | 147 | |
| 148 | ||
| 149 | /* main layout */ | |
| 150 | ||
| 151 | def refresh() | |
| 152 |   {
 | |
| 59459 | 153 | val new_nodes = graphview.visible_graph.topological_order | 
| 59392 | 154 |     if (new_nodes != nodes) {
 | 
| 60846 | 155 | tree.clearSelection | 
| 156 | ||
| 59392 | 157 | nodes = new_nodes | 
| 158 | ||
| 159 | root.removeAllChildren | |
| 160 | for (node <- nodes) root.add(new DefaultMutableTreeNode(node)) | |
| 161 | ||
| 60905 | 162 | tree.expandRow(0) | 
| 59392 | 163 | tree.revalidate() | 
| 164 | } | |
| 165 | revalidate() | |
| 166 | repaint() | |
| 167 | } | |
| 168 | ||
| 169 | layout(tree_pane) = BorderPanel.Position.Center | |
| 170 | layout(controls) = BorderPanel.Position.North | |
| 171 | refresh() | |
| 172 | } |