1818import java .util .Objects ;
1919import java .util .logging .Level ;
2020import java .util .logging .Logger ;
21-
21+ import java .util .stream .Collectors ;
22+
23+ import javafx .event .EventHandler ;
24+ import javafx .scene .control .ContextMenu ;
25+ import javafx .scene .control .MenuItem ;
26+ import javafx .scene .control .SelectionMode ;
27+ import javafx .scene .control .cell .PropertyValueFactory ;
28+ import javafx .scene .input .ContextMenuEvent ;
2229import org .csstudio .display .builder .model .DisplayModel ;
2330import org .csstudio .display .builder .model .Widget ;
2431import org .csstudio .display .builder .model .WidgetDescriptor ;
3138import org .epics .vtype .AlarmSeverity ;
3239import org .epics .vtype .VNumberArray ;
3340import org .epics .vtype .VType ;
41+ import org .phoebus .core .types .ProcessVariable ;
3442import org .phoebus .core .vtypes .VTypeHelper ;
43+ import org .phoebus .framework .adapter .AdapterService ;
3544import org .phoebus .framework .macros .Macros ;
45+ import org .phoebus .framework .selection .SelectionService ;
46+ import org .phoebus .ui .application .ContextMenuService ;
3647import org .phoebus .ui .dialog .DialogHelper ;
3748import org .phoebus .ui .dialog .ExceptionDetailsErrorDialog ;
3849import org .phoebus .ui .javafx .ReadOnlyTextCell ;
6172import javafx .scene .layout .Priority ;
6273import javafx .scene .layout .VBox ;
6374import javafx .stage .FileChooser ;
75+ import org .phoebus .ui .spi .ContextMenuEntry ;
6476
6577/** Dialog for displaying widget information
6678 * @author Kay Kasemir
@@ -75,16 +87,14 @@ public class WidgetInfoDialog extends Dialog<Boolean>
7587 private DisplayWidgetStats stats ;
7688
7789 /** PV info */
78- public static class NameStateValue
90+ public static class NameStateValue extends ProcessVariable
7991 {
80- /** PV Name */
81- public final String name ;
8292 /** State, incl. read-only or writable? */
83- public final String state ;
93+ private final String state ;
8494 /** Last known value */
85- public final VType value ;
95+ private final VType value ;
8696 /** Path to Widget within display that uses the PV */
87- public final String path ;
97+ private final String path ;
8898
8999 /** @param name PV Name
90100 * @param state State, incl. read-only or writable?
@@ -93,11 +103,23 @@ public static class NameStateValue
93103 */
94104 public NameStateValue (final String name , final String state , final VType value , final String path )
95105 {
96- this . name = name ;
106+ super ( name ) ;
97107 this .state = state ;
98108 this .value = value ;
99109 this .path = path ;
100110 }
111+
112+ public String getState () {
113+ return state ;
114+ }
115+
116+ public VType getValue () {
117+ return value ;
118+ }
119+
120+ public String getPath () {
121+ return path ;
122+ }
101123 }
102124
103125 /** Cell with text colored based on alarm severity */
@@ -209,8 +231,8 @@ private void exportToCSV(File file){
209231
210232 buffer .append ("PVS (name, state, value, widget path)" ).append (System .lineSeparator ())
211233 .append (horizontalRuler ).append (System .lineSeparator ());
212- pvs .stream ().sorted (Comparator .comparing (pv -> pv .name )).forEach (pv -> {
213- buffer .append (pv .name ).append (itemSeparator )
234+ pvs .stream ().sorted (Comparator .comparing (pv -> pv .getName () )).forEach (pv -> {
235+ buffer .append (pv .getName () ).append (itemSeparator )
214236 .append (pv .state )
215237 .append (itemSeparator )
216238 .append (getPVValue (pv .value ))
@@ -292,16 +314,13 @@ private Tab createPVs(final Collection<NameStateValue> pvs)
292314 {
293315 // Use text field to allow users to copy the name, value to clipboard
294316 final TableColumn <NameStateValue , String > name = new TableColumn <>(Messages .WidgetInfoDialog_Name );
295- name .setCellFactory (col -> new ReadOnlyTextCell <>());
296- name .setCellValueFactory (param -> new ReadOnlyStringWrapper (param .getValue ().name ));
317+ name .setCellValueFactory (new PropertyValueFactory <NameStateValue , String >("name" ));
297318
298319 final TableColumn <NameStateValue , String > state = new TableColumn <>(Messages .WidgetInfoDialog_State );
299- state .setCellFactory (col -> new ReadOnlyTextCell <>());
300- state .setCellValueFactory (param -> new ReadOnlyStringWrapper (param .getValue ().state ));
320+ state .setCellValueFactory (new PropertyValueFactory <NameStateValue , String >("state" ));
301321
302322 final TableColumn <NameStateValue , String > path = new TableColumn <>(Messages .WidgetInfoDialog_Path );
303- path .setCellFactory (col -> new ReadOnlyTextCell <>());
304- path .setCellValueFactory (param -> new ReadOnlyStringWrapper (param .getValue ().path ));
323+ path .setCellValueFactory (new PropertyValueFactory <NameStateValue , String >("path" ));
305324
306325 final TableColumn <NameStateValue , String > value = new TableColumn <>(Messages .WidgetInfoDialog_Value );
307326 value .setCellFactory (col -> new AlarmColoredCell ());
@@ -312,14 +331,51 @@ private Tab createPVs(final Collection<NameStateValue> pvs)
312331 });
313332
314333 final ObservableList <NameStateValue > pv_data = FXCollections .observableArrayList (pvs );
315- pv_data .sort (Comparator .comparing (a -> a .name ));
334+ pv_data .sort (Comparator .comparing (a -> a .getName () ));
316335 final TableView <NameStateValue > table = new TableView <>(pv_data );
317336 table .getColumns ().add (name );
318337 table .getColumns ().add (state );
319338 table .getColumns ().add (value );
320339 table .getColumns ().add (path );
321340 table .setColumnResizePolicy (TableView .CONSTRAINED_RESIZE_POLICY );
322341
342+ table .getSelectionModel ().setSelectionMode (SelectionMode .MULTIPLE );
343+ table .getSelectionModel ().selectedItemProperty ().addListener ((obs , oldSelection , newSelection ) -> {
344+ if (newSelection != null ) {
345+ SelectionService .getInstance ().setSelection (table , table .getSelectionModel ().getSelectedItems ());
346+ }
347+ });
348+
349+ table .setOnContextMenuRequested (event -> {
350+
351+ final ContextMenu contextMenu = new ContextMenu ();
352+
353+ List <ContextMenuEntry > contextEntries = ContextMenuService .getInstance ().listSupportedContextMenuEntries ();
354+
355+ contextEntries .forEach (entry -> {
356+ MenuItem item = new MenuItem (entry .getName (), new ImageView (entry .getIcon ()));
357+ item .setOnAction (e -> {
358+ try {
359+ ObservableList <NameStateValue > old = table .getSelectionModel ().getSelectedItems ();
360+
361+ List <Object > selectedPVs = SelectionService .getInstance ().getSelection ().getSelections ().stream ().map (s -> {
362+ return AdapterService .adapt (s , entry .getSupportedType ()).get ();
363+ }).collect (Collectors .toList ());
364+ // set the selection
365+ SelectionService .getInstance ().setSelection (table , selectedPVs );
366+ entry .call (SelectionService .getInstance ().getSelection ());
367+ // reset the selection
368+ SelectionService .getInstance ().setSelection (table , old );
369+ } catch (Exception ex ) {
370+ //logger.log(Level.WARNING, "Failed to execute action " + entry.getName(), ex);
371+ }
372+ });
373+ contextMenu .getItems ().add (item );
374+ });
375+
376+ table .setContextMenu (contextMenu );
377+ });
378+
323379 return new Tab (Messages .WidgetInfoDialog_TabPVs , table );
324380 }
325381
0 commit comments