diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/AdvancedSearchViewController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/AdvancedSearchViewController.java index e97816cd58..19c23d32e2 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/AdvancedSearchViewController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/AdvancedSearchViewController.java @@ -24,6 +24,7 @@ import javafx.beans.value.ObservableValue; import javafx.fxml.FXML; import javafx.geometry.Pos; +import javafx.scene.Node; import javafx.scene.control.Button; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; @@ -113,10 +114,11 @@ public class AdvancedSearchViewController { private final SimpleBooleanProperty requireAttachments = new SimpleBooleanProperty(false); private Runnable searchCallback = () -> { - throw new IllegalStateException("Search callback is not set on AdvancedSearchViewConroller!"); + throw new IllegalStateException("Search callback is not set on AdvancedSearchViewController!"); }; - public AdvancedSearchViewController(LogClient logClient, SearchParameters searchParameters) { + public AdvancedSearchViewController(LogClient logClient, + SearchParameters searchParameters) { this.logClient = logClient; this.searchParameters = searchParameters; } diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java index 33e7c45aeb..9c20464e57 100644 --- a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/LogEntryCellController.java @@ -6,6 +6,7 @@ import javafx.scene.control.Label; import javafx.scene.image.Image; import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.layout.VBox; import org.commonmark.Extension; @@ -16,9 +17,11 @@ import org.phoebus.logbook.Logbook; import org.phoebus.logbook.Tag; import org.phoebus.logbook.olog.ui.LogEntryTableViewController.TableViewListItem; +import org.phoebus.logbook.olog.ui.spi.Decoration; import org.phoebus.ui.javafx.ImageCache; import java.util.Arrays; +import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; @@ -40,11 +43,16 @@ public class LogEntryCellController { @FXML VBox root; + @FXML + VBox logEntryCell; + @FXML Label time; @FXML Label owner; @FXML + HBox decorationNodes; + @FXML Label title; @FXML Label logbooks; @@ -74,7 +82,6 @@ public class LogEntryCellController { private SimpleBooleanProperty expanded = new SimpleBooleanProperty(true); - public LogEntryCellController() { List extensions = Arrays.asList(TablesExtension.create(), ImageAttributesExtension.create()); @@ -92,8 +99,20 @@ public void initialize() { conversationIcon.setImage(conversation); } - @FXML + private List decorations = new LinkedList<>(); + + public void setDecorations(List decorations) { + this.decorations = decorations; + } + public void refresh() { + + decorationNodes.getChildren().clear(); + for (Decoration decoration : decorations) { + Node decorationNode = decoration.createDecorationForLogEntryCell(logEntry.getLogEntry()); + decorationNodes.getChildren().add(decorationNode); + } + if (logEntry != null) { time.setText(SECONDS_FORMAT.format(logEntry.getLogEntry().getCreatedDate())); @@ -126,8 +145,8 @@ public void refresh() { logEntryId.setText(logEntry.getLogEntry().getId() != null ? logEntry.getLogEntry().getId().toString() : ""); level.setText(logEntry.getLogEntry().getLevel()); - } + } public void setLogEntry(TableViewListItem logEntry) { 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 6993b63859..ab6f5882cb 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 @@ -11,6 +11,7 @@ import org.phoebus.logbook.LogClient; import org.phoebus.logbook.LogEntry; import org.phoebus.logbook.olog.ui.query.OlogQueryManager; +import org.phoebus.logbook.olog.ui.spi.Decoration; import org.phoebus.logbook.olog.ui.write.AttachmentsEditorController; import org.phoebus.ui.dialog.ExceptionDetailsErrorDialog; import org.phoebus.ui.docking.DockItem; @@ -18,8 +19,11 @@ import java.io.IOException; import java.net.URI; +import java.util.LinkedList; +import java.util.List; import java.util.Optional; import java.util.ResourceBundle; +import java.util.ServiceLoader; import java.util.logging.Level; import java.util.logging.Logger; @@ -32,10 +36,18 @@ public class LogEntryTable implements AppInstance { private LogEntryTableViewController controller; public GoBackAndGoForwardActions goBackAndGoForwardActions; - public LogEntryTable(final LogEntryTableApp app) { this.app = app; goBackAndGoForwardActions = new GoBackAndGoForwardActions(); + + List decorations = new LinkedList<>(); + { + ServiceLoader decorationClasses = ServiceLoader.load(Decoration.class); + for (Decoration decoration : decorationClasses) { + decorations.add(decoration); + } + } + try { OlogQueryManager ologQueryManager = OlogQueryManager.getInstance(); SearchParameters searchParameters = new SearchParameters(); @@ -51,10 +63,10 @@ public LogEntryTable(final LogEntryTableApp app) { if (clazz.isAssignableFrom(LogEntryTableViewController.class)) { LogEntryTableViewController logEntryTableViewController = (LogEntryTableViewController) clazz.getConstructor(LogClient.class, OlogQueryManager.class, SearchParameters.class).newInstance(app.getClient(), ologQueryManager, searchParameters); logEntryTableViewController.setGoBackAndGoForwardActions(goBackAndGoForwardActions); + logEntryTableViewController.setDecorations(decorations); return logEntryTableViewController; } else if (clazz.isAssignableFrom(AdvancedSearchViewController.class)) { - return clazz.getConstructor(LogClient.class, SearchParameters.class) - .newInstance(app.getClient(), searchParameters); + return clazz.getConstructor(LogClient.class, SearchParameters.class).newInstance(app.getClient(), searchParameters); } else if (clazz.isAssignableFrom(SingleLogEntryDisplayController.class)) { SingleLogEntryDisplayController singleLogEntryDisplayController = (SingleLogEntryDisplayController) clazz.getConstructor(LogClient.class).newInstance(app.getClient()); singleLogEntryDisplayController.setSelectLogEntryInUI(id -> goBackAndGoForwardActions.loadLogEntryWithID(id)); 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 0fe7b1b4cc..edeb6dc5ff 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 @@ -14,8 +14,22 @@ import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.Node; -import javafx.scene.control.*; +import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.ComboBox; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.control.MenuItem; +import javafx.scene.control.Pagination; +import javafx.scene.control.ProgressIndicator; +import javafx.scene.control.SelectionMode; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.TextField; +import javafx.scene.control.TreeView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; @@ -26,9 +40,15 @@ import javafx.util.Duration; import javafx.util.StringConverter; import org.phoebus.framework.jobs.JobManager; -import org.phoebus.logbook.*; +import org.phoebus.logbook.LogClient; +import org.phoebus.logbook.LogEntry; +import org.phoebus.logbook.LogService; +import org.phoebus.logbook.LogbookException; +import org.phoebus.logbook.LogbookPreferences; +import org.phoebus.logbook.SearchResult; import org.phoebus.logbook.olog.ui.query.OlogQuery; import org.phoebus.logbook.olog.ui.query.OlogQueryManager; +import org.phoebus.logbook.olog.ui.spi.Decoration; import org.phoebus.logbook.olog.ui.write.LogEntryEditorStage; import org.phoebus.logbook.olog.ui.write.LogEntryUpdateStage; import org.phoebus.olog.es.api.model.LogGroupProperty; @@ -207,6 +227,10 @@ public void initialize() { descriptionCol.setMaxWidth(1f * Integer.MAX_VALUE * 100); descriptionCol.setCellValueFactory(col -> new SimpleObjectProperty<>(col.getValue())); descriptionCol.setCellFactory(col -> new TableCell<>() { + { + setStyle("-fx-padding: -1px"); + } + private final Node graphic; private final PseudoClass childlessTopLevel = PseudoClass.getPseudoClass("grouped"); @@ -217,6 +241,7 @@ public void initialize() { FXMLLoader loader = new FXMLLoader(getClass().getResource("LogEntryCell.fxml")); graphic = loader.load(); controller = loader.getController(); + controller.setDecorations(decorations); } catch (IOException exc) { throw new RuntimeException(exc); } @@ -374,8 +399,20 @@ public void setLogs(List logs) { throw new RuntimeException(new UnsupportedOperationException()); } + private List decorations; + protected void setDecorations(List decorations) { + this.decorations = decorations; + for (Decoration decoration : decorations) { + decoration.setRefreshLogEntryTableView(() -> refresh()); + } + } + private void setSearchResult(SearchResult searchResult) { this.searchResult = searchResult; + + List logEntries = searchResult.getLogs(); + decorations.forEach(decoration -> decoration.setLogEntries(logEntries)); + Platform.runLater(() -> { hitCountProperty.set(searchResult.getHitCount()); pageCountProperty.set(1 + (hitCountProperty.get() / pageSizeProperty.get())); @@ -392,12 +429,19 @@ public String getQuery() { return query.getValue().getQuery(); } - private void refresh() { + private synchronized void refresh() { if (this.searchResult != null) { List selectedLogEntries = new ArrayList<>(tableView.getSelectionModel().getSelectedItems()); - ObservableList logsList = FXCollections.observableArrayList(); - logsList.addAll(searchResult.getLogs().stream().map(le -> new TableViewListItem(le, showDetails.get())).toList()); + + List logEntries = searchResult.getLogs(); + logEntries.sort((o1, o2) -> -(o1.getCreatedDate().compareTo(o2.getCreatedDate()))); + + boolean showDetailsBoolean = showDetails.get(); + var logs = logEntries.stream().map(le -> new TableViewListItem(le, showDetailsBoolean)).toList(); + + ObservableList logsList = FXCollections.observableArrayList(logs); tableView.setItems(logsList); + // This will ensure that selected entries stay selected after the list has been // updated from the search result. for (TableViewListItem selectedItem : selectedLogEntries) { @@ -408,8 +452,7 @@ private void refresh() { goBackAndGoForwardActions.get().setIsRecordingHistoryDisabled(true); // Do not create a "Back" action for the automatic reload. tableView.getSelectionModel().select(item); goBackAndGoForwardActions.get().setIsRecordingHistoryDisabled(false); - } - else { + } else { tableView.getSelectionModel().select(item); } }); @@ -562,14 +605,15 @@ protected void setLogEntry(LogEntry 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 * anyway to indicate that user selected log entry is not visible in {@link TreeView}. + * * @param logEntry User selected log entry. * @return true if user selected log entry is present in {@link TreeView}, otherwise * false. */ - public boolean selectLogEntry(LogEntry logEntry){ + public boolean selectLogEntry(LogEntry logEntry) { tableView.getSelectionModel().clearSelection(); Optional optional = tableView.getItems().stream().filter(i -> i.getLogEntry().getId().equals(logEntry.getId())).findFirst(); - if(optional.isPresent()){ + if (optional.isPresent()) { tableView.getSelectionModel().select(optional.get()); return true; } diff --git a/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/spi/Decoration.java b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/spi/Decoration.java new file mode 100644 index 0000000000..598b4f31f2 --- /dev/null +++ b/app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/spi/Decoration.java @@ -0,0 +1,14 @@ +package org.phoebus.logbook.olog.ui.spi; + +import javafx.scene.Node; +import org.phoebus.logbook.LogEntry; + +import java.util.List; + +public interface Decoration { + void setLogEntries(List logEntries); + + void setRefreshLogEntryTableView(Runnable refreshLogEntryTableView); + + Node createDecorationForLogEntryCell(LogEntry logEntry); +} diff --git a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/AdvancedSearchView.fxml b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/AdvancedSearchView.fxml index c1e8ceb922..848b9ab321 100644 --- a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/AdvancedSearchView.fxml +++ b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/AdvancedSearchView.fxml @@ -121,7 +121,6 @@ - diff --git a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryCell.fxml b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryCell.fxml index c2e0360d82..a906f9d067 100644 --- a/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryCell.fxml +++ b/app/logbook/olog/ui/src/main/resources/org/phoebus/logbook/olog/ui/LogEntryCell.fxml @@ -12,75 +12,90 @@ summary of the log entry and its metadata. - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + +