From 31c9a38d06aa6a8c816470078868f2b143eac755 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sun, 18 Jul 2021 14:00:38 +0200 Subject: [PATCH] Use ModelMenuContributor for context menu on the RecordedModelsTab --- .../ctbrec/ui/action/AddToGroupAction.java | 16 +- .../ctbrec/ui/action/EditNotesAction.java | 16 +- .../ctbrec/ui/action/SetStopDateAction.java | 6 + .../ctbrec/ui/menu/ModelMenuContributor.java | 148 ++++++++++++-- .../ui/tabs/recorded/RecordLaterTab.java | 10 +- .../ui/tabs/recorded/RecordedModelsTab.java | 185 +----------------- 6 files changed, 173 insertions(+), 208 deletions(-) diff --git a/client/src/main/java/ctbrec/ui/action/AddToGroupAction.java b/client/src/main/java/ctbrec/ui/action/AddToGroupAction.java index f903cf83..d1cea603 100644 --- a/client/src/main/java/ctbrec/ui/action/AddToGroupAction.java +++ b/client/src/main/java/ctbrec/ui/action/AddToGroupAction.java @@ -30,13 +30,13 @@ import javafx.scene.layout.Region; public class AddToGroupAction { private Node source; - private Model model; + private List models; private Recorder recorder; - public AddToGroupAction(Node source, Recorder recorder, Model model) { + public AddToGroupAction(Node source, Recorder recorder, List models) { this.source = source; this.recorder = recorder; - this.model = model; + this.models = models; } public void execute() { @@ -56,13 +56,17 @@ public class AddToGroupAction { Set modelGroups = recorder.getModelGroups(); Optional existingGroup = modelGroups.stream().filter(mg -> mg.getName().equalsIgnoreCase(text)).findFirst(); if (existingGroup.isPresent()) { - existingGroup.get().add(model); + for (Model model : models) { + existingGroup.get().add(model); + } recorder.saveModelGroup(existingGroup.get()); } else { var group = new ModelGroup(); group.setId(UUID.randomUUID()); group.setName(text); - group.add(model); + for (Model model : models) { + group.add(model); + } modelGroups.add(group); recorder.saveModelGroup(group); } @@ -122,7 +126,7 @@ public class AddToGroupAction { } private void suggestInitialName(Set modelGroups) { - String bestName = model.getDisplayName(); + String bestName = models.get(0).getDisplayName(); for (ModelGroup modelGroup : modelGroups) { if (StringUtil.percentageOfEquality(bestName, modelGroup.getName()) > 70) { bestName = modelGroup.getName(); diff --git a/client/src/main/java/ctbrec/ui/action/EditNotesAction.java b/client/src/main/java/ctbrec/ui/action/EditNotesAction.java index 0a7772c3..8fcfcee9 100644 --- a/client/src/main/java/ctbrec/ui/action/EditNotesAction.java +++ b/client/src/main/java/ctbrec/ui/action/EditNotesAction.java @@ -8,23 +8,21 @@ import org.slf4j.LoggerFactory; import ctbrec.Config; import ctbrec.Model; -import ctbrec.ui.JavaFxModel; import ctbrec.ui.controls.Dialogs; import javafx.scene.Cursor; import javafx.scene.Node; -import javafx.scene.control.TableView; public class EditNotesAction { private static final Logger LOG = LoggerFactory.getLogger(EditNotesAction.class); private Node source; private Model model; - private TableView table; + private Runnable callback; - public EditNotesAction(Node source, Model selectedModel, TableView table) { + public EditNotesAction(Node source, Model selectedModel, Runnable callback) { this.source = source; this.model = selectedModel; - this.table = table; + this.callback = callback; } public void execute() { @@ -43,7 +41,13 @@ public class EditNotesAction { LOG.warn("Couldn't save config", e); } }); - table.refresh(); + if (callback != null) { + try { + callback.run(); + } catch (Exception e) { + LOG.error("Error while executing callback", e); + } + } source.setCursor(Cursor.DEFAULT); } } diff --git a/client/src/main/java/ctbrec/ui/action/SetStopDateAction.java b/client/src/main/java/ctbrec/ui/action/SetStopDateAction.java index df183fad..ef85a32d 100644 --- a/client/src/main/java/ctbrec/ui/action/SetStopDateAction.java +++ b/client/src/main/java/ctbrec/ui/action/SetStopDateAction.java @@ -77,6 +77,12 @@ public class SetStopDateAction { model.setRecordUntil(stopAt); model.setRecordUntilSubsequentAction(action); try { + try { + Thread.sleep(1000); + } catch (InterruptedException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } recorder.stopRecordingAt(model); } catch (InvalidKeyException | NoSuchAlgorithmException | IOException e) { Dialogs.showError(source.getScene(), "Error", "Couln't set stop date", e); diff --git a/client/src/main/java/ctbrec/ui/menu/ModelMenuContributor.java b/client/src/main/java/ctbrec/ui/menu/ModelMenuContributor.java index 86649713..7fbdd5f9 100644 --- a/client/src/main/java/ctbrec/ui/menu/ModelMenuContributor.java +++ b/client/src/main/java/ctbrec/ui/menu/ModelMenuContributor.java @@ -1,8 +1,12 @@ package ctbrec.ui.menu; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.function.Consumer; import org.slf4j.Logger; @@ -18,9 +22,11 @@ import ctbrec.ui.DesktopIntegration; import ctbrec.ui.SiteUiFactory; import ctbrec.ui.StreamSourceSelectionDialog; import ctbrec.ui.action.AddToGroupAction; +import ctbrec.ui.action.EditNotesAction; import ctbrec.ui.action.IgnoreModelsAction; import ctbrec.ui.action.OpenRecordingsDir; import ctbrec.ui.action.PlayAction; +import ctbrec.ui.action.RemoveTimeLimitAction; import ctbrec.ui.action.SetStopDateAction; import ctbrec.ui.action.StartRecordingAction; import ctbrec.ui.action.StopRecordingAction; @@ -35,6 +41,7 @@ import javafx.scene.control.Alert; import javafx.scene.control.ContextMenu; import javafx.scene.control.MenuItem; import javafx.scene.control.SeparatorMenuItem; +import javafx.scene.control.TabPane; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; @@ -52,6 +59,7 @@ public class ModelMenuContributor { private TriConsumer followCallback; private Consumer ignoreCallback; private boolean removeWithIgnore = false; + private Runnable callback; private ModelMenuContributor(Node source, Config config, Recorder recorder) { this.source = source; @@ -87,21 +95,39 @@ public class ModelMenuContributor { startStopCallback = Optional.ofNullable(startStopCallback).orElse(m -> {}); followCallback = Optional.ofNullable(followCallback).orElse((m, f, s) -> {}); ignoreCallback = Optional.ofNullable(ignoreCallback).orElse(m -> {}); + callback = Optional.ofNullable(callback).orElse(() -> {}); addOpenInPlayer(menu, selectedModels); addOpenInBrowser(menu, selectedModels); addCopyUrl(menu, selectedModels); menu.getItems().add(new SeparatorMenuItem()); + addStartOrStop(menu, selectedModels); addStartRecordingWithTimeLimit(menu, selectedModels); + addRemoveTimeLimit(menu, selectedModels); + addSwitchStreamSource(menu, selectedModels); addStartPaused(menu, selectedModels); addRecordLater(menu, selectedModels); addPauseResume(menu, selectedModels); addGroupMenu(menu, selectedModels); menu.getItems().add(new SeparatorMenuItem()); + addFollowUnfollow(menu, selectedModels); addSendTip(menu, selectedModels); addIgnore(menu, selectedModels); addOpenRecDir(menu, selectedModels); + addNotes(menu, selectedModels); + } + + public ModelMenuContributor afterwards(Runnable callback) { + this.callback = callback; + return this; + } + + private void addNotes(ContextMenu menu, List selectedModels) { + var notes = new MenuItem("Notes"); + notes.setDisable(selectedModels.size() != 1); + notes.setOnAction(e -> new EditNotesAction(source, selectedModels.get(0), callback).execute()); + menu.getItems().add(notes); } private void addOpenRecDir(ContextMenu menu, List selectedModels) { @@ -110,7 +136,11 @@ public class ModelMenuContributor { } var model = selectedModels.get(0); var openRecDir = new MenuItem("Open recording directory"); - openRecDir.setOnAction(e -> new OpenRecordingsDir(source, model).execute()); + openRecDir.setDisable(selectedModels.size() != 1); + openRecDir.setOnAction(e -> { + new OpenRecordingsDir(source, model).execute(); + executeCallback(); + }); menu.getItems().add(openRecDir); } @@ -124,26 +154,28 @@ public class ModelMenuContributor { if (selectedModels == null || selectedModels.isEmpty()) { return; } - var model = selectedModels.get(0); var copyUrl = new MenuItem("Copy URL"); copyUrl.setOnAction(e -> { + var sb = new StringBuilder(); + for (Model model : selectedModels) { + sb.append(model.getUrl()).append('\n'); + } + sb.deleteCharAt(sb.length() - 1); final var content = new ClipboardContent(); - content.putString(model.getUrl()); + content.putString(sb.toString()); Clipboard.getSystemClipboard().setContent(content); }); menu.getItems().add(copyUrl); } private void addSendTip(ContextMenu menu, List selectedModels) { - if (selectedModels == null || selectedModels.isEmpty()) { - return; - } var model = selectedModels.get(0); var site = model.getSite(); if (site.supportsTips()) { var sendTip = new MenuItem("Send Tip"); sendTip.setOnAction(e -> new TipAction(model, source).execute()); sendTip.setDisable(!site.credentialsAvailable()); + sendTip.setDisable(selectedModels.size() != 1); menu.getItems().add(sendTip); } } @@ -166,13 +198,21 @@ public class ModelMenuContributor { var unfollow = new MenuItem("Unfollow"); unfollow.setOnAction(e -> follow(selectedModels, false)); - var followOrUnFollow = (source instanceof FollowedTab) ? unfollow : follow; // TODO source instanceof FollowedTab does not work + var followOrUnFollow = isFollowedTab() ? unfollow : follow; followOrUnFollow.setDisable(!site.credentialsAvailable()); menu.getItems().add(followOrUnFollow); followOrUnFollow.setDisable(!site.credentialsAvailable()); } } + private boolean isFollowedTab() { + if (source instanceof TabPane) { + var tabPane = (TabPane) source; + return tabPane.getSelectionModel().getSelectedItem() instanceof FollowedTab; + } + return false; + } + protected void follow(List selectedModels, boolean follow) { for (Model model : selectedModels) { follow(model, follow); @@ -219,11 +259,71 @@ public class ModelMenuContributor { }, GlobalThreadPool.get()); } + private void addSwitchStreamSource(ContextMenu menu, List selectedModels) { + var model = selectedModels.get(0); + if (!recorder.isTracked(model)) { + return; + } + var switchStreamSource = new MenuItem("Switch resolution"); + switchStreamSource.setOnAction(e -> switchStreamSource(selectedModels.get(0))); + menu.getItems().add(switchStreamSource); + switchStreamSource.setDisable(selectedModels.size() != 1); + } + + private void switchStreamSource(Model selectedModel) { + var couldntSwitchHeaderText = "Couldn't switch stream resolution"; + + try { + if (!selectedModel.isOnline(true)) { + Dialogs.showError(source.getScene(), couldntSwitchHeaderText, "The resolution can only be changed, when the model is online", null); + return; + } + } catch (InterruptedException e1) { + Thread.currentThread().interrupt(); + Dialogs.showError(source.getScene(), couldntSwitchHeaderText, "An error occured while checking, if the model is online", null); + return; + } catch (IOException | ExecutionException e1) { + Dialogs.showError(source.getScene(), couldntSwitchHeaderText, "An error occured while checking, if the model is online", null); + return; + } + + Consumer onSuccess = m -> { + try { + recorder.switchStreamSource(m); + } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException e) { + LOG.error(couldntSwitchHeaderText, e); + showStreamSwitchErrorDialog(e); + } + }; + Consumer onFail = t -> { + LOG.error(couldntSwitchHeaderText, t); + showStreamSwitchErrorDialog(t); + }; + StreamSourceSelectionDialog.show(source.getScene(), selectedModel, onSuccess, onFail); + } + + private void showStreamSwitchErrorDialog(Throwable throwable) { + showErrorDialog(throwable, "Couldn't switch stream resolution", "Error while switching stream resolution"); + } + + private void showErrorDialog(Throwable throwable, String header, String msg) { + Alert alert = new AutosizeAlert(Alert.AlertType.ERROR, source.getScene()); + alert.setTitle("Error"); + alert.setHeaderText(header); + alert.setContentText(msg + ": " + throwable.getLocalizedMessage()); + alert.showAndWait(); + } + private void addGroupMenu(ContextMenu menu, List selectedModels) { var model = selectedModels.get(0); var addToGroup = new MenuItem("Add to group"); - addToGroup.setOnAction(e -> new AddToGroupAction(source, recorder, model).execute()); - var groupSubMenu = new ModelGroupMenuBuilder().model(model).recorder(recorder).node(source).build(); + addToGroup.setOnAction(e -> new AddToGroupAction(source, recorder, selectedModels).execute(callback)); + var groupSubMenu = new ModelGroupMenuBuilder() // @formatter:off + .model(model) + .recorder(recorder) + .node(source) + .callback(m -> callback.run()) + .build(); // @formatter:on Optional modelGroup = recorder.getModelGroup(model); menu.getItems().add(modelGroup.isEmpty() ? addToGroup : groupSubMenu); } @@ -279,15 +379,33 @@ public class ModelMenuContributor { } private void addStartRecordingWithTimeLimit(ContextMenu menu, List selectedModels) { - var text = recorder.isTracked(selectedModels.get(0)) ? "Record Until" : "Start Recording Until"; + var model = selectedModels.get(0); + var text = recorder.isTracked(model) ? "Record Until" : "Start Recording Until"; var start = new MenuItem(text); menu.getItems().add(start); start.setOnAction(e -> { startStopAction(selectedModels, true); - selectedModels.forEach(m -> new SetStopDateAction(source, m, recorder).execute()); + selectedModels.forEach(m -> new SetStopDateAction(source, m, recorder).execute() // + .thenAccept(b -> executeCallback())); }); } + private void addRemoveTimeLimit(ContextMenu menu, List selectedModels) { + var model = selectedModels.get(0); + if (!model.isRecordingTimeLimited()) { + return; + } + var removeTimeLimit = new MenuItem("Remove Time Limit"); + removeTimeLimit.setOnAction(e -> removeTimeLimit(model)); + menu.getItems().add(removeTimeLimit); + } + + private void removeTimeLimit(Model selectedModel) { + new RemoveTimeLimitAction(source, selectedModel, recorder) // + .execute() // + .whenComplete((result, exception) -> executeCallback()); + } + private void addOpenInPlayer(ContextMenu menu, List selectedModels) { var openInPlayer = new MenuItem("Open in Player"); openInPlayer.setOnAction(e -> selectedModels.forEach(m -> new PlayAction(source, m).execute())); @@ -343,4 +461,12 @@ public class ModelMenuContributor { private void stopRecording(List models) { new StopRecordingAction(source, models, recorder).execute(startStopCallback); } + + private void executeCallback() { + try { + callback.run(); + } catch (Exception e) { + LOG.error("Error while executing menu callback", e); + } + } } diff --git a/client/src/main/java/ctbrec/ui/tabs/recorded/RecordLaterTab.java b/client/src/main/java/ctbrec/ui/tabs/recorded/RecordLaterTab.java index a5f85da7..3844acff 100644 --- a/client/src/main/java/ctbrec/ui/tabs/recorded/RecordLaterTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/recorded/RecordLaterTab.java @@ -26,7 +26,6 @@ import ctbrec.ui.DesktopIntegration; import ctbrec.ui.JavaFxModel; import ctbrec.ui.PreviewPopupHandler; import ctbrec.ui.action.CheckModelAccountAction; -import ctbrec.ui.action.EditNotesAction; import ctbrec.ui.action.FollowAction; import ctbrec.ui.action.IgnoreModelsAction; import ctbrec.ui.action.PlayAction; @@ -501,16 +500,13 @@ public class RecordLaterTab extends Tab implements TabSelectionListener { follow.setDisable(!selectedModels.stream().allMatch(m -> m.getSite().supportsFollow() && m.getSite().credentialsAvailable())); var ignore = new MenuItem("Ignore"); ignore.setOnAction(e -> ignore(selectedModels)); - var notes = new MenuItem("Notes"); - notes.setOnAction(e -> notes(selectedModels)); - ContextMenu menu = new CustomMouseBehaviorContextMenu(start, stop, copyUrl, openInPlayer, openInBrowser, follow, notes, ignore); + ContextMenu menu = new CustomMouseBehaviorContextMenu(start, stop, copyUrl, openInPlayer, openInBrowser, follow, ignore); if (selectedModels.size() > 1) { copyUrl.setDisable(true); openInPlayer.setDisable(true); openInBrowser.setDisable(true); - notes.setDisable(true); } return menu; @@ -524,10 +520,6 @@ public class RecordLaterTab extends Tab implements TabSelectionListener { new FollowAction(getTabPane(), new ArrayList<>(selectedModels)).execute(); } - private void notes(ObservableList selectedModels) { - new EditNotesAction(getTabPane(), selectedModels.get(0), table).execute(); - } - private void openInPlayer(JavaFxModel selectedModel) { new PlayAction(getTabPane(), selectedModel).execute(); } diff --git a/client/src/main/java/ctbrec/ui/tabs/recorded/RecordedModelsTab.java b/client/src/main/java/ctbrec/ui/tabs/recorded/RecordedModelsTab.java index eb125c2e..efa52a03 100644 --- a/client/src/main/java/ctbrec/ui/tabs/recorded/RecordedModelsTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/recorded/RecordedModelsTab.java @@ -6,18 +6,15 @@ import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.time.Instant; -import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -34,20 +31,10 @@ import ctbrec.sites.Site; import ctbrec.ui.AutosizeAlert; import ctbrec.ui.JavaFxModel; import ctbrec.ui.PreviewPopupHandler; -import ctbrec.ui.StreamSourceSelectionDialog; -import ctbrec.ui.action.AddToGroupAction; import ctbrec.ui.action.CheckModelAccountAction; -import ctbrec.ui.action.EditNotesAction; -import ctbrec.ui.action.FollowAction; -import ctbrec.ui.action.IgnoreModelsAction; -import ctbrec.ui.action.OpenRecordingsDir; -import ctbrec.ui.action.OpenUrlAction; import ctbrec.ui.action.PauseAction; import ctbrec.ui.action.PlayAction; -import ctbrec.ui.action.RemoveTimeLimitAction; import ctbrec.ui.action.ResumeAction; -import ctbrec.ui.action.SetStopDateAction; -import ctbrec.ui.action.StartRecordingAction; import ctbrec.ui.action.StopRecordingAction; import ctbrec.ui.action.ToggleRecordingAction; import ctbrec.ui.controls.CustomMouseBehaviorContextMenu; @@ -56,7 +43,7 @@ import ctbrec.ui.controls.Dialogs; import ctbrec.ui.controls.SearchBox; import ctbrec.ui.controls.autocomplete.AutoFillTextField; import ctbrec.ui.controls.autocomplete.ObservableListSuggester; -import ctbrec.ui.menu.ModelGroupMenuBuilder; +import ctbrec.ui.menu.ModelMenuContributor; import ctbrec.ui.tabs.TabSelectionListener; import javafx.application.Platform; import javafx.beans.property.SimpleObjectProperty; @@ -71,12 +58,11 @@ import javafx.concurrent.WorkerStateEvent; import javafx.event.ActionEvent; import javafx.geometry.Insets; import javafx.geometry.Pos; +import javafx.scene.Cursor; import javafx.scene.control.Alert; import javafx.scene.control.Button; import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; -import javafx.scene.control.Menu; -import javafx.scene.control.MenuItem; import javafx.scene.control.ScrollPane; import javafx.scene.control.SelectionMode; import javafx.scene.control.Tab; @@ -92,8 +78,6 @@ import javafx.scene.control.Tooltip; import javafx.scene.control.cell.CheckBoxTableCell; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.control.cell.TextFieldTableCell; -import javafx.scene.input.Clipboard; -import javafx.scene.input.ClipboardContent; import javafx.scene.input.ContextMenuEvent; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; @@ -505,6 +489,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { filteredModels.clear(); filter(filter.getText()); table.sort(); + table.refresh(); } private void addOrUpdateModels(List updatedModels) { @@ -635,162 +620,21 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { } private ContextMenu createContextMenu() { - ObservableList selectedModels = table.getSelectionModel().getSelectedItems(); + List selectedModels = table.getSelectionModel().getSelectedItems().stream().map(JavaFxModel::getDelegate).collect(Collectors.toList()); if (selectedModels.isEmpty()) { return null; } - var stop = new MenuItem("Remove Model"); - stop.setOnAction(e -> stopAction(selectedModels)); - var recordLater = new MenuItem("Record Later"); - recordLater.setOnAction(e -> recordLater(selectedModels)); - var copyUrl = new MenuItem("Copy URL"); - copyUrl.setOnAction(e -> { - Model selected = selectedModels.get(0); - final var clipboard = Clipboard.getSystemClipboard(); - final var content = new ClipboardContent(); - content.putString(selected.getUrl()); - clipboard.setContent(content); - }); + ContextMenu menu = new CustomMouseBehaviorContextMenu(); - var pauseRecording = new MenuItem("Pause Recording"); - pauseRecording.setOnAction(e -> pauseRecording(selectedModels)); - var resumeRecording = new MenuItem("Resume Recording"); - resumeRecording.setOnAction(e -> resumeRecording(selectedModels)); - var stopRecordingAt = new MenuItem("Stop Recording at Date"); - stopRecordingAt.setOnAction(e -> setStopDate(selectedModels.get(0))); - var removeTimeLimit = new MenuItem("Remove Time Limit"); - removeTimeLimit.setOnAction(e -> removeTimeLimit(selectedModels.get(0))); - var openInBrowser = new MenuItem("Open in Browser"); - openInBrowser.setOnAction(e -> openInBrowser(selectedModels)); - var openInPlayer = new MenuItem("Open in Player"); - openInPlayer.setOnAction(e -> openInPlayer(selectedModels.get(0))); - var switchStreamSource = new MenuItem("Switch resolution"); - switchStreamSource.setOnAction(e -> switchStreamSource(selectedModels.get(0))); - var follow = new MenuItem("Follow"); - follow.setOnAction(e -> follow(selectedModels)); - follow.setDisable(!selectedModels.stream().allMatch(m -> m.getSite().supportsFollow() && m.getSite().credentialsAvailable())); - var ignore = new MenuItem("Ignore"); - ignore.setOnAction(e -> ignore(selectedModels)); - var notes = new MenuItem("Notes"); - notes.setOnAction(e -> notes(selectedModels)); - var openRecDir = new MenuItem("Open recording directory"); - openRecDir.setOnAction(e -> new OpenRecordingsDir(table, selectedModels.get(0)).execute()); - - var addToGroup = new MenuItem("Add to group"); - addToGroup.setOnAction(e -> new AddToGroupAction(this.getContent(), recorder, selectedModels.get(0)).execute(() -> table.refresh())); - var groupSubMenu = createModelGroupMenu(selectedModels); - - ContextMenu menu = new CustomMouseBehaviorContextMenu(stop, recordLater); - if (selectedModels.size() == 1) { - menu.getItems().add(selectedModels.get(0).isSuspended() ? resumeRecording : pauseRecording); - menu.getItems().add(stopRecordingAt); - if (selectedModels.get(0).isRecordingTimeLimited()) { - menu.getItems().add(removeTimeLimit); - } - } else { - menu.getItems().addAll(resumeRecording, pauseRecording); - } - Optional modelGroup = recorder.getModelGroup(selectedModels.get(0)); - menu.getItems().add(modelGroup.isEmpty() ? addToGroup : groupSubMenu); - menu.getItems().addAll(copyUrl, openInPlayer, openInBrowser, openRecDir, switchStreamSource, follow, notes, ignore); - - if (selectedModels.size() > 1) { - copyUrl.setDisable(true); - openInPlayer.setDisable(true); - switchStreamSource.setDisable(true); - notes.setDisable(true); - } + ModelMenuContributor.newContributor(getTabPane(), Config.getInstance(), recorder) // + .withStartStopCallback(m -> getTabPane().setCursor(Cursor.DEFAULT)) // + .afterwards(table::refresh) + .contributeToMenu(selectedModels, menu); return menu; } - private void openInBrowser(ObservableList selectedModels) { - List models = selectedModels.stream().map(JavaFxModel::getDelegate).collect(Collectors.toList()); - new OpenUrlAction(getTabPane(), models).execute(); - } - - private Menu createModelGroupMenu(ObservableList selectedModels) { - return new ModelGroupMenuBuilder() // - .model(selectedModels.get(0)) // - .recorder(recorder) // - .node(table) // - .callback(m -> table.refresh()) // - .build(); - } - - private void setStopDate(JavaFxModel model) { - new SetStopDateAction(table, model.getDelegate(), recorder) // - .execute() // - .whenComplete((result, exception) -> table.refresh()); - } - - private void removeTimeLimit(JavaFxModel selectedModel) { - new RemoveTimeLimitAction(table, selectedModel.getDelegate(), recorder) // - .execute() // - .whenComplete((result, exception) -> table.refresh()); - } - - private void ignore(ObservableList selectedModels) { - new IgnoreModelsAction(table, selectedModels, recorder, true).execute(); - } - - private void follow(ObservableList selectedModels) { - new FollowAction(getTabPane(), new ArrayList<>(selectedModels)).execute(); - } - - private void notes(ObservableList selectedModels) { - new EditNotesAction(getTabPane(), selectedModels.get(0), table).execute(); - } - - private void openInPlayer(JavaFxModel selectedModel) { - new PlayAction(getTabPane(), selectedModel).execute(); - } - - private void switchStreamSource(JavaFxModel fxModel) { - var couldntSwitchHeaderText = "Couldn't switch stream resolution"; - - try { - if (!fxModel.isOnline(true)) { - Dialogs.showError(getTabPane().getScene(), couldntSwitchHeaderText, "The resolution can only be changed, when the model is online", null); - return; - } - } catch (InterruptedException e1) { - Thread.currentThread().interrupt(); - Dialogs.showError(getTabPane().getScene(), couldntSwitchHeaderText, "An error occured while checking, if the model is online", null); - return; - } catch (IOException | ExecutionException e1) { - Dialogs.showError(getTabPane().getScene(), couldntSwitchHeaderText, "An error occured while checking, if the model is online", null); - return; - } - - Consumer onSuccess = m -> { - try { - recorder.switchStreamSource(m); - } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException e) { - LOG.error(couldntSwitchHeaderText, e); - showStreamSwitchErrorDialog(e); - } - }; - Consumer onFail = t -> { - LOG.error(couldntSwitchHeaderText, t); - showStreamSwitchErrorDialog(t); - }; - StreamSourceSelectionDialog.show(getTabPane().getScene(), fxModel.getDelegate(), onSuccess, onFail); - } - - private void showStreamSwitchErrorDialog(Throwable throwable) { - showErrorDialog(throwable, "Couldn't switch stream resolution", "Error while switching stream resolution"); - } - - private void showErrorDialog(Throwable throwable, String header, String msg) { - Alert alert = new AutosizeAlert(Alert.AlertType.ERROR, getTabPane().getScene()); - alert.setTitle("Error"); - alert.setHeaderText(header); - alert.setContentText(msg + ": " + throwable.getLocalizedMessage()); - alert.showAndWait(); - } - private boolean stopAction(List selectedModels) { var confirmed = true; if (Config.getInstance().getSettings().confirmationForDangerousActions) { @@ -809,17 +653,6 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { return confirmed; } - private void recordLater(List selectedModels) { - boolean confirmed = stopAction(selectedModels); - if (confirmed) { - List models = selectedModels.stream().map(JavaFxModel::getDelegate).map(m -> { - m.setMarkedForLaterRecording(true); - return m; - }).collect(Collectors.toList()); - new StartRecordingAction(table, models, recorder).execute(); - } - } - private void pauseRecording(List selectedModels) { List models = selectedModels.stream().map(JavaFxModel::getDelegate).collect(Collectors.toList()); new PauseAction(getTabPane(), models, recorder).execute();