From 25e7c8a0ea92a479991cf665d10d9ccda26b5690 Mon Sep 17 00:00:00 2001 From: 0xboobface <0xboobface@gmail.com> Date: Fri, 5 Apr 2019 13:40:08 +0200 Subject: [PATCH] Add filter input to filter by model name and url --- .../java/ctbrec/ui/RecordedModelsTab.java | 131 ++++++++++++++---- 1 file changed, 104 insertions(+), 27 deletions(-) diff --git a/client/src/main/java/ctbrec/ui/RecordedModelsTab.java b/client/src/main/java/ctbrec/ui/RecordedModelsTab.java index a751084b..130ac211 100644 --- a/client/src/main/java/ctbrec/ui/RecordedModelsTab.java +++ b/client/src/main/java/ctbrec/ui/RecordedModelsTab.java @@ -13,6 +13,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; import java.util.stream.Collectors; @@ -31,6 +32,7 @@ import ctbrec.ui.action.PlayAction; import ctbrec.ui.action.ResumeAction; import ctbrec.ui.action.StopRecordingAction; import ctbrec.ui.controls.AutoFillTextField; +import ctbrec.ui.controls.SearchBox; import javafx.beans.property.SimpleStringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -38,6 +40,7 @@ import javafx.concurrent.ScheduledService; import javafx.concurrent.Task; import javafx.event.ActionEvent; import javafx.geometry.Insets; +import javafx.geometry.Pos; import javafx.scene.control.Alert; import javafx.scene.control.Button; import javafx.scene.control.ContextMenu; @@ -50,6 +53,7 @@ import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn.SortType; import javafx.scene.control.TableRow; import javafx.scene.control.TableView; +import javafx.scene.control.TextField; import javafx.scene.control.Tooltip; import javafx.scene.control.cell.CheckBoxTableCell; import javafx.scene.control.cell.PropertyValueFactory; @@ -63,11 +67,13 @@ import javafx.scene.input.MouseEvent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.FlowPane; import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; import javafx.util.Duration; public class RecordedModelsTab extends Tab implements TabSelectionListener { private static final transient Logger LOG = LoggerFactory.getLogger(RecordedModelsTab.class); + private ReentrantLock lock = new ReentrantLock(); private ScheduledService> updateService; private Recorder recorder; private List sites; @@ -76,6 +82,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { ScrollPane scrollPane = new ScrollPane(); TableView table = new TableView(); ObservableList observableModels = FXCollections.observableArrayList(); + ObservableList filteredModels = FXCollections.observableArrayList(); ContextMenu popup; Label modelLabel = new Label("Model"); @@ -83,6 +90,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { Button addModelButton = new Button("Record"); Button pauseAll = new Button("Pause All"); Button resumeAll = new Button("Resume All"); + TextField filter; public RecordedModelsTab(String title, Recorder recorder, List sites) { super(title); @@ -196,6 +204,27 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { pauseAll.setOnAction(this::pauseAll); resumeAll.setOnAction(this::resumeAll); + HBox filterContainer = new HBox(); + filterContainer.setSpacing(0); + filterContainer.setPadding(new Insets(0)); + filterContainer.setAlignment(Pos.CENTER_RIGHT); + HBox.setHgrow(filterContainer, Priority.ALWAYS); + filter = new SearchBox(false); + filter.setPromptText("Filter"); + filter.textProperty().addListener( (observableValue, oldValue, newValue) -> { + String q = filter.getText(); + lock.lock(); + try { + filter(q); + } finally { + lock.unlock(); + } + }); + filter.getStyleClass().remove("search-box-icon"); + filterContainer.getChildren().add(filter); + addModelBox.getChildren().add(filterContainer); + + BorderPane root = new BorderPane(); root.setPadding(new Insets(5)); root.setTop(addModelBox); @@ -295,37 +324,44 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { return; } - for (JavaFxModel updatedModel : models) { - int index = observableModels.indexOf(updatedModel); - if (index == -1) { - observableModels.add(updatedModel); - updatedModel.getPausedProperty().addListener((obs, oldV, newV) -> { - if (newV) { - if(!recorder.isSuspended(updatedModel)) { - pauseRecording(Collections.singletonList(updatedModel)); + lock.lock(); + try { + for (JavaFxModel updatedModel : models) { + int index = observableModels.indexOf(updatedModel); + if (index == -1) { + observableModels.add(updatedModel); + updatedModel.getPausedProperty().addListener((obs, oldV, newV) -> { + if (newV) { + if(!recorder.isSuspended(updatedModel)) { + pauseRecording(Collections.singletonList(updatedModel)); + } + } else { + if(recorder.isSuspended(updatedModel)) { + resumeRecording(Collections.singletonList(updatedModel)); + } } - } else { - if(recorder.isSuspended(updatedModel)) { - resumeRecording(Collections.singletonList(updatedModel)); - } - } - }); - } else { - // make sure to update the JavaFX online property, so that the table cell is updated - JavaFxModel oldModel = observableModels.get(index); - oldModel.setSuspended(updatedModel.isSuspended()); - oldModel.getOnlineProperty().set(updatedModel.getOnlineProperty().get()); - oldModel.getRecordingProperty().set(updatedModel.getRecordingProperty().get()); - } - } - - for (Iterator iterator = observableModels.iterator(); iterator.hasNext();) { - Model model = iterator.next(); - if (!models.contains(model)) { - iterator.remove(); + }); + } else { + // make sure to update the JavaFX online property, so that the table cell is updated + JavaFxModel oldModel = observableModels.get(index); + oldModel.setSuspended(updatedModel.isSuspended()); + oldModel.getOnlineProperty().set(updatedModel.getOnlineProperty().get()); + oldModel.getRecordingProperty().set(updatedModel.getRecordingProperty().get()); + } } + + for (Iterator iterator = observableModels.iterator(); iterator.hasNext();) { + Model model = iterator.next(); + if (!models.contains(model)) { + iterator.remove(); + } + } + } finally { + lock.unlock(); } + filteredModels.clear(); + filter(filter.getText()); table.sort(); }); updateService.setOnFailed((event) -> { @@ -333,6 +369,47 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { }); } + private void filter(String filter) { + lock.lock(); + try { + if (StringUtil.isBlank(filter)) { + observableModels.addAll(filteredModels); + filteredModels.clear(); + return; + } + + String[] tokens = filter.split(" "); + observableModels.addAll(filteredModels); + filteredModels.clear(); + for (int i = 0; i < table.getItems().size(); i++) { + StringBuilder sb = new StringBuilder(); + for (TableColumn tc : table.getColumns()) { + Object cellData = tc.getCellData(i); + if(cellData != null) { + String content = cellData.toString(); + sb.append(content).append(' '); + } + } + String searchText = sb.toString(); + + boolean tokensMissing = false; + for (String token : tokens) { + if(!searchText.toLowerCase().contains(token.toLowerCase())) { + tokensMissing = true; + break; + } + } + if(tokensMissing) { + JavaFxModel model = table.getItems().get(i); + filteredModels.add(model); + } + } + observableModels.removeAll(filteredModels); + } finally { + lock.unlock(); + } + } + private ScheduledService> createUpdateService() { ScheduledService> updateService = new ScheduledService>() { @Override