From 9bb2d5d593d54a43e3a076bf08db0d01677e3675 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sun, 9 May 2021 18:38:42 +0200 Subject: [PATCH] Add dialog to edit model groups --- .../java/ctbrec/ui/CamrecApplication.java | 2 +- client/src/main/java/ctbrec/ui/Icon.java | 18 ++ .../src/main/java/ctbrec/ui/JavaFxModel.java | 18 +- .../ctbrec/ui/action/AddToGroupAction.java | 4 +- .../ctbrec/ui/action/EditGroupAction.java | 161 ++++++++++++++++++ .../main/java/ctbrec/ui/controls/Dialogs.java | 20 +-- .../java/ctbrec/ui/tabs/ThumbOverviewTab.java | 52 +++--- .../ui/tabs/recorded/ClickableTableCell.java | 21 +++ .../ui/tabs/recorded/IconTableCell.java | 47 +++++ .../ui/tabs/recorded/ModelNameTableCell.java | 40 +++++ .../ui/tabs/recorded/OnlineTableCell.java | 32 ++++ .../tabs/{ => recorded}/RecordLaterTab.java | 3 +- .../{ => recorded}/RecordedModelsTab.java | 144 ++++++++-------- .../ui/tabs/{ => recorded}/RecordedTab.java | 3 +- .../ui/tabs/recorded/RecordingTableCell.java | 47 +++++ client/src/main/resources/16/check-circle.png | Bin 0 -> 4962 bytes client/src/main/resources/16/check-small.png | Bin 0 -> 4792 bytes client/src/main/resources/16/check.png | Bin 0 -> 4791 bytes client/src/main/resources/16/clock.png | Bin 0 -> 4949 bytes client/src/main/resources/16/users.png | Bin 0 -> 4966 bytes client/src/main/resources/32/check-circle.png | Bin 0 -> 822 bytes client/src/main/resources/32/check-small.png | Bin 0 -> 317 bytes client/src/main/resources/32/check-small.svg | 57 +++++++ client/src/main/resources/32/check.png | Bin 0 -> 291 bytes client/src/main/resources/32/clock.png | Bin 0 -> 757 bytes client/src/main/resources/32/users.png | Bin 0 -> 603 bytes .../src/main/resources/buymeacoffee-round.png | Bin 9832 -> 0 bytes client/src/main/resources/kofi-round.png | Bin 13904 -> 0 bytes client/src/main/resources/kofi-round600.png | Bin 15776 -> 0 bytes client/src/main/resources/patreon-round.png | Bin 7770 -> 0 bytes client/src/main/resources/paypal-round.png | Bin 8842 -> 0 bytes common/src/main/java/ctbrec/Settings.java | 1 + 32 files changed, 548 insertions(+), 122 deletions(-) create mode 100644 client/src/main/java/ctbrec/ui/Icon.java create mode 100644 client/src/main/java/ctbrec/ui/action/EditGroupAction.java create mode 100644 client/src/main/java/ctbrec/ui/tabs/recorded/ClickableTableCell.java create mode 100644 client/src/main/java/ctbrec/ui/tabs/recorded/IconTableCell.java create mode 100644 client/src/main/java/ctbrec/ui/tabs/recorded/ModelNameTableCell.java create mode 100644 client/src/main/java/ctbrec/ui/tabs/recorded/OnlineTableCell.java rename client/src/main/java/ctbrec/ui/tabs/{ => recorded}/RecordLaterTab.java (99%) rename client/src/main/java/ctbrec/ui/tabs/{ => recorded}/RecordedModelsTab.java (89%) rename client/src/main/java/ctbrec/ui/tabs/{ => recorded}/RecordedTab.java (96%) create mode 100644 client/src/main/java/ctbrec/ui/tabs/recorded/RecordingTableCell.java create mode 100644 client/src/main/resources/16/check-circle.png create mode 100644 client/src/main/resources/16/check-small.png create mode 100644 client/src/main/resources/16/check.png create mode 100644 client/src/main/resources/16/clock.png create mode 100644 client/src/main/resources/16/users.png create mode 100644 client/src/main/resources/32/check-circle.png create mode 100644 client/src/main/resources/32/check-small.png create mode 100644 client/src/main/resources/32/check-small.svg create mode 100644 client/src/main/resources/32/check.png create mode 100644 client/src/main/resources/32/clock.png create mode 100644 client/src/main/resources/32/users.png delete mode 100644 client/src/main/resources/buymeacoffee-round.png delete mode 100644 client/src/main/resources/kofi-round.png delete mode 100644 client/src/main/resources/kofi-round600.png delete mode 100644 client/src/main/resources/patreon-round.png delete mode 100644 client/src/main/resources/paypal-round.png diff --git a/client/src/main/java/ctbrec/ui/CamrecApplication.java b/client/src/main/java/ctbrec/ui/CamrecApplication.java index 94edea00..c5df0f6e 100644 --- a/client/src/main/java/ctbrec/ui/CamrecApplication.java +++ b/client/src/main/java/ctbrec/ui/CamrecApplication.java @@ -63,12 +63,12 @@ import ctbrec.ui.settings.SettingsTab; import ctbrec.ui.tabs.DonateTabFx; import ctbrec.ui.tabs.HelpTab; import ctbrec.ui.tabs.RecentlyWatchedTab; -import ctbrec.ui.tabs.RecordedTab; import ctbrec.ui.tabs.RecordingsTab; import ctbrec.ui.tabs.SiteTab; import ctbrec.ui.tabs.TabSelectionListener; import ctbrec.ui.tabs.UpdateTab; import ctbrec.ui.tabs.logging.LoggingTab; +import ctbrec.ui.tabs.recorded.RecordedTab; import javafx.application.Application; import javafx.application.HostServices; import javafx.application.Platform; diff --git a/client/src/main/java/ctbrec/ui/Icon.java b/client/src/main/java/ctbrec/ui/Icon.java new file mode 100644 index 00000000..2f55c911 --- /dev/null +++ b/client/src/main/java/ctbrec/ui/Icon.java @@ -0,0 +1,18 @@ +package ctbrec.ui; + +public enum Icon { + + GROUP_16(Icon.class.getResource("/16/users.png").toExternalForm()), + CHECK_16(Icon.class.getResource("/16/check-small.png").toExternalForm()), + CLOCK_16(Icon.class.getResource("/16/clock.png").toExternalForm()); + + private String url; + + private Icon(String url) { + this.url = url; + } + + public String url() { + return url; + } +} diff --git a/client/src/main/java/ctbrec/ui/JavaFxModel.java b/client/src/main/java/ctbrec/ui/JavaFxModel.java index 05a4f32b..c76bc9ee 100644 --- a/client/src/main/java/ctbrec/ui/JavaFxModel.java +++ b/client/src/main/java/ctbrec/ui/JavaFxModel.java @@ -22,15 +22,13 @@ import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleObjectProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; /** * Just a wrapper for Model, which augments it with JavaFX value binding properties, so that UI widgets get updated proeprly */ public class JavaFxModel implements Model { - private transient StringProperty onlineProperty = new SimpleStringProperty(); - private transient StringProperty recordingProperty = new SimpleStringProperty(); + private transient BooleanProperty onlineProperty = new SimpleBooleanProperty(); + private transient BooleanProperty recordingProperty = new SimpleBooleanProperty(); private transient BooleanProperty pausedProperty = new SimpleBooleanProperty(); private transient SimpleIntegerProperty priorityProperty = new SimpleIntegerProperty(); private transient SimpleObjectProperty lastSeenProperty = new SimpleObjectProperty<>(); @@ -105,14 +103,22 @@ public class JavaFxModel implements Model { return delegate.toString(); } - public StringProperty getOnlineProperty() { + public BooleanProperty getOnlineProperty() { return onlineProperty; } - public StringProperty getRecordingProperty() { + public void setOnlineProperty(boolean online) { + this.onlineProperty.set(online); + } + + public BooleanProperty getRecordingProperty() { return recordingProperty; } + public void setRecordingProperty(boolean recording) { + this.recordingProperty.setValue(recording); + } + public BooleanProperty getPausedProperty() { return pausedProperty; } diff --git a/client/src/main/java/ctbrec/ui/action/AddToGroupAction.java b/client/src/main/java/ctbrec/ui/action/AddToGroupAction.java index db2a5c86..0a285db4 100644 --- a/client/src/main/java/ctbrec/ui/action/AddToGroupAction.java +++ b/client/src/main/java/ctbrec/ui/action/AddToGroupAction.java @@ -41,7 +41,7 @@ public class AddToGroupAction { public void execute() { source.setCursor(Cursor.WAIT); try { - var dialog = new ModelGroupDialog(); + var dialog = new AddModelGroupDialog(); boolean ok = Dialogs.showCustomInput(source.getScene(), "Add model to group", dialog.getMainPane()); dialog.requestFocus(); if (ok) { @@ -70,7 +70,7 @@ public class AddToGroupAction { } } - private static class ModelGroupDialog { + private static class AddModelGroupDialog { private ComboBox comboBox; private TextField editor; private ObservableListSuggester suggester; diff --git a/client/src/main/java/ctbrec/ui/action/EditGroupAction.java b/client/src/main/java/ctbrec/ui/action/EditGroupAction.java new file mode 100644 index 00000000..c60a0cfb --- /dev/null +++ b/client/src/main/java/ctbrec/ui/action/EditGroupAction.java @@ -0,0 +1,161 @@ +package ctbrec.ui.action; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import ctbrec.Config; +import ctbrec.Model; +import ctbrec.ModelGroup; +import ctbrec.recorder.Recorder; +import ctbrec.ui.controls.Dialogs; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.scene.control.ListView; +import javafx.scene.control.TextField; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; + +public class EditGroupAction { + + private static final String DIALOG_TITLE = "Edit model group"; + + private Node source; + private Model model; + private Recorder recorder; + + public EditGroupAction(Node source, Recorder recorder, Model model) { + this.source = source; + this.recorder = recorder; + this.model = model; + } + + public void execute() { + source.setCursor(Cursor.WAIT); + try { + var dialog = new EditModelGroupDialog(model); + boolean ok = Dialogs.showCustomInput(source.getScene(), DIALOG_TITLE, dialog); + if (ok) { + var group = dialog.getModelGroup(); + group.setName(dialog.getGroupName()); + group.getModelUrls().clear(); + group.getModelUrls().addAll(dialog.getUrls()); + recorder.saveModelGroup(group); + } + } catch (Exception e) { + Dialogs.showError(source.getScene(), DIALOG_TITLE, "Editing model group failed", e); + } finally { + source.setCursor(Cursor.DEFAULT); + } + } + + private static class EditModelGroupDialog extends GridPane { + private TextField groupName; + private ListView urlListView; + private ObservableList urlList; + private ModelGroup modelGroup; + private List urls; + + public EditModelGroupDialog(Model model) { + Optional optionalModelGroup = Config.getInstance().getModelGroup(model); + if (optionalModelGroup.isPresent()) { + modelGroup = optionalModelGroup.get(); + urls = new ArrayList<>(modelGroup.getModelUrls()); + createGui(modelGroup); + } else { + Dialogs.showError(getScene(), DIALOG_TITLE, "No group found for model", null); + } + } + + public ModelGroup getModelGroup() { + return modelGroup; + } + + public List getUrls() { + return urls; + } + + public String getGroupName() { + return groupName.getText(); + } + + void createGui(ModelGroup modelGroup) { + setHgap(5); + vgapProperty().bind(hgapProperty()); + + groupName = new TextField(modelGroup.getName()); + Button up = createUpButton(); + Button down = createDownButton(); + Button remove = createRemoveButton(); + var buttons = new VBox(3, up, down, remove); + urlList = FXCollections.observableList(modelGroup.getModelUrls()); + urlList.addListener((ListChangeListener) change -> { + urls = new ArrayList<>(urlList); + }); + urlListView = new ListView<>(urlList); + GridPane.setHgrow(urlListView, Priority.ALWAYS); + + var row = 0; + add(groupName, 0, row++); + add(urlListView, 0, row); + add(buttons, 1, row); + + urlListView.getSelectionModel().selectedIndexProperty().addListener((obs, oldV, newV) -> { + var idx = newV.intValue(); + boolean noSelection = idx == -1; + up.setDisable(noSelection || idx == 0); + down.setDisable(noSelection || idx == urlList.size() - 1); + remove.setDisable(noSelection); + }); + } + + private Button createUpButton() { + var button = createButton("\u25B4", "Move step up"); + button.setOnAction(evt -> { + int idx = urlListView.getSelectionModel().getSelectedIndex(); + String selectedItem = urlListView.getSelectionModel().getSelectedItem(); + urlList.remove(idx); + urlList.add(idx - 1, selectedItem); + urlListView.getSelectionModel().select(idx - 1); + }); + return button; + } + + private Button createDownButton() { + var button = createButton("\u25BE", "Move step down"); + button.setOnAction(evt -> { + int idx = urlListView.getSelectionModel().getSelectedIndex(); + String selectedItem = urlListView.getSelectionModel().getSelectedItem(); + urlList.remove(idx); + urlList.add(idx + 1, selectedItem); + urlListView.getSelectionModel().select(idx + 1); + }); + return button; + } + + private Button createRemoveButton() { + var button = createButton("-", "Remove selected step"); + button.setOnAction(evt -> { + String selectedItem = urlListView.getSelectionModel().getSelectedItem(); + if (selectedItem != null) { + urlList.remove(selectedItem); + } + }); + return button; + } + + private Button createButton(String text, String tooltip) { + var b = new Button(text); + b.setTooltip(new Tooltip(tooltip)); + b.setDisable(true); + b.setPrefSize(32, 32); + return b; + } + } +} diff --git a/client/src/main/java/ctbrec/ui/controls/Dialogs.java b/client/src/main/java/ctbrec/ui/controls/Dialogs.java index 3de44bb5..1e5292ec 100644 --- a/client/src/main/java/ctbrec/ui/controls/Dialogs.java +++ b/client/src/main/java/ctbrec/ui/controls/Dialogs.java @@ -56,7 +56,7 @@ public class Dialogs { } alert.setContentText(content); if (parent != null) { - Stage stage = (Stage) alert.getDialogPane().getScene().getWindow(); + var stage = (Stage) alert.getDialogPane().getScene().getWindow(); stage.getScene().getStylesheets().addAll(parent.getStylesheets()); } alert.showAndWait(); @@ -77,18 +77,18 @@ public class Dialogs { dialog.initModality(Modality.APPLICATION_MODAL); dialog.setResizable(true); InputStream icon = Dialogs.class.getResourceAsStream("/icon.png"); - Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow(); + var stage = (Stage) dialog.getDialogPane().getScene().getWindow(); stage.getIcons().add(new Image(icon)); if (parent != null) { stage.getScene().getStylesheets().addAll(parent.getStylesheets()); } - GridPane grid = new GridPane(); + var grid = new GridPane(); grid.setHgap(10); grid.setVgap(10); grid.setPadding(new Insets(20, 150, 10, 10)); - TextArea notes = new TextArea(text); + var notes = new TextArea(text); notes.setPrefRowCount(3); grid.add(notes, 0, 0); dialog.getDialogPane().setContent(grid); @@ -112,7 +112,7 @@ public class Dialogs { dialog.initModality(Modality.APPLICATION_MODAL); dialog.setResizable(true); InputStream icon = Dialogs.class.getResourceAsStream("/icon.png"); - Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow(); + var stage = (Stage) dialog.getDialogPane().getScene().getWindow(); stage.getIcons().add(new Image(icon)); if (parent != null) { stage.getScene().getStylesheets().addAll(parent.getStylesheets()); @@ -123,7 +123,7 @@ public class Dialogs { } public static boolean showConfirmDialog(String title, String message, String header, Scene parent) { - AutosizeAlert confirm = new AutosizeAlert(AlertType.CONFIRMATION, message, parent, YES, NO); + var confirm = new AutosizeAlert(AlertType.CONFIRMATION, message, parent, YES, NO); confirm.setTitle(title); confirm.setHeaderText(header); confirm.showAndWait(); @@ -131,8 +131,8 @@ public class Dialogs { } public static ButtonType showShutdownDialog(Scene parent) { - String message = "There are recordings in progress"; - AutosizeAlert confirm = new AutosizeAlert(AlertType.CONFIRMATION, "", parent, YES, FINISH, NO); + var message = "There are recordings in progress"; + var confirm = new AutosizeAlert(AlertType.CONFIRMATION, "", parent, YES, FINISH, NO); confirm.setTitle("Shutdown"); confirm.setHeaderText(message); ((Button) confirm.getDialogPane().lookupButton(ButtonType.YES)).setText("Shutdown Now"); @@ -146,7 +146,7 @@ public class Dialogs { } public static Optional showModelGroupSelectionDialog(Scene parent, Model model) { - GridPane dialogPane = new GridPane(); + var dialogPane = new GridPane(); Set modelGroups = Config.getInstance().getSettings().modelGroups; ObservableList comboBoxModel = FXCollections.observableArrayList(modelGroups); ComboBox comboBox = new ComboBox<>(comboBoxModel); @@ -165,7 +165,7 @@ public class Dialogs { existingGroup.get().add(model); return existingGroup; } else { - ModelGroup group = new ModelGroup(); + var group = new ModelGroup(); group.setId(UUID.randomUUID()); group.setName(text); group.add(model); diff --git a/client/src/main/java/ctbrec/ui/tabs/ThumbOverviewTab.java b/client/src/main/java/ctbrec/ui/tabs/ThumbOverviewTab.java index d7748246..073fe677 100644 --- a/client/src/main/java/ctbrec/ui/tabs/ThumbOverviewTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/ThumbOverviewTab.java @@ -41,6 +41,7 @@ import ctbrec.ui.SiteUiFactory; import ctbrec.ui.TipDialog; import ctbrec.ui.TokenLabel; import ctbrec.ui.action.AddToGroupAction; +import ctbrec.ui.action.EditGroupAction; import ctbrec.ui.action.IgnoreModelsAction; import ctbrec.ui.action.OpenRecordingsDir; import ctbrec.ui.action.SetStopDateAction; @@ -469,57 +470,57 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { } private ContextMenu createContextMenu(ThumbCell cell) { - Model model = cell.getModel(); + var model = cell.getModel(); boolean modelIsTrackedByRecorder = recorder.isTracked(model); - MenuItem openInPlayer = new MenuItem("Open in Player"); + var openInPlayer = new MenuItem("Open in Player"); openInPlayer.setOnAction(e -> startPlayer(getSelectedThumbCells(cell))); - MenuItem start = new MenuItem("Start Recording"); + var start = new MenuItem("Start Recording"); start.setOnAction(e -> startStopAction(getSelectedThumbCells(cell), true)); - MenuItem stop = new MenuItem("Stop Recording"); + var stop = new MenuItem("Stop Recording"); stop.setOnAction(e -> startStopAction(getSelectedThumbCells(cell), false)); - MenuItem startStop = recorder.isTracked(model) ? stop : start; + var startStop = recorder.isTracked(model) ? stop : start; - MenuItem recordUntil = new MenuItem("Start Recording Until"); + var recordUntil = new MenuItem("Start Recording Until"); recordUntil.setOnAction(e -> startRecordingWithTimeLimit(getSelectedThumbCells(cell))); - MenuItem addPaused = new MenuItem("Add in paused state"); + var addPaused = new MenuItem("Add in paused state"); addPaused.setOnAction(e -> addPaused(getSelectedThumbCells(cell))); - MenuItem recordLater = new MenuItem("Record Later"); + var recordLater = new MenuItem("Record Later"); recordLater.setOnAction(e -> recordLater(getSelectedThumbCells(cell), true)); - MenuItem removeRecordLater = new MenuItem("Forget Model"); + var removeRecordLater = new MenuItem("Forget Model"); removeRecordLater.setOnAction(e -> recordLater(getSelectedThumbCells(cell), false)); - MenuItem addRemoveBookmark = recorder.isMarkedForLaterRecording(model) ? removeRecordLater : recordLater; + var addRemoveBookmark = recorder.isMarkedForLaterRecording(model) ? removeRecordLater : recordLater; - MenuItem pause = new MenuItem("Pause Recording"); + var pause = new MenuItem("Pause Recording"); pause.setOnAction(e -> pauseResumeAction(getSelectedThumbCells(cell), true)); - MenuItem resume = new MenuItem("Resume Recording"); + var resume = new MenuItem("Resume Recording"); resume.setOnAction(e -> pauseResumeAction(getSelectedThumbCells(cell), false)); - MenuItem pauseResume = recorder.isSuspended(model) ? resume : pause; + var pauseResume = recorder.isSuspended(model) ? resume : pause; - MenuItem follow = new MenuItem("Follow"); + var follow = new MenuItem("Follow"); follow.setOnAction(e -> follow(getSelectedThumbCells(cell), true)); - MenuItem unfollow = new MenuItem("Unfollow"); + var unfollow = new MenuItem("Unfollow"); unfollow.setOnAction(e -> follow(getSelectedThumbCells(cell), false)); - MenuItem addToGroup = new MenuItem("Add to group"); + var addToGroup = new MenuItem("Add to group"); addToGroup.setOnAction(e -> addToGroup(model)); - MenuItem editGroup = new MenuItem("Edit group"); + var editGroup = new MenuItem("Edit group"); editGroup.setOnAction(e -> editGroup(model)); - MenuItem ignore = new MenuItem("Ignore"); + var ignore = new MenuItem("Ignore"); ignore.setOnAction(e -> ignore(getSelectedThumbCells(cell))); - MenuItem refresh = new MenuItem("Refresh Overview"); + var refresh = new MenuItem("Refresh Overview"); refresh.setOnAction(e -> refresh()); - MenuItem openRecDir = new MenuItem("Open recording directory"); + var openRecDir = new MenuItem("Open recording directory"); openRecDir.setOnAction(e -> new OpenRecordingsDir(cell, model).execute()); - MenuItem copyUrl = createCopyUrlMenuItem(cell); - MenuItem openInBrowser = createOpenInBrowser(cell); - MenuItem sendTip = createTipMenuItem(cell); + var copyUrl = createCopyUrlMenuItem(cell); + var openInBrowser = createOpenInBrowser(cell); + var sendTip = createTipMenuItem(cell); configureItemsForSelection(cell, openInPlayer, copyUrl, sendTip); @@ -535,7 +536,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { } contextMenu.getItems().add(new SeparatorMenuItem()); if (site.supportsFollow()) { - MenuItem followOrUnFollow = (this instanceof FollowedTab) ? unfollow : follow; + var followOrUnFollow = (this instanceof FollowedTab) ? unfollow : follow; followOrUnFollow.setDisable(!site.credentialsAvailable()); contextMenu.getItems().add(followOrUnFollow); } @@ -546,7 +547,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { contextMenu.getItems().add(modelGroup.isEmpty() ? addToGroup : editGroup); contextMenu.getItems().addAll(copyUrl, openInBrowser, ignore, refresh, openRecDir); if (model instanceof MyFreeCamsModel && Objects.equals(System.getenv("CTBREC_DEV"), "1")) { - MenuItem debug = new MenuItem("debug"); + var debug = new MenuItem("debug"); debug.setOnAction(e -> MyFreeCamsClient.getInstance().getSessionState(model)); contextMenu.getItems().add(debug); } @@ -555,6 +556,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { } private void editGroup(Model model) { + new EditGroupAction(this.getContent(), recorder, model).execute(); } private void addToGroup(Model model) { diff --git a/client/src/main/java/ctbrec/ui/tabs/recorded/ClickableTableCell.java b/client/src/main/java/ctbrec/ui/tabs/recorded/ClickableTableCell.java new file mode 100644 index 00000000..85262a0f --- /dev/null +++ b/client/src/main/java/ctbrec/ui/tabs/recorded/ClickableTableCell.java @@ -0,0 +1,21 @@ +package ctbrec.ui.tabs.recorded; + +import ctbrec.ui.JavaFxModel; +import ctbrec.ui.action.PlayAction; +import javafx.scene.control.TableCell; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; + +public class ClickableTableCell extends TableCell { + + public ClickableTableCell() { + addEventFilter(MouseEvent.MOUSE_CLICKED, event -> { + if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) { + JavaFxModel selectedModel = getTableView().getSelectionModel().getSelectedItem(); + if(selectedModel != null) { + new PlayAction(getTableView(), selectedModel).execute(); + } + } + }); + } +} diff --git a/client/src/main/java/ctbrec/ui/tabs/recorded/IconTableCell.java b/client/src/main/java/ctbrec/ui/tabs/recorded/IconTableCell.java new file mode 100644 index 00000000..cd7884f9 --- /dev/null +++ b/client/src/main/java/ctbrec/ui/tabs/recorded/IconTableCell.java @@ -0,0 +1,47 @@ +package ctbrec.ui.tabs.recorded; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import ctbrec.ui.Icon; +import javafx.scene.control.Tooltip; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; + +public class IconTableCell extends ClickableTableCell { + + protected String tooltip; + protected HBox iconRow; + private Map icons; + + public IconTableCell(Map icons) { + this.icons = Objects.requireNonNullElse(icons, new HashMap<>()); + iconRow = new HBox(3); + } + + protected void show(Icon iconName) { + var imageView = icons.get(iconName); + if (imageView != null) { + iconRow.getChildren().remove(imageView); + iconRow.getChildren().add(imageView); + } + } + + protected void hide(Icon iconName) { + var imageView = icons.get(iconName); + if (imageView != null) { + iconRow.getChildren().remove(imageView); + } + } + + @Override + protected void updateItem(T value, boolean empty) { + if (tooltip != null) { + setTooltip(new Tooltip(tooltip)); + } else { + setTooltip(null); + } + setGraphic(iconRow); + } +} diff --git a/client/src/main/java/ctbrec/ui/tabs/recorded/ModelNameTableCell.java b/client/src/main/java/ctbrec/ui/tabs/recorded/ModelNameTableCell.java new file mode 100644 index 00000000..4f9120f3 --- /dev/null +++ b/client/src/main/java/ctbrec/ui/tabs/recorded/ModelNameTableCell.java @@ -0,0 +1,40 @@ +package ctbrec.ui.tabs.recorded; + +import static ctbrec.ui.Icon.*; + +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import ctbrec.Config; +import ctbrec.Model; +import ctbrec.ModelGroup; +import javafx.scene.image.ImageView; + +public class ModelNameTableCell extends IconTableCell { + + public ModelNameTableCell() { + super(Map.of(GROUP_16, new ImageView(GROUP_16.url()))); + } + + @Override + protected void updateItem(String item, boolean empty) { + setText(null); + tooltip = null; + hide(GROUP_16); + + if (item != null && !empty) { + setText(item); + Model m = getTableView().getItems().get(getTableRow().getIndex()); + Optional optionalGroup = Config.getInstance().getModelGroup(m); + if (optionalGroup.isPresent()) { + ModelGroup group = optionalGroup.get(); + setText(group.getName() + " (aka " + item + ')'); + show(GROUP_16); + tooltip = group.getModelUrls().size() + " models:\n"; + tooltip += group.getModelUrls().stream().collect(Collectors.joining("\n")); + } + } + super.updateItem(item, empty); + } +} diff --git a/client/src/main/java/ctbrec/ui/tabs/recorded/OnlineTableCell.java b/client/src/main/java/ctbrec/ui/tabs/recorded/OnlineTableCell.java new file mode 100644 index 00000000..fb535162 --- /dev/null +++ b/client/src/main/java/ctbrec/ui/tabs/recorded/OnlineTableCell.java @@ -0,0 +1,32 @@ +package ctbrec.ui.tabs.recorded; + +import static ctbrec.ui.Icon.*; + +import java.util.Map; + +import com.google.common.base.Objects; + +import javafx.scene.image.ImageView; + +public class OnlineTableCell extends IconTableCell { + + public OnlineTableCell() { + super(Map.of(CHECK_16, new ImageView(CHECK_16.url()))); + } + + @Override + protected void updateItem(Boolean value, boolean empty) { + if (!empty) { + if (Objects.equal(value, Boolean.TRUE)) { + show(CHECK_16); + tooltip = "Online"; + } else { + hide(CHECK_16); + tooltip = null; + } + } else { + tooltip = null; + } + super.updateItem(value, empty); + } +} diff --git a/client/src/main/java/ctbrec/ui/tabs/RecordLaterTab.java b/client/src/main/java/ctbrec/ui/tabs/recorded/RecordLaterTab.java similarity index 99% rename from client/src/main/java/ctbrec/ui/tabs/RecordLaterTab.java rename to client/src/main/java/ctbrec/ui/tabs/recorded/RecordLaterTab.java index 59a7266e..f7a2148e 100644 --- a/client/src/main/java/ctbrec/ui/tabs/RecordLaterTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/recorded/RecordLaterTab.java @@ -1,4 +1,4 @@ -package ctbrec.ui.tabs; +package ctbrec.ui.tabs.recorded; import java.io.IOException; import java.security.InvalidKeyException; @@ -37,6 +37,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.tabs.TabSelectionListener; import javafx.application.Platform; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringPropertyBase; diff --git a/client/src/main/java/ctbrec/ui/tabs/RecordedModelsTab.java b/client/src/main/java/ctbrec/ui/tabs/recorded/RecordedModelsTab.java similarity index 89% rename from client/src/main/java/ctbrec/ui/tabs/RecordedModelsTab.java rename to client/src/main/java/ctbrec/ui/tabs/recorded/RecordedModelsTab.java index b0da6346..e3bf2a74 100644 --- a/client/src/main/java/ctbrec/ui/tabs/RecordedModelsTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/recorded/RecordedModelsTab.java @@ -1,16 +1,11 @@ -package ctbrec.ui.tabs; +package ctbrec.ui.tabs.recorded; import static ctbrec.Recording.State.*; -import static ctbrec.ui.UnicodeEmoji.*; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.time.Instant; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.FormatStyle; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -31,6 +26,7 @@ import org.slf4j.LoggerFactory; import ctbrec.Config; import ctbrec.Model; +import ctbrec.ModelGroup; import ctbrec.Recording; import ctbrec.StringUtil; import ctbrec.recorder.Recorder; @@ -40,7 +36,9 @@ import ctbrec.ui.DesktopIntegration; 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.EditGroupAction; import ctbrec.ui.action.EditNotesAction; import ctbrec.ui.action.FollowAction; import ctbrec.ui.action.IgnoreModelsAction; @@ -59,6 +57,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.tabs.TabSelectionListener; import javafx.application.Platform; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringPropertyBase; @@ -157,7 +156,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { table.setEditable(true); table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); - PreviewPopupHandler previewPopupHandler = new PreviewPopupHandler(table); + var previewPopupHandler = new PreviewPopupHandler(table); table.setRowFactory(tableview -> { TableRow row = new TableRow<>(); row.addEventHandler(MouseEvent.ANY, previewPopupHandler); @@ -174,7 +173,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { TableColumn name = new TableColumn<>("Model"); name.setPrefWidth(200); name.setCellValueFactory(new PropertyValueFactory<>("displayName")); - name.setCellFactory(new ClickableCellFactory<>()); + name.setCellFactory(param -> new ModelNameTableCell()); name.setEditable(false); name.setId("name"); TableColumn url = new TableColumn<>("URL"); @@ -183,15 +182,16 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { url.setPrefWidth(400); url.setEditable(false); url.setId("url"); - TableColumn online = new TableColumn<>("Online"); + TableColumn online = new TableColumn<>("Online"); online.setCellValueFactory(cdf -> cdf.getValue().getOnlineProperty()); + online.setCellFactory(param -> new OnlineTableCell()); online.setPrefWidth(100); online.setEditable(false); online.setId("online"); online.setStyle(STYLE_ALIGN_CENTER); - TableColumn recording = new TableColumn<>("Recording"); + TableColumn recording = new TableColumn<>("Recording"); recording.setCellValueFactory(cdf -> cdf.getValue().getRecordingProperty()); - recording.setCellFactory(tc -> new RecordingCell()); + recording.setCellFactory(tc -> new RecordingTableCell()); recording.setPrefWidth(100); recording.setEditable(false); recording.setId("recording"); @@ -272,7 +272,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { scrollPane.setContent(table); - HBox addModelBox = new HBox(5); + var addModelBox = new HBox(5); modelLabel.setPadding(new Insets(5, 0, 0, 0)); ObservableList suggestions = FXCollections.observableArrayList(); sites.forEach(site -> suggestions.add(site.getClass().getSimpleName())); @@ -302,7 +302,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { checkModelAccountExistance.setOnAction(evt -> new CheckModelAccountAction(checkModelAccountExistance, recorder) .execute(Predicate.not(Model::isMarkedForLaterRecording))); - HBox filterContainer = new HBox(); + var filterContainer = new HBox(); filterContainer.setSpacing(0); filterContainer.setPadding(new Insets(0)); filterContainer.setAlignment(Pos.CENTER_RIGHT); @@ -326,7 +326,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { filterContainer.getChildren().add(filter); addModelBox.getChildren().add(filterContainer); - BorderPane root = new BorderPane(); + var root = new BorderPane(); root.setPadding(new Insets(5)); root.setTop(addModelBox); root.setCenter(scrollPane); @@ -338,7 +338,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { private void jumpToNextModel(KeyCode code) { if (!table.getItems().isEmpty() && (code.isLetterKey() || code.isDigitKey())) { // determine where to start looking for the next model - int startAt = 0; + var startAt = 0; if (table.getSelectionModel().getSelectedIndex() >= 0) { startAt = table.getSelectionModel().getSelectedIndex() + 1; if (startAt >= table.getItems().size()) { @@ -346,7 +346,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { } } - String c = code.getChar().toLowerCase(); + var c = code.getChar().toLowerCase(); int i = startAt; do { JavaFxModel current = table.getItems().get(i); @@ -378,7 +378,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { private void updatePriority(JavaFxModel model, int priority) { try { if (priority < 0 || priority > 100) { - String msg = "Priority has to be between 0 and 100"; + var msg = "Priority has to be between 0 and 100"; Dialogs.showError(table.getScene(), "Invalid value", msg, null); } else { model.setPriority(priority); @@ -404,7 +404,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { private void addModelByUrl(String url) { for (Site site : sites) { - Model newModel = site.createModelFromUrl(url); + var newModel = site.createModelFromUrl(url); if (newModel != null) { try { recorder.addModel(newModel); @@ -431,7 +431,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { for (Site site : sites) { if (Objects.equals(siteName.toLowerCase(), site.getClass().getSimpleName().toLowerCase())) { try { - Model m = site.createModel(modelName); + var m = site.createModel(modelName); recorder.addModel(m); } catch (IOException | InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e1) { Dialogs.showError(getTabPane().getScene(), "Couldn't add model", "The model " + modelName + " could not be added:", e1); @@ -548,18 +548,18 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { String[] tokens = filter.split(" "); observableModels.addAll(filteredModels); filteredModels.clear(); - for (int i = 0; i < table.getItems().size(); i++) { - StringBuilder sb = new StringBuilder(); + for (var i = 0; i < table.getItems().size(); i++) { + var sb = new StringBuilder(); for (TableColumn tc : table.getColumns()) { Object cellData = tc.getCellData(i); if (cellData != null) { - String content = cellData.toString(); + var content = cellData.toString(); sb.append(content).append(' '); } } - String searchText = sb.toString(); + var searchText = sb.toString(); - boolean tokensMissing = false; + var tokensMissing = false; for (String token : tokens) { if (!searchText.toLowerCase().contains(token.toLowerCase())) { tokensMissing = true; @@ -594,18 +594,14 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { .peek(fxm -> { // NOSONAR for (Recording recording : recordings) { if(recording.getStatus() == RECORDING && Objects.equals(recording.getModel(), fxm)){ - String recordingValue = HEAVY_CHECK_MARK; - if(!Objects.equals(recording.getModel().getRecordUntil(), Instant.ofEpochMilli(Model.RECORD_INDEFINITELY))) { - recordingValue += ' ' + CLOCK; - } - fxm.getRecordingProperty().set(recordingValue); + fxm.setRecordingProperty(true); break; } } for (Model onlineModel : onlineModels) { if(Objects.equals(onlineModel, fxm)) { - fxm.getOnlineProperty().set(HEAVY_CHECK_MARK); + fxm.setOnlineProperty(true); break; } } @@ -616,7 +612,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { } }; ExecutorService executor = Executors.newSingleThreadExecutor(r -> { - Thread t = new Thread(r); + var t = new Thread(r); t.setDaemon(true); t.setName("RecordedModelsTab UpdateService"); return t; @@ -645,44 +641,49 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { if (selectedModels.isEmpty()) { return null; } - MenuItem stop = new MenuItem("Remove Model"); + var stop = new MenuItem("Remove Model"); stop.setOnAction(e -> stopAction(selectedModels)); - MenuItem recordLater = new MenuItem("Record Later"); + var recordLater = new MenuItem("Record Later"); recordLater.setOnAction(e -> recordLater(selectedModels)); - MenuItem copyUrl = new MenuItem("Copy URL"); + var copyUrl = new MenuItem("Copy URL"); copyUrl.setOnAction(e -> { Model selected = selectedModels.get(0); - final Clipboard clipboard = Clipboard.getSystemClipboard(); - final ClipboardContent content = new ClipboardContent(); + final var clipboard = Clipboard.getSystemClipboard(); + final var content = new ClipboardContent(); content.putString(selected.getUrl()); clipboard.setContent(content); }); - MenuItem pauseRecording = new MenuItem("Pause Recording"); + var pauseRecording = new MenuItem("Pause Recording"); pauseRecording.setOnAction(e -> pauseRecording(selectedModels)); - MenuItem resumeRecording = new MenuItem("Resume Recording"); + var resumeRecording = new MenuItem("Resume Recording"); resumeRecording.setOnAction(e -> resumeRecording(selectedModels)); - MenuItem stopRecordingAt = new MenuItem("Stop Recording at Date"); + var stopRecordingAt = new MenuItem("Stop Recording at Date"); stopRecordingAt.setOnAction(e -> setStopDate(selectedModels.get(0))); - MenuItem removeTimeLimit = new MenuItem("Remove Time Limit"); + var removeTimeLimit = new MenuItem("Remove Time Limit"); removeTimeLimit.setOnAction(e -> removeTimeLimit(selectedModels.get(0))); - MenuItem openInBrowser = new MenuItem("Open in Browser"); + var openInBrowser = new MenuItem("Open in Browser"); openInBrowser.setOnAction(e -> DesktopIntegration.open(selectedModels.get(0).getUrl())); - MenuItem openInPlayer = new MenuItem("Open in Player"); + var openInPlayer = new MenuItem("Open in Player"); openInPlayer.setOnAction(e -> openInPlayer(selectedModels.get(0))); - MenuItem switchStreamSource = new MenuItem("Switch resolution"); + var switchStreamSource = new MenuItem("Switch resolution"); switchStreamSource.setOnAction(e -> switchStreamSource(selectedModels.get(0))); - MenuItem follow = new MenuItem("Follow"); + var follow = new MenuItem("Follow"); follow.setOnAction(e -> follow(selectedModels)); follow.setDisable(!selectedModels.stream().allMatch(m -> m.getSite().supportsFollow() && m.getSite().credentialsAvailable())); - MenuItem ignore = new MenuItem("Ignore"); + var ignore = new MenuItem("Ignore"); ignore.setOnAction(e -> ignore(selectedModels)); - MenuItem notes = new MenuItem("Notes"); + var notes = new MenuItem("Notes"); notes.setOnAction(e -> notes(selectedModels)); - MenuItem openRecDir = new MenuItem("Open recording directory"); + 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 -> addToGroup(selectedModels.get(0))); + var editGroup = new MenuItem("Edit group"); + editGroup.setOnAction(e -> editGroup(selectedModels.get(0))); + ContextMenu menu = new CustomMouseBehaviorContextMenu(stop, recordLater); if (selectedModels.size() == 1) { menu.getItems().add(selectedModels.get(0).isSuspended() ? resumeRecording : pauseRecording); @@ -693,6 +694,8 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { } else { menu.getItems().addAll(resumeRecording, pauseRecording); } + Optional modelGroup = Config.getInstance().getModelGroup(selectedModels.get(0)); + menu.getItems().add(modelGroup.isEmpty() ? addToGroup : editGroup); menu.getItems().addAll(copyUrl, openInPlayer, openInBrowser, openRecDir, switchStreamSource, follow, notes, ignore); if (selectedModels.size() > 1) { @@ -706,6 +709,16 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { return menu; } + private void addToGroup(Model model) { + new AddToGroupAction(this.getContent(), recorder, model).execute(); + table.refresh(); + } + + private void editGroup(Model model) { + new EditGroupAction(this.getContent(), recorder, model).execute(); + table.refresh(); + } + private void setStopDate(JavaFxModel model) { new SetStopDateAction(table, model.getDelegate(), recorder) // .execute() // @@ -735,7 +748,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { } private void switchStreamSource(JavaFxModel fxModel) { - String couldntSwitchHeaderText = "Couldn't switch stream resolution"; + var couldntSwitchHeaderText = "Couldn't switch stream resolution"; try { if (!fxModel.isOnline(true)) { @@ -781,7 +794,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { } private boolean stopAction(List selectedModels) { - boolean confirmed = true; + var confirmed = true; if (Config.getInstance().getSettings().confirmationForDangerousActions) { int n = selectedModels.size(); String plural = n > 1 ? "s" : ""; @@ -829,9 +842,9 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { Config.getInstance().getSettings().recordedModelsSortType = col.getSortType().toString(); } int columns = table.getColumns().size(); - double[] columnWidths = new double[columns]; - String[] columnIds = new String[columns]; - for (int i = 0; i < columnWidths.length; i++) { + var columnWidths = new double[columns]; + var columnIds = new String[columns]; + for (var i = 0; i < columnWidths.length; i++) { columnWidths[i] = table.getColumns().get(i).getWidth(); columnIds[i] = table.getColumns().get(i).getId(); } @@ -862,8 +875,8 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { private void restoreColumnOrder() { String[] columnIds = Config.getInstance().getSettings().recordedModelsColumnIds; ObservableList> columns = table.getColumns(); - for (int i = 0; i < columnIds.length; i++) { - for (int j = 0; j < table.getColumns().size(); j++) { + for (var i = 0; i < columnIds.length; i++) { + for (var j = 0; j < table.getColumns().size(); j++) { if(Objects.equals(columnIds[i], columns.get(j).getId())) { TableColumn col = columns.get(j); columns.remove(j); // NOSONAR @@ -876,7 +889,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { private void restoreColumnWidths() { double[] columnWidths = Config.getInstance().getSettings().recordedModelsColumnWidths; if (columnWidths != null && columnWidths.length == table.getColumns().size()) { - for (int i = 0; i < columnWidths.length; i++) { + for (var i = 0; i < columnWidths.length; i++) { table.getColumns().get(i).setPrefWidth(columnWidths[i]); } } @@ -930,25 +943,4 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { return tableCell; } } - - private class RecordingCell extends TableCell { - @Override - protected void updateItem(String value, boolean empty) { - super.updateItem(value, empty); - if (value == null) { - setTooltip(null); - setText(null); - } else { - Model m = getTableView().getItems().get(getTableRow().getIndex()); - if (m.isRecordingTimeLimited()) { - Tooltip tooltip = new Tooltip(); - DateTimeFormatter dtf = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT); - ZonedDateTime zonedDateTime = m.getRecordUntil().atZone(ZoneId.systemDefault()); - tooltip.setText("Recording until " + dtf.format(zonedDateTime)); - setTooltip(tooltip); - } - setText(value); - } - } - } } diff --git a/client/src/main/java/ctbrec/ui/tabs/RecordedTab.java b/client/src/main/java/ctbrec/ui/tabs/recorded/RecordedTab.java similarity index 96% rename from client/src/main/java/ctbrec/ui/tabs/RecordedTab.java rename to client/src/main/java/ctbrec/ui/tabs/recorded/RecordedTab.java index 38a112d4..547fdd33 100644 --- a/client/src/main/java/ctbrec/ui/tabs/RecordedTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/recorded/RecordedTab.java @@ -1,10 +1,11 @@ -package ctbrec.ui.tabs; +package ctbrec.ui.tabs.recorded; import java.util.List; import ctbrec.recorder.Recorder; import ctbrec.sites.Site; import ctbrec.ui.ShutdownListener; +import ctbrec.ui.tabs.TabSelectionListener; import javafx.beans.value.ChangeListener; import javafx.geometry.Side; import javafx.scene.control.Tab; diff --git a/client/src/main/java/ctbrec/ui/tabs/recorded/RecordingTableCell.java b/client/src/main/java/ctbrec/ui/tabs/recorded/RecordingTableCell.java new file mode 100644 index 00000000..ffd35375 --- /dev/null +++ b/client/src/main/java/ctbrec/ui/tabs/recorded/RecordingTableCell.java @@ -0,0 +1,47 @@ +package ctbrec.ui.tabs.recorded; + +import static ctbrec.ui.Icon.*; + +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.util.Map; +import java.util.Objects; + +import ctbrec.Model; +import ctbrec.SubsequentAction; +import javafx.scene.image.ImageView; + +public class RecordingTableCell extends IconTableCell { + + public RecordingTableCell() { + super(Map.of( // + CHECK_16, new ImageView(CHECK_16.url()), // + CLOCK_16, new ImageView(CLOCK_16.url()))); + } + + @Override + protected void updateItem(Boolean value, boolean empty) { + tooltip = null; + hide(CHECK_16); + hide(CLOCK_16); + if (value == null || empty) { + return; + } + + if (Objects.equals(value, Boolean.TRUE)) { + show(CHECK_16); + tooltip = "Recording"; + } + + Model m = getTableView().getItems().get(getTableRow().getIndex()); + if (m.isRecordingTimeLimited()) { + show(CLOCK_16); + var dtf = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT); + var zonedDateTime = m.getRecordUntil().atZone(ZoneId.systemDefault()); + String action = m.getRecordUntilSubsequentAction() == SubsequentAction.PAUSE ? "Pause" : "Remove"; + tooltip = action + " at " + dtf.format(zonedDateTime); + } + super.updateItem(value, empty); + } +} \ No newline at end of file diff --git a/client/src/main/resources/16/check-circle.png b/client/src/main/resources/16/check-circle.png new file mode 100644 index 0000000000000000000000000000000000000000..2c3c102448d768624883f6ec376fbbb8157a0ad2 GIT binary patch literal 4962 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 HFK)vhOx+K} literal 0 HcmV?d00001 diff --git a/client/src/main/resources/16/clock.png b/client/src/main/resources/16/clock.png new file mode 100644 index 0000000000000000000000000000000000000000..00c9e221299342b528fcbd2fda480ec7a34c9c73 GIT binary patch literal 4949 zcmeHKX;f3!77kh}qu}hKh}WPf;!SSm!9+y@1|%{B5D~?jo11Wj3{3(FgNle!nNg~J z6kBKo)Cx+W${->t&jCfO^wC;H6h-O@u$7Q}|y3lnbK}6|$@`wGgmL2?CFnZWq3NsC|x>J}VT= z;BRrh-n;m?y4D7~1{9 zGVT63r%eVM+38>H`VRU%6+^xAw$duA61peVUE()+`p(`(eUyCFjgvU$_Q?@yY=WrNB^nO2nJzCLY-e>_-mGSea4m-llkm6a66oYi$! zBq5YmS&5jgKQ^Wpn;koI`6RjO+WM!zIdEKlPs#PQcTQ}CgYN_^n~|?A<^>Eb4e3vB zFG>0&{ninR)r!cCaR(EUjy+BU%O@n)rv6fK#^I!gM9kGjJ;?1XG@m%5ucH0ph-;EP ztlUYP57^Uo2bM%$@k(KHp9NWPn6|1FD~{Z{yI^6i4Z9^unTS+nqYuty(5!(V>WRG* z{ms9NIX-XO5{~AOM7tpP>gNB3Rmm>7Mz#F%nEU+5?gipzS$D|MT7%|EZ@1Ga0~r2M z>bLvCj~ZNyLwv)E!UtlSlEPRW($!xo<0`M7{Qjq1L)c-g`D%V@NLb&k;;w?8n)SmQ z@j}VQT)Y%AUNbsXLXF^qm!Md3TFR>;p3?_P#qp7vAoDnUl~tGjL>~GBMZM z`nEm?zb(Aqq5U%{ETVx4`uuEJrl?MKF#pwV;;yw%Yqz$YzO}32MdyN{sqvQeDH~Uk zi}ts^a;6VFEb84vIUiho*^)poOTj!m{JlLqUP~Vre8HAnzVO@%$EFBzdhp^o zy}-SkBJR2=Gd^uwJiTb;p&4N@P|c8aK7GC`DJwauu`#>p$d2^N^2z1Nz%#RZ-Q5R! zhznkl!K%*&I;-bC`knuKhuO;dEA;Q899wn2`YyO9aj9Dz+dxcmN||#0iz`czHGB5- zJ4Oum4?H3LyYkev7o3r{f+oV(LSD1Y4VMn!tBxw$mOsx=3CRn-r@a@jmn3|CF{b#E zs=%6L&7(<5#DnLjNGe(zeI`|Yb9jp1lvTl^(!_Z;q@NGYl$V90WeJaG?b3`~rnX~~ z=WX{~;+||_9-QaDD|2=k*L7xK)!gBJyV|Y0!>ww3Hr)?hVeTMpw|hCMq3(yrd#7DJ z=b=?^_Wg8xTl7ZS)-!l97 zE1O^PHeB)(UwpWIm^M66Y*``NDkPirsK?Kn{XBBA7` zJ9bOkHMtYA{+NlkCS@J#%oY<{F)#nJ__MTiOZK+@eEuXK!NIDz@Ug$YPkkJ(S*yz z-;Kj$B49GnMe&KDB7eX`r9lBYiB5t*PXiW9B`&i9cp9mU8z}G^gTTM|#7Lb^%_Wod zdOb-`BdIiUGKIt8kRd9WN(FHXP#dSz!3I#Nbv8nbVhB(zqQTTUOr->jn6N|@qvI2a z_&o4BKZRN(dIPW2j8+(L|fdks@R?8dcz?T0AP{osg@%MgDI*j1z&+;Gp2IJCxW}b!F|X2b?qi&+)5rH_O1R%&&y5jPcy#lU+Re^A#4y55U{_cH#VuKycdR&NhHs1pAdq{ojlH@i!3;76g4 zC978nOot-EA0Bnv@Hb1faGjPwu%2eT%n11fv+==6I&YEZqzC4fb`~r_=4S!;d6(iX za1UZPXWpn-6BIDryp`eCXz37+@Nuc8%d0(Po#pzRd0y$64ms+*J35 zi>$Z@8{-vTcj8z7XUT+I!}iR@4O`OY*kx}hfc=|}be;OfE&fngO>^jE(ly-I=nBFEEmRQEZmv- z?T=oDI~3uD>z&pu&F-7CA1|T>Aj_)td3F1+jEKhS(2~!;uVEnv_REeAGGFd%YWEEB UNBNE^g+{u)J=X}zS8huF9}TZ*ivR!s literal 0 HcmV?d00001 diff --git a/client/src/main/resources/16/users.png b/client/src/main/resources/16/users.png new file mode 100644 index 0000000000000000000000000000000000000000..cb1ce7035d61e63cd0794216ff21b9e4d2e9c055 GIT binary patch literal 4966 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 AD*ylh literal 0 HcmV?d00001 diff --git a/client/src/main/resources/32/check-small.png b/client/src/main/resources/32/check-small.png new file mode 100644 index 0000000000000000000000000000000000000000..540010a158e6e7b54199bf830dc903f02ebd5fbf GIT binary patch literal 317 zcmV-D0mA-?P)VHN!vJxO^VUxTyTqe&q9Xv$jmzXUppTucU z_JNH^ZW)ge`2}Xm%3(WF`i}J=vx28c?!2me$X=xU4QmDR$~O;bVLvhAIr97r)1#7y zBMu_6+FpBVM7KbGqJfW03x|ItPmSnds%q(Pf5$zH;{`kTtU4f*$z;9(W8P_}EbeX& P00000NkvXXu0mjfr+A2D literal 0 HcmV?d00001 diff --git a/client/src/main/resources/32/check-small.svg b/client/src/main/resources/32/check-small.svg new file mode 100644 index 00000000..2980ea7a --- /dev/null +++ b/client/src/main/resources/32/check-small.svg @@ -0,0 +1,57 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/client/src/main/resources/32/check.png b/client/src/main/resources/32/check.png new file mode 100644 index 0000000000000000000000000000000000000000..bc8b618bc070631a96dd2a8cece503b5ba68aa0a GIT binary patch literal 291 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzwj^(N7l!{JxM1({$v}}Co-U3d z5v^~h`f?pI5NO>Wt?`vf@0K=)?!Bn|2T@D&b_>LG-`?fHwD5vj=D&bX31y!9k3Tsp z($#foCDXYBvKhgL%@2r5q}DbhYgMp*k%}?D__S%YRM8>#Bb!s2_cy4q)ky8Q-?+U) z(pE9*h1U9Q@(tU|J}|zKc@X_zo7g?Yw4$no$JYtpYm^q#o8`mexm(kuSgolyN|}Gx z3=X^J)eka%EZ|xe{$N_l?amd_=avayG`|xi?09|k>Q0T_cdxl@k?-#9Tv0H&Q{{uD mu;)WB(}Ibi%iF~NuHSbfkimxUaiTKNj|`r!elF{r5}E++Zh0{P literal 0 HcmV?d00001 diff --git a/client/src/main/resources/32/clock.png b/client/src/main/resources/32/clock.png new file mode 100644 index 0000000000000000000000000000000000000000..a7ad0f32e00e559d5c51822cdeef5746280d44c2 GIT binary patch literal 757 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 diff --git a/client/src/main/resources/kofi-round.png b/client/src/main/resources/kofi-round.png deleted file mode 100644 index 412a0a6661742ee9e11c58086a1d4c3ede58820a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13904 zcmV-WHm}KvP)dQ@0+Qek%> zaB^>EX>4U6ba`-PAZ2)IW&i+q+O3;savi&Nh5us|9s+V04q}yO;NkmQ=q4#jx_VbG zizS|;6FTg$!Ukma|NQqc|HYr;iZ?Npnp@76Ke5H;J5Q>8{O-{#v3g($7WiGue#xM1(^_*_AF{&xN*e!IX$@w@Rn zglu?-A-@MBAm4898IDj=V|

Xnrf|2HSlch7S%x9`4sNKZU=E=jHx1eyZ(u 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 Q1PIdCy{1#Hg^Kup00ftwGXMYp diff --git a/client/src/main/resources/patreon-round.png b/client/src/main/resources/patreon-round.png deleted file mode 100644 index 22c9edac95c448290334b180bdfd0b93bb42fbfd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7770 zcmV-g9;M-lP)dq2w{R0LwVyEf*nIHWpCkl z<0ShbwfiYmsY>!;D_LhI0%UWw2`HG0gl_1HE+frNGo!h5pY!gA z_i3p$jAo`Ur{{F{^RGJ9qtQq+bI$Yc=lMUk7ZeJ`MPc+7L0qg-ziZ|+)ze^iqtUhHWNFiRL zUdYf6%m=OpZW8a%EJSD);&?H;pIM_1g$RQ}oB?1r@H+4wa6-bheqcnsixOlyq-X=? z0ZW7sHw#I!TB2cGI8-seq7Y^{N4xX@uK=$CA4*u(2MjA&lpxvgsZKn_d6VUf3jp5 z5K^23o)@wl0J?yoTFi=>8Y4zh2+#>!2W$pbinnOPWtD0@?97s-SG>wD;As*1Mhr*2 z^%!OKCk`@U+ZrI-pQh7pmyNW!T5fh$#G%otI6fc?M^B>XyvD>_;wNcqW; z5S_6O_%g5-xD1!EQp~KFKL)%BJPN!4bQxnZN|5q-jTT^q=#0C?Yc!!XjMzLTTr10d z;BnC>yMVz;3%g8(kt2a?3E2RAO-RzNIwSUB*KXipTxI@5C3%xXg%HEwngv$?_sF#j zSLaZ~+rF~xJK5~w&#bk(fU}jLO-u!l!^2g<*8^V%*5HbN>NP6Qn{)$ji#K@{=#G1T zmx(z!lDH|oyMPDe9jIQlMn!9r5z!;x7j1Gl&UF(LQ(|O8s%wDzgdmI13{|xt4IBcV z0e*-Z-il?&WnxHX1AGVAhO3zbN|1@k(TS^TJSciYvsAT#G0`R8#nnxY#w8VOVnU82ZdT(y z;7ho|pSFtBo-X+icucPSaS-n&2IQy{Z}EV5kA-Low^otCO)Wky-sA&g%xKLL#9CXA z+bi{T;7;7WDHY=?-n)RGws_ICjC0l(N~2-h$||-fXi_0Fvbkm z3_*k(*8|_ewW+r$ITYi~1eXSW5%_!Ht(hQ+Hv{BIiRbuJ;5J;1L&>3-=*_!LY~)XY zPs$r>^%6t``D$DN@HSjQP-`5DsYr6G*vx8qgRMS-NGf-w2=a{@Rw?oz$!xKiuLCQ^ zma2y!GRLt5R{*?I1-YUCl5~jeJP2GVHdXlq$*GXvhpTg3qU2B%Xsh#Z3pwruI>ojs zmmt!3I9GnDF9}h!&Y>t2**DvJ<;`}U*jVKeL|WEcagB$6fNLMo3b~?KB*c~;6x(VQ zTZ`S%9msd#I$W1|oR?y}ZhWM2VeT!?QB zcm_Nld>@mnlfR2CNmhw%_2OzJLlr|1$ueAvtCFuK!a=9wr+fJ9SN@#g(NRo^GR8p| zaMy-)Jo49n5q|<$d%>H7?eE3*oW>5GfwM!{BL|?n3p>ycqhpX6gG?Hm9s>*}QHMY-^CrlSLMNhhXbE;O}ZX18P7I^y=L=i1mQYwZqW%vkIRA~E6RBFeAB&2vPW z-sm4ZOW&D+l6jF#5Kv!V8J+gV7{X&m2@mYX9z6^vj$*sJU}ykFhGBFBJny`x$lf&h z*kaEe*k;AE*w8{U35`vdrWRm`yv+YXv2}ur?dX^+nVsd2D zad+W%R<%=xLZC6uP)bD*6W`F{0*?{y*-7S=ZScXnFg6P5F&9dm3!Q*u-PCU}W&$Y; z&I1h%Lf3KZ!F`ZOVp8>lOIP4sw;q4(r)T{A0aqir3%7&oF>CFqQjYGH+JiKT?)WNh zhFmjN5s)Lj?Fo3}Wx_-IG3UGk7ZRhC?>+!w2xFs8)9m;a!H(Ano?eK5+ZMbVKkIah zHzV15Uu^BGxJBCAaEF%0iy%_CSpocK5l}ktR6=iIdrlMX+eP^53)r1+gXd%X8G8T8 zhHJ-;5Iph^1g|`gw{|`L4eK#&?K7oER^ck`N36BKDb=u5qC^kU;5HuK!}&RKMG44Z zM}`UB{x$Zs?bri*U}OXmF?sLtJgBdSgL?=M?ZdwNCg$3;ByQgV#(XS+5HV7?ooV*~ zC#n z06FLRPN(d7pWwh=!i`%<-1VPaO{IK#WSQ9FUTf`t6dP(KiX?}@wUTTGmZOW^iYLeL zFu}k63*k%8LNXQSV%_9Ok71wwF+tC1{LNp)%$r}X?_4LgxCJ3#JMu0sI8?_Yyqu7z6>{#x3Q@ZZ+ce z9^8t{YV{TpYIy}d*n|l3`*Fvf>3l~aX%}M%f4P;+qyJmTQRT_M;j;w)_J6Q1Z^LFH zUz}*fHt(m1YH7uFeUc|oejJ(m+P}#^B@I1nQ{Vw75pPvggBSgE{ z>Pia8vU+)vBR8#kg$nb6;#2JIorF*R7zWPN<~=Y5f`H(O9}&FyTGV&>A3=uTM-M~KsoDsuCL_tnS;EJEMEK#JsJ~k)ZhL3mCk9NLAi}&k z;yrZ1bV1|@2)^^T*rSJBACsmjuqTcZZhI0NtvB^?GfJC*Ir(^ze4bJru3Pt3^&W-j z4hVks1HyOTn#PPq4Iz;reDh_(U;iR%Ju(Zpg*-bx@*zkzbETU}3PAAouL)n@9?xdO znTDSrc>FPfx11h{h$PvKD4zp5rt3wfMG%=Wzf$5j^&a_pk5ebHTYu_0aupNnk@OhB zPri>m(-);^Z^Lb9T*5TV6J8$w@h!jt)gJjH-19#6=)u|+<WamCc$_T7Ic-1kA0-=ZCt@}1UZ#_4#FCfw$T1!&cL z`VU&-X#3K0*uw|T4HYS-xNp7P1g~tvjz)GANJ4D+dfcF2;RF#OGf%unhd#c1A_hZn zXg`@3pLQ#B6w`FMA$IRhY)^O8UgR#OQ8=8+gVf=wkSozS^ZcLnbB|*(Y1JP2YLS!2 z2w&QY4I(=(y+Lex2~!jfry@ujZq{6nE6A4|#|{&|yQ4BjW#fiK68rWWFc?|!FkAHA zz!bZEy-C6=4|i0-?YIdoMIH!#xfPR9VP2>gu_up{dH%@@^jwM2CboSElXQzt<`wF3 z1Mu_F!eD;hqw6H>e*gS}Xhr_0OA)@l9XmP_Rb3<@dqx=rD z#Kz}cj0@f*;nflq=LMj@7rW>E>YOc~34$Cxfc@xTRNuiD8(+djvb5e5%$lX)RUY2M zTI@g{_V`hE7>%NMjE)fO+a0wR`SisiC2xX6m{Q@X0P&P2;q@~It*1x1OefZ1BAViRn~p6_4!QDN;T1wYyz&q z?G@6u!Tf?|_(-P(ORwwt+!Y;~fUD(oN`kab7LzF^@gGMIV7rbhK}s#uhX$}ik*3Yh z>5%d1UT>WK@Pr`C#e*pFA;_NhA;{>BC^ZK9u|3^U?$K(%jgz;HkD9!T=#OR%ukuHD zs!P2`L^_0%N28pn@xzbHXk zaDyZ*`cw)Cu}+`V*7j(G2zH<^YJxQ51_PU?BFME9sd>dD#K<-!4xLegL?zTm&Vmi2 zCdg`_C0D3>IS<*FFKR~HOY(evazBShlps+dlZG(T?&z!zX(6Zn=ZzESbI`f-!UTG6 zH|YFFR1p>?ordW5!GDYcxdch!)-%?tKraAi`t-I%sX;R7a?ICcIgpx&AdO5hJffIH zhv@uA#35Fu90X}(TqY}*AlYJx76=PLHJvLMQ=wi4oZ~=NsOJ)-9(QDwZdNWt;WcY# zPPFQvt{fyu;dW-#U*JKc#Uv%<&_KG75UZ_2XFwvmxfYX5mV+QhIFQQnz{?>@6WZQh zxCd!yz@!>FgQ13$oAr>vOz%Ls?W=}+rcC<)XoNsQQqajvQ8li|Tf?l93-rboXlO3iH=HoU@|7SZK&k=LdASPp(((glwYsZ3@NpU4gtgY>5~LYT z^A;In85d@Sf!>Rf-BVz8JEmi9ISF!(3%LYoMCU+?C&-m6AfZyObOBEtX7Msi`R-|! z3l|W?!TAVu-RU>05hb1zXHQZmV$#LVw3tCVDdRuh932}?tg#wNT4i&Te{>VD3i zgTLm6atC#Z@dQzn>h89*Vy?apwC$vLgdt|tni>5OyqsJ?hBC}F)&xl|?ZmX`-tXe| zM*_2ML%F}}1zgAyBm~CfRZ%QKu2=^1FVh=Q41x?SU5>wSaa3m%Ues>^qZ1M2tP-R| z1o3^m)t}NOazzm&#Q&`?y8Bt)O)()b3* zl{4|(Gr+)jf&@Un{H3B)0`Jz}Qi2rp?vlj&)CSDVoG~8&XMn&MV{<)7uM(t$O3zvn z>u<+gb&XqkrkG}c6Jl1b#;jO9Q%m?VKo5}4^&sioPbx~FL*(K5pT8HHTh)_H7Y&Vg zYt~{~XV276kmW*VJVCnEgG4}-%df!u zX%nO$P4h-W5YNZo{6$!_M0>O*!q_ORz5(y<`!3LLGf0p#;|UT1LrRbcNz&9z^7kKt zhDHswauJ4b=>ig8_zI@6ITk-rE*CsuY__~UrXf~@B)PPc#P2?U)x{1ux+95y=NIrU z?To`uV}%1BYc&BF0glNRD5ALg*5ci?5&yO=PLI^OCou-^hR@*NaC2M^9vTHc5-u>F zAj7~1xSlLUB=#V+kuQ81v-T!P)bI*`Od50b8vNh+Dx~V;@>2~1yMeRDn2&jov%ntt zB1M!$N!F3L|7)0Kt7=oUkBwrkxt_!W-^4V>>b%&GaljkrUXF-+Q5_OBN$OJg4}24| zcv;k^x5mtKWH2i~LE?}94AT_5!<0vX!}9tuf~0{{XtA(-dZe|D`X6dS$IsDTs zT}k4PzJ+OwZ`VGwbjn$Xk3PY>~f8!SX2mcG+ zy!mncNr%Mt2eJbFVgz|hUKQmeOMN|wd;bvs{;#>LPBko_3j=7LMe>ing?G>I#np3+ zxBbD~Pma$s4g-7T)uL;F$_{`1ZFrrF$UO2tv0cY1**)T2+KKo358*9YUI{;OuI+O% z50VDDghiUknju|c7F|i=zx^HlEq7vO nPu!SJNw6tN?-;V##Useh^vijpB;|k@8 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 diff --git a/client/src/main/resources/paypal-round.png b/client/src/main/resources/paypal-round.png deleted file mode 100644 index 481490eafcbc5bfbe5050400126c777ea7c8a45c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8842 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 modelNotes = new HashMap<>();