diff --git a/app/display/actions/src/main/java/org/csstudio/display/actions/Messages.java b/app/display/actions/src/main/java/org/csstudio/display/actions/Messages.java
index 3361c61929..6052dea50d 100644
--- a/app/display/actions/src/main/java/org/csstudio/display/actions/Messages.java
+++ b/app/display/actions/src/main/java/org/csstudio/display/actions/Messages.java
@@ -22,6 +22,7 @@ public class Messages {
public static String ActionCloseDisplay,
ActionExecuteCommand,
ActionExecuteScript,
+ ActionOpenDataBrowser,
ActionOpenDisplay,
ActionOpenFile,
ActionOpenWebPage,
diff --git a/app/display/actions/src/main/java/org/csstudio/display/actions/OpenDataBrowserAction.java b/app/display/actions/src/main/java/org/csstudio/display/actions/OpenDataBrowserAction.java
new file mode 100644
index 0000000000..6198802837
--- /dev/null
+++ b/app/display/actions/src/main/java/org/csstudio/display/actions/OpenDataBrowserAction.java
@@ -0,0 +1,81 @@
+package org.csstudio.display.actions;
+
+import javafx.scene.image.Image;
+import org.csstudio.display.builder.model.persist.ModelReader;
+import org.csstudio.display.builder.model.persist.ModelWriter;
+import org.csstudio.display.builder.model.persist.XMLTags;
+import org.csstudio.display.builder.model.properties.ActionInfoBase;
+import org.csstudio.display.builder.representation.javafx.actionsdialog.ActionsDialog;
+import org.phoebus.framework.persistence.XMLUtil;
+import org.phoebus.ui.javafx.ImageCache;
+import org.w3c.dom.Element;
+
+import javax.xml.stream.XMLStreamWriter;
+
+public class OpenDataBrowserAction extends ActionInfoBase {
+
+ public static final String OPEN_DATA_BROWSER = "open_data_browser";
+ private static final Integer PRIORITY = 10;
+ private String pvs = "$(pv_name)";
+ private String timeframe = "1 hour";
+
+ @SuppressWarnings("unused")
+ /**
+ * Do not remove, needed by SPI framework.
+ */
+ public OpenDataBrowserAction() {
+ this.description = Messages.ActionOpenDataBrowser;
+ this.type = OPEN_DATA_BROWSER;
+ }
+
+ public OpenDataBrowserAction(String description, String pvs, String timeframe) {
+ this.pvs = pvs;
+ this.timeframe = timeframe;
+ this.description = description;
+ this.type = OPEN_DATA_BROWSER;
+ }
+
+ @Override
+ public Image getImage() {
+ return ImageCache.getImage(ActionsDialog.class, "/icons/databrowser.png");
+ }
+
+ @Override
+ public Integer getPriority() {
+ return PRIORITY;
+ }
+
+ @Override
+ public void readFromXML(ModelReader modelReader, Element actionXml) {
+ pvs = XMLUtil.getChildString(actionXml, XMLTags.PV_NAME).orElse("");
+ timeframe = XMLUtil.getChildString(actionXml, XMLTags.TIME_FRAME).orElse("");
+
+ if (description.isEmpty())
+ description = Messages.ActionOpenDataBrowser;
+ }
+
+ @Override
+ public void writeToXML(ModelWriter modelWriter, XMLStreamWriter writer) throws Exception {
+ writer.writeAttribute(XMLTags.TYPE, OPEN_DATA_BROWSER);
+ writeDescriptionToXML(writer, description);
+ writer.writeStartElement(XMLTags.PV_NAME);
+ writer.writeCharacters(pvs);
+ writer.writeEndElement();
+ writer.writeStartElement(XMLTags.TIME_FRAME);
+ writer.writeCharacters(timeframe);
+ writer.writeEndElement();
+ }
+
+ public String getPVs() {
+ return pvs;
+ }
+
+ public String getTimeframe() {
+ return timeframe;
+ }
+
+ @Override
+ public boolean matchesAction(String actionId) {
+ return actionId.equalsIgnoreCase(OPEN_DATA_BROWSER);
+ }
+}
diff --git a/app/display/actions/src/main/java/org/csstudio/display/actions/OpenDataBrowserActionController.java b/app/display/actions/src/main/java/org/csstudio/display/actions/OpenDataBrowserActionController.java
new file mode 100644
index 0000000000..111e82cbca
--- /dev/null
+++ b/app/display/actions/src/main/java/org/csstudio/display/actions/OpenDataBrowserActionController.java
@@ -0,0 +1,45 @@
+package org.csstudio.display.actions;
+
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.fxml.FXML;
+import javafx.scene.control.TextField;
+import org.csstudio.display.builder.model.ActionControllerBase;
+import org.csstudio.display.builder.model.Widget;
+import org.csstudio.display.builder.model.spi.ActionInfo;
+
+/**
+ * FXML Controller for the open data browser action editor
+ */
+public class OpenDataBrowserActionController extends ActionControllerBase {
+
+ @FXML
+ private TextField pvNames;
+
+ @FXML
+ private TextField timeframe;
+
+ private final StringProperty pvNameProperty = new SimpleStringProperty();
+ private final StringProperty timeframeProperty = new SimpleStringProperty();
+
+ public OpenDataBrowserActionController(OpenDataBrowserAction openDataBrowserActionInfo){
+ descriptionProperty.set(openDataBrowserActionInfo.getDescription());
+ pvNameProperty.setValue(openDataBrowserActionInfo.getPVs());
+ timeframeProperty.setValue(openDataBrowserActionInfo.getTimeframe());
+ }
+
+ /**
+ * Init
+ */
+ @FXML
+ @Override
+ public void initialize() {
+ super.initialize();
+ pvNames.textProperty().bindBidirectional(pvNameProperty);
+ timeframe.textProperty().bindBidirectional(timeframeProperty);
+ }
+
+ public ActionInfo getActionInfo(){
+ return new OpenDataBrowserAction(descriptionProperty.get(), pvNameProperty.get(), timeframeProperty.get());
+ }
+}
diff --git a/app/display/actions/src/main/java/org/csstudio/display/actions/OpenDataBrowserActionEditor.java b/app/display/actions/src/main/java/org/csstudio/display/actions/OpenDataBrowserActionEditor.java
new file mode 100644
index 0000000000..e5d02434b1
--- /dev/null
+++ b/app/display/actions/src/main/java/org/csstudio/display/actions/OpenDataBrowserActionEditor.java
@@ -0,0 +1,60 @@
+package org.csstudio.display.actions;
+
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Node;
+import org.csstudio.display.builder.model.Widget;
+import org.csstudio.display.builder.model.spi.ActionEditor;
+import org.csstudio.display.builder.model.spi.ActionInfo;
+import org.phoebus.framework.nls.NLS;
+
+import java.io.IOException;
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Editor for {@link OpenDataBrowserAction}.
+ */
+public class OpenDataBrowserActionEditor implements ActionEditor {
+
+ private OpenDataBrowserActionController openDataBrowserActionController;
+ private Node openDataBowserEditorUi;
+
+ @Override
+ public boolean matchesAction(String type) {
+ return OpenDataBrowserAction.OPEN_DATA_BROWSER.equalsIgnoreCase(type);
+ }
+
+ @Override
+ public ActionInfo getActionInfo() {
+ return openDataBrowserActionController.getActionInfo();
+ }
+
+ @Override
+ public Node getEditorUi() {
+ return openDataBowserEditorUi;
+ }
+
+ @Override
+ public void configure(Widget widget, ActionInfo actionInfo) {
+ FXMLLoader fxmlLoader = new FXMLLoader();
+ fxmlLoader.setResources(NLS.getMessages(Messages.class));
+ fxmlLoader.setLocation(this.getClass().getResource("OpenDataBrowserAction.fxml"));
+ fxmlLoader.setControllerFactory(clazz -> {
+ try {
+ return clazz.getConstructor(OpenDataBrowserAction.class).newInstance(actionInfo);
+ } catch (Exception e) {
+ Logger.getLogger(OpenDataBrowserActionEditor.class.getName()).log(Level.SEVERE, "Failed to construct OpenDataBrowserActionController", e);
+ }
+ return null;
+ });
+
+ try {
+ openDataBowserEditorUi = fxmlLoader.load();
+ openDataBrowserActionController = fxmlLoader.getController();
+ } catch (IOException e) {
+ Logger.getLogger(OpenDataBrowserActionEditor.class.getName()).log(Level.SEVERE, "Failed to load the OpenDataBrowserAction UI", e);
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/app/display/actions/src/main/resources/META-INF/services/org.csstudio.display.builder.model.spi.ActionEditor b/app/display/actions/src/main/resources/META-INF/services/org.csstudio.display.builder.model.spi.ActionEditor
index b04d508548..4c7fa91c2b 100644
--- a/app/display/actions/src/main/resources/META-INF/services/org.csstudio.display.builder.model.spi.ActionEditor
+++ b/app/display/actions/src/main/resources/META-INF/services/org.csstudio.display.builder.model.spi.ActionEditor
@@ -5,3 +5,4 @@ org.csstudio.display.actions.OpenDisplayActionEditor
org.csstudio.display.actions.OpenFileActionEditor
org.csstudio.display.actions.OpenWebPageActionEditor
org.csstudio.display.actions.CloseDisplayActionEditor
+org.csstudio.display.actions.OpenDataBrowserActionEditor
diff --git a/app/display/actions/src/main/resources/META-INF/services/org.csstudio.display.builder.model.spi.ActionInfo b/app/display/actions/src/main/resources/META-INF/services/org.csstudio.display.builder.model.spi.ActionInfo
index c05adaf60e..08898016b1 100644
--- a/app/display/actions/src/main/resources/META-INF/services/org.csstudio.display.builder.model.spi.ActionInfo
+++ b/app/display/actions/src/main/resources/META-INF/services/org.csstudio.display.builder.model.spi.ActionInfo
@@ -4,4 +4,5 @@ org.csstudio.display.actions.ExecuteScriptAction
org.csstudio.display.actions.ExecuteCommandAction
org.csstudio.display.actions.OpenFileAction
org.csstudio.display.actions.OpenWebPageAction
-org.csstudio.display.actions.CloseDisplayAction
\ No newline at end of file
+org.csstudio.display.actions.CloseDisplayAction
+org.csstudio.display.actions.OpenDataBrowserAction
\ No newline at end of file
diff --git a/app/display/actions/src/main/resources/org/csstudio/display/actions/OpenDataBrowserAction.fxml b/app/display/actions/src/main/resources/org/csstudio/display/actions/OpenDataBrowserAction.fxml
new file mode 100644
index 0000000000..86e4dc7f48
--- /dev/null
+++ b/app/display/actions/src/main/resources/org/csstudio/display/actions/OpenDataBrowserAction.fxml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/display/actions/src/main/resources/org/csstudio/display/actions/messages.properties b/app/display/actions/src/main/resources/org/csstudio/display/actions/messages.properties
index f0efc8e1f4..f00d1f43e8 100644
--- a/app/display/actions/src/main/resources/org/csstudio/display/actions/messages.properties
+++ b/app/display/actions/src/main/resources/org/csstudio/display/actions/messages.properties
@@ -1,6 +1,7 @@
ActionCloseDisplay=Close Display
ActionExecuteCommand=Execute Command
ActionExecuteScript=Execute Script
+ActionOpenDataBrowser=Open Data Browser
ActionOpenDisplay=Open Display
ActionsDialog_Description=Description:
ActionOpenFile=Open File
@@ -14,8 +15,12 @@ ActionsDialog_ExecuteAll=Execute all actions as one
ActionsDialog_FilePath=File Path:
ActionsDialog_Info=Configure actions which open displays, write PVs etc.
ActionsDialog_PVName=PV Name:
+ActionsDialog_PVNames=PV Names:
+ActionsDialog_PVNames_Help=List names of PVs separated by a space (e.g. 'PV:1 PV:2')
ActionsDialog_ScriptPath=Script File:
ActionsDialog_ScriptText=Embedded Script Text:
+ActionsDialog_Timeframe=Timeframe Start:
+ActionsDialog_Timeframe_Help=Format: temporal (e.g. '1 hour', '1 day') or absolute (e.g. 'yyyy-MM-dd HH:mm:ss')
ActionsDialog_URL=URL:
ActionsDialog_Value=Value:
Add=Add
diff --git a/app/display/model/src/main/java/org/csstudio/display/builder/model/persist/XMLTags.java b/app/display/model/src/main/java/org/csstudio/display/builder/model/persist/XMLTags.java
index 2c6c95b12d..fd383c2b9b 100644
--- a/app/display/model/src/main/java/org/csstudio/display/builder/model/persist/XMLTags.java
+++ b/app/display/model/src/main/java/org/csstudio/display/builder/model/persist/XMLTags.java
@@ -37,6 +37,7 @@ public interface XMLTags
SCRIPT = "script",
TARGET = "target",
TEXT = "text",
+ TIME_FRAME = "timeframe",
TRIGGER = "trigger",
TYPE = "type",
URL = "url",
diff --git a/app/display/runtime/src/main/java/org/csstudio/display/builder/runtime/app/actionhandlers/OpenDataBrowserActionHandler.java b/app/display/runtime/src/main/java/org/csstudio/display/builder/runtime/app/actionhandlers/OpenDataBrowserActionHandler.java
new file mode 100644
index 0000000000..05434471f9
--- /dev/null
+++ b/app/display/runtime/src/main/java/org/csstudio/display/builder/runtime/app/actionhandlers/OpenDataBrowserActionHandler.java
@@ -0,0 +1,102 @@
+package org.csstudio.display.builder.runtime.app.actionhandlers;
+
+import org.csstudio.display.actions.OpenDataBrowserAction;
+import org.csstudio.display.builder.model.DisplayModel;
+import org.csstudio.display.builder.model.Widget;
+import org.csstudio.display.builder.model.spi.ActionHandler;
+import org.csstudio.display.builder.model.spi.ActionInfo;
+import org.csstudio.display.builder.representation.ToolkitRepresentation;
+import org.csstudio.trends.databrowser3.DataBrowserApp;
+import org.csstudio.trends.databrowser3.DataBrowserInstance;
+import org.csstudio.trends.databrowser3.model.PVItem;
+import org.phoebus.framework.macros.MacroHandler;
+import org.phoebus.framework.macros.MacroValueProvider;
+import org.phoebus.framework.util.ResourceParser;
+import org.phoebus.framework.workbench.ApplicationService;
+import org.phoebus.util.time.TimeParser;
+import org.phoebus.util.time.TimeRelativeInterval;
+import org.phoebus.util.time.TimestampFormats;
+
+import java.time.Instant;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class OpenDataBrowserActionHandler implements ActionHandler {
+
+ private final Logger logger = Logger.getLogger(OpenDataBrowserActionHandler.class.getName());
+
+ /**
+ * Expand macros that may exist in the PV name
+ * @param name PV name
+ * @param macros Macros available
+ * @return PV name with macros replaced
+ */
+ private String expandMacros(String name, MacroValueProvider macros) {
+ try {
+ name = MacroHandler.replace(macros, name);
+ } catch (Exception ignore) {
+ // NOP
+ }
+ return name;
+ }
+
+ @Override
+ public void handleAction(Widget sourceWidget, ActionInfo pluggableActionInfo) {
+ OpenDataBrowserAction action = (OpenDataBrowserAction) pluggableActionInfo;
+
+ try
+ {
+ final DisplayModel model = sourceWidget.getTopDisplayModel();
+ final ToolkitRepresentation