From 385e721fc6eb9dbfb4bb09d7b16a61525c85ac64 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Wed, 21 Aug 2024 11:14:52 +0200 Subject: [PATCH 01/19] CSSTUDIO-2371 In "SingleLogEntryDisplayController", load links to logbook entries in the Logbook application. --- .../logbook/olog/ui/LogEntryTable.java | 4 ++- .../ui/SingleLogEntryDisplayController.java | 25 +++++++++++++++--- .../ui/web/HyperLinkRedirectListener.java | 26 ++++++++++++++----- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java index 5b4a3146b1..84e5db8a82 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java @@ -50,7 +50,9 @@ public LogEntryTable(final LogEntryTableApp app) { return clazz.getConstructor(LogClient.class, SearchParameters.class) .newInstance(app.getClient(), searchParameters); } else if (clazz.isAssignableFrom(SingleLogEntryDisplayController.class)) { - return clazz.getConstructor(LogClient.class).newInstance(app.getClient()); + SingleLogEntryDisplayController singleLogEntryDisplayController = (SingleLogEntryDisplayController) clazz.getConstructor(LogClient.class).newInstance(app.getClient()); + singleLogEntryDisplayController.setSelectLogEntryInUI(logEntry -> controller.selectLogEntry(logEntry)); + return singleLogEntryDisplayController; } else if (clazz.isAssignableFrom(LogEntryDisplayController.class)) { return clazz.getConstructor().newInstance(); } else if (clazz.isAssignableFrom(LogPropertiesController.class)) { diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/SingleLogEntryDisplayController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/SingleLogEntryDisplayController.java index 9ab9552142..7a63dca055 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/SingleLogEntryDisplayController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/SingleLogEntryDisplayController.java @@ -30,6 +30,9 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -87,11 +90,24 @@ public class SingleLogEntryDisplayController extends HtmlAwareController { private final SimpleBooleanProperty logEntryUpdated = new SimpleBooleanProperty(); + private Optional> openLogEntryWithID = Optional.empty(); + public SingleLogEntryDisplayController(LogClient logClient) { super(logClient.getServiceUrl()); this.logClient = logClient; } + public void setSelectLogEntryInUI(Function selectLogEntry) { + this.openLogEntryWithID = Optional.of(id -> { + LogEntry logEntry = logClient.getLog(id); + boolean selected = selectLogEntry.apply(logEntry); + if (!selected) { + // The log entry was not available in the TreeView. Set the log entry without selecting it in the treeview: + setLogEntry(logEntry); + } + }); + } + @FXML public void initialize() { @@ -101,9 +117,12 @@ public void initialize() { copyURLButton.visibleProperty().setValue(LogbookUIPreferences.web_client_root_URL != null && !LogbookUIPreferences.web_client_root_URL.isEmpty()); - webEngine = webView.getEngine(); - // This will make links clicked in the WebView to open in default browser. - webEngine.getLoadWorker().stateProperty().addListener(new HyperLinkRedirectListener(webView)); + { + Optional webClientRoot = LogbookUIPreferences.web_client_root_URL == null || LogbookUIPreferences.web_client_root_URL.equals("") ? Optional.empty() : Optional.of(LogbookUIPreferences.web_client_root_URL); + webEngine = webView.getEngine(); + // This will make links clicked in the WebView to open in default browser. + webEngine.getLoadWorker().stateProperty().addListener(new HyperLinkRedirectListener(webView, webClientRoot, openLogEntryWithID)); + } updatedIndicator.visibleProperty().bind(logEntryUpdated); updatedIndicator.setOnMouseEntered(me -> updatedIndicator.setCursor(Cursor.HAND)); diff --git a/core/ui/src/main/java/org/phoebus/ui/web/HyperLinkRedirectListener.java b/core/ui/src/main/java/org/phoebus/ui/web/HyperLinkRedirectListener.java index 386a5161c8..9179d69b01 100644 --- a/core/ui/src/main/java/org/phoebus/ui/web/HyperLinkRedirectListener.java +++ b/core/ui/src/main/java/org/phoebus/ui/web/HyperLinkRedirectListener.java @@ -33,6 +33,8 @@ import org.w3c.dom.html.HTMLAnchorElement; import java.net.URI; +import java.util.Optional; +import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; @@ -53,14 +55,18 @@ public class HyperLinkRedirectListener implements ChangeListener, EventLi private static final String ANCHOR_TAG = "a"; private final WebView webView; + private final Optional webClientRoot; + private final Optional> openLogentryWithID; private static final Logger LOGGER = Logger.getLogger(HyperLinkRedirectListener.class.getName()); /** * @param webView The {@link WebView} showing the document. */ - public HyperLinkRedirectListener(WebView webView) { + public HyperLinkRedirectListener(WebView webView, Optional webClientRoot, Optional> openLogentryWithID) { this.webView = webView; + this.webClientRoot = webClientRoot; + this.openLogentryWithID = openLogentryWithID; } @Override @@ -78,13 +84,21 @@ public void changed(ObservableValue observable, State oldValue, @Override public void handleEvent(Event event) { + event.preventDefault(); HTMLAnchorElement anchorElement = (HTMLAnchorElement) event.getCurrentTarget(); String href = anchorElement.getHref(); - try { - ApplicationService.createInstance("web", new URI(href)); - event.preventDefault(); - } catch (Exception e) { - LOGGER.log(Level.SEVERE, "Failed to launch WebBrowserApplication", e); + if (webClientRoot.isPresent() && openLogentryWithID.isPresent() && href.startsWith(webClientRoot.get())) { + String withoutWebClientRoot = href.substring(webClientRoot.get().length()); + String idString = withoutWebClientRoot.charAt(0) == '/' ? withoutWebClientRoot.substring(1) : withoutWebClientRoot.substring(0); + long id = Long.parseLong(idString); + openLogentryWithID.get().accept(id); + } + else { + try { + ApplicationService.createInstance("web", new URI(href)); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Failed to launch WebBrowserApplication", e); + } } } } From 0a190803a7622813b19ebbf94d0cbeb8bb22431c Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Thu, 22 Aug 2024 08:35:24 +0200 Subject: [PATCH 02/19] CSSTUDIO-2371 Add the class "UndoAndRedoActions" to the class "LogEntryTable". Implement the selection of a new log entry using the functionality of this class. --- .../logbook/olog/ui/LogEntryTable.java | 89 ++++++++++++++++++- .../olog/ui/LogEntryTableViewController.java | 8 ++ .../ui/SingleLogEntryDisplayController.java | 18 ++-- 3 files changed, 101 insertions(+), 14 deletions(-) diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java index 84e5db8a82..a2d506abcb 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java @@ -1,5 +1,7 @@ package org.phoebus.logbook.olog.ui; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import javafx.fxml.FXMLLoader; import javafx.scene.control.Alert; import org.phoebus.framework.nls.NLS; @@ -29,8 +31,11 @@ public class LogEntryTable implements AppInstance { private final LogEntryTableApp app; private LogEntryTableViewController controller; + public UndoAndRedoActions undoAndRedoActions; + public LogEntryTable(final LogEntryTableApp app) { this.app = app; + undoAndRedoActions = new UndoAndRedoActions(); try { OlogQueryManager ologQueryManager = OlogQueryManager.getInstance(); SearchParameters searchParameters = new SearchParameters(); @@ -51,7 +56,7 @@ public LogEntryTable(final LogEntryTableApp app) { .newInstance(app.getClient(), searchParameters); } else if (clazz.isAssignableFrom(SingleLogEntryDisplayController.class)) { SingleLogEntryDisplayController singleLogEntryDisplayController = (SingleLogEntryDisplayController) clazz.getConstructor(LogClient.class).newInstance(app.getClient()); - singleLogEntryDisplayController.setSelectLogEntryInUI(logEntry -> controller.selectLogEntry(logEntry)); + singleLogEntryDisplayController.setSelectLogEntryInUI(id -> undoAndRedoActions.loadLogEntryWithID(id)); return singleLogEntryDisplayController; } else if (clazz.isAssignableFrom(LogEntryDisplayController.class)) { return clazz.getConstructor().newInstance(); @@ -120,4 +125,86 @@ public void save(final Memento memento) { public void logEntryChanged(LogEntry logEntry){ controller.logEntryChanged(logEntry); } + + public class UndoAndRedoActions { + + public ObservableList undoActions; + public ObservableList redoActions; + + public UndoAndRedoActions() { + undoActions = FXCollections.observableArrayList(); + redoActions = FXCollections.observableArrayList(); + } + + public void loadLogEntryWithID(Long id) { + { + LogEntry currentLogEntry = controller.getLogEntry(); + + Runnable undoAction = () -> { + boolean selected = controller.selectLogEntry(currentLogEntry); + if (!selected) { + // The log entry was not available in the TreeView. Set the log entry without selecting it in the treeview: + controller.setLogEntry(currentLogEntry); + } + }; + + undoActions.add(undoAction); + } + + redoActions = FXCollections.observableArrayList(); + + { + LogEntry logEntry = controller.client.getLog(id); + boolean selected = controller.selectLogEntry(logEntry); + if (!selected) { + // The log entry was not available in the TreeView. Set the log entry without selecting it in the treeview: + controller.setLogEntry(logEntry); + } + } + } + + public void performUndo() { + if (undoActions.size() > 0) { + Runnable undoAction = undoActions.get(0); + undoActions.remove(0); + + { + LogEntry currentLogEntry = controller.getLogEntry(); + Runnable redoAction = () -> { + boolean selected = controller.selectLogEntry(currentLogEntry); + if (!selected) { + // The log entry was not available in the TreeView. Set the log entry without selecting it in the treeview: + controller.setLogEntry(currentLogEntry); + } + }; + redoActions.add(0, redoAction); + } + + undoAction.run(); + } + } + + public void performRedo() { + if (redoActions.size() > 0) { + Runnable redoAction = redoActions.get(0); + redoActions.remove(0); + + { + LogEntry currentLogEntry = controller.getLogEntry(); + + Runnable undoAction = () -> { + boolean selected = controller.selectLogEntry(currentLogEntry); + if (!selected) { + // The log entry was not available in the TreeView. Set the log entry without selecting it in the treeview: + controller.setLogEntry(currentLogEntry); + } + }; + + undoActions.add(undoAction); + } + + redoAction.run(); + } + } + } } diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java index 9cb6a534f8..162fe481b5 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java @@ -530,6 +530,14 @@ public void logEntryChanged(LogEntry logEntry) { logEntryDisplayController.updateLogEntry(logEntry); } + protected LogEntry getLogEntry() { + return logEntryDisplayController.getLogEntry(); + } + + protected void setLogEntry(LogEntry logEntry) { + logEntryDisplayController.setLogEntry(logEntry); + } + /** * Selects a log entry as a result of an action outside the {@link TreeView}, but selection happens on the * {@link TreeView} item, if it exists (match on log entry id). If it does not exist, selection is cleared diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/SingleLogEntryDisplayController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/SingleLogEntryDisplayController.java index 7a63dca055..997a04905e 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/SingleLogEntryDisplayController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/SingleLogEntryDisplayController.java @@ -32,7 +32,6 @@ import java.util.List; import java.util.Optional; import java.util.function.Consumer; -import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -90,23 +89,16 @@ public class SingleLogEntryDisplayController extends HtmlAwareController { private final SimpleBooleanProperty logEntryUpdated = new SimpleBooleanProperty(); - private Optional> openLogEntryWithID = Optional.empty(); + private Optional> selectLogEntryInUI = Optional.empty(); public SingleLogEntryDisplayController(LogClient logClient) { super(logClient.getServiceUrl()); this.logClient = logClient; } - public void setSelectLogEntryInUI(Function selectLogEntry) { - this.openLogEntryWithID = Optional.of(id -> { - LogEntry logEntry = logClient.getLog(id); - boolean selected = selectLogEntry.apply(logEntry); - if (!selected) { - // The log entry was not available in the TreeView. Set the log entry without selecting it in the treeview: - setLogEntry(logEntry); - } - }); - } + public void setSelectLogEntryInUI(Consumer selectLogEntryInUI) { + this.selectLogEntryInUI = Optional.of(id -> selectLogEntryInUI.accept(id)); + }; @FXML public void initialize() { @@ -121,7 +113,7 @@ public void initialize() { Optional webClientRoot = LogbookUIPreferences.web_client_root_URL == null || LogbookUIPreferences.web_client_root_URL.equals("") ? Optional.empty() : Optional.of(LogbookUIPreferences.web_client_root_URL); webEngine = webView.getEngine(); // This will make links clicked in the WebView to open in default browser. - webEngine.getLoadWorker().stateProperty().addListener(new HyperLinkRedirectListener(webView, webClientRoot, openLogEntryWithID)); + webEngine.getLoadWorker().stateProperty().addListener(new HyperLinkRedirectListener(webView, webClientRoot, selectLogEntryInUI)); } updatedIndicator.visibleProperty().bind(logEntryUpdated); From 9066946e65a170563010c250036d1a95e1bb55a3 Mon Sep 17 00:00:00 2001 From: Abraham Wolk Date: Thu, 22 Aug 2024 08:57:35 +0200 Subject: [PATCH 03/19] CSSTUDIO-2371 Add "back" and "forward" buttons to the UI. --- .../logbook/olog/ui/LogEntryDisplayController.java | 10 ++++++++++ .../org/phoebus/logbook/olog/ui/LogEntryTable.java | 4 ++-- .../logbook/olog/ui/LogEntryTableViewController.java | 8 +++++++- .../org/phoebus/logbook/olog/ui/LogEntryDisplay.fxml | 4 ++++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryDisplayController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryDisplayController.java index a259a46ee8..c09da16980 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryDisplayController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryDisplayController.java @@ -116,6 +116,16 @@ public void newLogEntry(){ new LogEntryEditorStage(new OlogLog(), null, null).show(); } + @FXML + public void back() { + logEntryTableViewController.undoAndRedoActions.performUndo(); + } + + @FXML + public void forward() { + logEntryTableViewController.undoAndRedoActions.performRedo(); + } + public void setLogEntry(LogEntry logEntry) { if(logEntry == null){ currentViewProperty.set(EMPTY); diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java index a2d506abcb..bca8fc05d7 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTable.java @@ -49,8 +49,8 @@ public LogEntryTable(final LogEntryTableApp app) { try { if (app.getClient() != null) { if (clazz.isAssignableFrom(LogEntryTableViewController.class)) { - return clazz.getConstructor(LogClient.class, OlogQueryManager.class, SearchParameters.class) - .newInstance(app.getClient(), ologQueryManager, searchParameters); + return clazz.getConstructor(LogClient.class, OlogQueryManager.class, SearchParameters.class, UndoAndRedoActions.class) + .newInstance(app.getClient(), ologQueryManager, searchParameters, undoAndRedoActions); } else if (clazz.isAssignableFrom(AdvancedSearchViewController.class)) { return clazz.getConstructor(LogClient.class, SearchParameters.class) .newInstance(app.getClient(), searchParameters); diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java index 162fe481b5..daa02771b8 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryTableViewController.java @@ -105,10 +105,14 @@ public class LogEntryTableViewController extends LogbookSearchController { * * @param logClient Log client implementation */ - public LogEntryTableViewController(LogClient logClient, OlogQueryManager ologQueryManager, SearchParameters searchParameters) { + public LogEntryTableViewController(LogClient logClient, + OlogQueryManager ologQueryManager, + SearchParameters searchParameters, + LogEntryTable.UndoAndRedoActions undoAndRedoActions) { setClient(logClient); this.ologQueryManager = ologQueryManager; this.searchParameters = searchParameters; + this.undoAndRedoActions = undoAndRedoActions; } private final SimpleIntegerProperty hitCountProperty = new SimpleIntegerProperty(0); @@ -121,6 +125,8 @@ public LogEntryTableViewController(LogClient logClient, OlogQueryManager ologQue private final SearchParameters searchParameters; + protected final LogEntryTable.UndoAndRedoActions undoAndRedoActions; + @FXML public void initialize() { diff --git a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryDisplay.fxml b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryDisplay.fxml index 6f84534947..09e02fa6cb 100644 --- a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryDisplay.fxml +++ b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryDisplay.fxml @@ -32,6 +32,10 @@