src/Tools/jEdit/src/query_dockable.scala
changeset 56879 ee2b61f37ad9
parent 56872 1435f0c771dc
child 56881 15e18540df10
equal deleted inserted replaced
56878:a5d082a85124 56879:ee2b61f37ad9
       
     1 /*  Title:      Tools/jEdit/src/query_dockable.scala
       
     2     Author:     Makarius
       
     3 
       
     4 Dockable window for query operations.
       
     5 */
       
     6 
       
     7 package isabelle.jedit
       
     8 
       
     9 
       
    10 import isabelle._
       
    11 
       
    12 import java.awt.event.{ComponentEvent, ComponentAdapter, KeyEvent}
       
    13 import javax.swing.{JComponent, JTextField}
       
    14 
       
    15 import scala.swing.{Button, Component, TextField, CheckBox, Label, ListView,
       
    16   ComboBox, TabbedPane, BorderPanel}
       
    17 import scala.swing.event.{SelectionChanged, ButtonClicked, Key, KeyPressed}
       
    18 
       
    19 import org.gjt.sp.jedit.View
       
    20 
       
    21 
       
    22 object Query_Dockable
       
    23 {
       
    24   private abstract class Operation(view: View)
       
    25   {
       
    26     val pretty_text_area = new Pretty_Text_Area(view)
       
    27     def query_operation: Query_Operation[View]
       
    28     def query: JComponent
       
    29     def select: Unit
       
    30     def page: TabbedPane.Page
       
    31   }
       
    32 }
       
    33 
       
    34 class Query_Dockable(view: View, position: String) extends Dockable(view, position)
       
    35 {
       
    36   /* common GUI components */
       
    37 
       
    38   private var zoom_factor = 100
       
    39 
       
    40   private val zoom =
       
    41     new GUI.Zoom_Box(factor => if (zoom_factor != factor) { zoom_factor = factor; handle_resize() })
       
    42     {
       
    43       tooltip = "Zoom factor for output font size"
       
    44     }
       
    45 
       
    46 
       
    47   private def make_query(property: String, tooltip: String, apply_query: () => Unit): JTextField =
       
    48     new Completion_Popup.History_Text_Field(property)
       
    49     {
       
    50       override def processKeyEvent(evt: KeyEvent)
       
    51       {
       
    52         if (evt.getID == KeyEvent.KEY_PRESSED && evt.getKeyCode == KeyEvent.VK_ENTER) apply_query()
       
    53         super.processKeyEvent(evt)
       
    54       }
       
    55       { val max = getPreferredSize; max.width = Integer.MAX_VALUE; setMaximumSize(max) }
       
    56       setColumns(40)
       
    57       setToolTipText(tooltip)
       
    58       setFont(GUI.imitate_font(Font_Info.main_family(), getFont, 1.2))
       
    59     }
       
    60 
       
    61 
       
    62   /* consume status */
       
    63 
       
    64   def consume_status(process_indicator: Process_Indicator, status: Query_Operation.Status.Value)
       
    65   {
       
    66     status match {
       
    67       case Query_Operation.Status.WAITING =>
       
    68         process_indicator.update("Waiting for evaluation of context ...", 5)
       
    69       case Query_Operation.Status.RUNNING =>
       
    70         process_indicator.update("Running find operation ...", 15)
       
    71       case Query_Operation.Status.FINISHED =>
       
    72         process_indicator.update(null, 0)
       
    73     }
       
    74   }
       
    75 
       
    76 
       
    77   /* find theorems */
       
    78 
       
    79   private val find_theorems = new Query_Dockable.Operation(view)
       
    80   {
       
    81     /* query */
       
    82 
       
    83     private val process_indicator = new Process_Indicator
       
    84 
       
    85     val query_operation =
       
    86       new Query_Operation(PIDE.editor, view, "find_theorems",
       
    87         consume_status(process_indicator, _),
       
    88         (snapshot, results, body) =>
       
    89           pretty_text_area.update(snapshot, results, Pretty.separate(body)))
       
    90 
       
    91     private def apply_query()
       
    92     {
       
    93       query_operation.apply_query(List(limit.text, allow_dups.selected.toString, query.getText))
       
    94     }
       
    95 
       
    96     private val query_label = new Label("Search criteria:") {
       
    97       tooltip =
       
    98         GUI.tooltip_lines(
       
    99           "Search criteria for find operation, e.g.\n\"_ = _\" \"op +\" name: Group -name: monoid")
       
   100     }
       
   101 
       
   102     val query = make_query("isabelle-find-theorems", query_label.tooltip, apply_query _)
       
   103 
       
   104 
       
   105     /* GUI page */
       
   106 
       
   107     private val limit = new TextField(PIDE.options.int("find_theorems_limit").toString, 5) {
       
   108       tooltip = "Limit of displayed results"
       
   109       verifier = (s: String) =>
       
   110         s match { case Properties.Value.Int(x) => x >= 0 case _ => false }
       
   111       listenTo(keys)
       
   112       reactions += { case KeyPressed(_, Key.Enter, 0, _) => apply_query() }
       
   113     }
       
   114 
       
   115     private val allow_dups = new CheckBox("Duplicates") {
       
   116       tooltip = "Show all versions of matching theorems"
       
   117       selected = false
       
   118     }
       
   119 
       
   120     private val apply_button = new Button("Apply") {
       
   121       tooltip = "Find theorems meeting specified criteria"
       
   122       reactions += { case ButtonClicked(_) => apply_query() }
       
   123     }
       
   124 
       
   125     private val control_panel =
       
   126       new Wrap_Panel(Wrap_Panel.Alignment.Right)(
       
   127         query_label, Component.wrap(query), limit, allow_dups,
       
   128         process_indicator.component, apply_button)
       
   129 
       
   130     def select { control_panel.contents += zoom }
       
   131 
       
   132     val page =
       
   133       new TabbedPane.Page("Find Theorems", new BorderPanel {
       
   134         add(control_panel, BorderPanel.Position.North)
       
   135         add(Component.wrap(pretty_text_area), BorderPanel.Position.Center)
       
   136       }, apply_button.tooltip)
       
   137   }
       
   138 
       
   139 
       
   140   /* find consts */
       
   141 
       
   142   private val find_consts = new Query_Dockable.Operation(view)
       
   143   {
       
   144     /* query */
       
   145 
       
   146     private val process_indicator = new Process_Indicator
       
   147 
       
   148     val query_operation =
       
   149       new Query_Operation(PIDE.editor, view, "find_consts",
       
   150         consume_status(process_indicator, _),
       
   151         (snapshot, results, body) =>
       
   152           pretty_text_area.update(snapshot, results, Pretty.separate(body)))
       
   153 
       
   154     private def apply_query()
       
   155     {
       
   156       query_operation.apply_query(List(query.getText))
       
   157     }
       
   158 
       
   159     private val query_label = new Label("Search criteria:") {
       
   160       tooltip = GUI.tooltip_lines("Name / type patterns for constants")
       
   161     }
       
   162 
       
   163     val query = make_query("isabelle-find-consts", query_label.tooltip, apply_query _)
       
   164 
       
   165 
       
   166     /* GUI page */
       
   167 
       
   168     private val apply_button = new Button("Apply") {
       
   169       tooltip = "Find constants by name / type patterns"
       
   170       reactions += { case ButtonClicked(_) => apply_query() }
       
   171     }
       
   172 
       
   173     private val control_panel =
       
   174       new Wrap_Panel(Wrap_Panel.Alignment.Right)(
       
   175         query_label, Component.wrap(query), process_indicator.component, apply_button)
       
   176 
       
   177     def select { control_panel.contents += zoom }
       
   178 
       
   179     val page =
       
   180       new TabbedPane.Page("Find Constants", new BorderPanel {
       
   181         add(control_panel, BorderPanel.Position.North)
       
   182         add(Component.wrap(pretty_text_area), BorderPanel.Position.Center)
       
   183       }, apply_button.tooltip)
       
   184   }
       
   185 
       
   186 
       
   187   /* print operation */
       
   188 
       
   189   private val print_operation = new Query_Dockable.Operation(view)
       
   190   {
       
   191     /* query */
       
   192 
       
   193     val query_operation =
       
   194       new Query_Operation(PIDE.editor, view, "print_operation", _ => (),
       
   195         (snapshot, results, body) =>
       
   196           pretty_text_area.update(snapshot, results, Pretty.separate(body)))
       
   197 
       
   198     private def apply_query()
       
   199     {
       
   200       query_operation.apply_query(List(_selector.selection.item))
       
   201     }
       
   202 
       
   203     private val query_label = new Label("Print:")
       
   204 
       
   205     def query: JComponent = _selector.peer.asInstanceOf[JComponent]
       
   206 
       
   207 
       
   208     /* items */
       
   209 
       
   210     case class Item(name: String, description: String)
       
   211 
       
   212     class Renderer(old_renderer: ListView.Renderer[String], items: List[Item])
       
   213       extends ListView.Renderer[String]
       
   214     {
       
   215       def componentFor(list: ListView[_],
       
   216         selected: Boolean, focused: Boolean, item: String, index: Int) =
       
   217       {
       
   218         val component = old_renderer.componentFor(list, selected, focused, item, index)
       
   219         try { component.tooltip = items(index).description }
       
   220         catch { case _: IndexOutOfBoundsException => }
       
   221         component
       
   222       }
       
   223     }
       
   224 
       
   225     private var _selector_item: Option[String] = None
       
   226     private var _selector = new ComboBox[String](Nil)
       
   227 
       
   228     private def set_selector()
       
   229     {
       
   230       val items = Print_Operation.print_operations(PIDE.session).map(p => Item(p._1, p._2))
       
   231 
       
   232       _selector =
       
   233         new ComboBox(items.map(_.name)) {
       
   234           _selector_item.foreach(item => selection.item = item)
       
   235           listenTo(selection)
       
   236           reactions += {
       
   237             case SelectionChanged(_) =>
       
   238               _selector_item = Some(selection.item)
       
   239               apply_query()
       
   240           }
       
   241         }
       
   242       _selector.renderer = new Renderer(_selector.renderer, items)
       
   243     }
       
   244     set_selector()
       
   245 
       
   246 
       
   247     /* GUI page */
       
   248 
       
   249     private val apply_button = new Button("Apply") {
       
   250       tooltip = "Apply to current context"
       
   251       reactions += { case ButtonClicked(_) => apply_query() }
       
   252     }
       
   253 
       
   254     private val control_panel = new Wrap_Panel(Wrap_Panel.Alignment.Right)()
       
   255 
       
   256     def select
       
   257     {
       
   258       set_selector()
       
   259       control_panel.contents.clear
       
   260       control_panel.contents ++= List(query_label, _selector, apply_button, zoom)
       
   261     }
       
   262 
       
   263     val page =
       
   264       new TabbedPane.Page("Print Context", new BorderPanel {
       
   265         add(control_panel, BorderPanel.Position.North)
       
   266         add(Component.wrap(pretty_text_area), BorderPanel.Position.Center)
       
   267       }, "Print information from context")
       
   268   }
       
   269 
       
   270 
       
   271   /* operations */
       
   272 
       
   273   private val operations = List(find_theorems, find_consts, print_operation)
       
   274 
       
   275   private val operations_pane = new TabbedPane
       
   276   {
       
   277     pages ++= operations.map(_.page)
       
   278     listenTo(selection)
       
   279     reactions += { case SelectionChanged(_) => select_operation() }
       
   280   }
       
   281 
       
   282   private def get_operation(): Option[Query_Dockable.Operation] =
       
   283     try { Some(operations(operations_pane.selection.index)) }
       
   284     catch { case _: IndexOutOfBoundsException => None }
       
   285 
       
   286   private def select_operation() {
       
   287     for (op <- get_operation()) { op.select; op.query.requestFocus }
       
   288     operations_pane.revalidate
       
   289   }
       
   290 
       
   291   override def focusOnDefaultComponent { for (op <- get_operation()) op.query.requestFocus }
       
   292 
       
   293   select_operation()
       
   294   set_content(operations_pane)
       
   295 
       
   296 
       
   297   /* resize */
       
   298 
       
   299   private def handle_resize(): Unit =
       
   300     Swing_Thread.require {
       
   301       for (op <- operations) {
       
   302         op.pretty_text_area.resize(
       
   303           Font_Info.main(PIDE.options.real("jedit_font_scale") * zoom_factor / 100))
       
   304       }
       
   305     }
       
   306 
       
   307   private val delay_resize =
       
   308     Swing_Thread.delay_first(PIDE.options.seconds("editor_update_delay")) { handle_resize() }
       
   309 
       
   310   addComponentListener(new ComponentAdapter {
       
   311     override def componentResized(e: ComponentEvent) { delay_resize.invoke() }
       
   312   })
       
   313 
       
   314 
       
   315   /* main */
       
   316 
       
   317   private val main =
       
   318     Session.Consumer[Session.Global_Options](getClass.getName) {
       
   319       case _: Session.Global_Options => Swing_Thread.later { handle_resize() }
       
   320     }
       
   321 
       
   322   override def init()
       
   323   {
       
   324     PIDE.session.global_options += main
       
   325     handle_resize()
       
   326     operations.foreach(op => op.query_operation.activate())
       
   327   }
       
   328 
       
   329   override def exit()
       
   330   {
       
   331     operations.foreach(op => op.query_operation.deactivate())
       
   332     PIDE.session.global_options -= main
       
   333     delay_resize.revoke()
       
   334   }
       
   335 }
       
   336