From 38b898f405ae169cb2561e9d702a93ed9e7be830 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Fri, 24 Dec 2021 15:38:29 +0100 Subject: [PATCH] Add setting to customize the date time format in the GUI --- .../java/ctbrec/ui/CamrecApplication.java | 2 +- .../ui/controls/DateTimeCellFactory.java | 19 ++++--- .../src/main/java/ctbrec/ui/news/NewsTab.java | 16 +++--- .../main/java/ctbrec/ui/news/StatusPane.java | 35 +++++------- .../java/ctbrec/ui/settings/SettingsTab.java | 54 ++++++++++++++++++- .../ctbrec/ui/tabs/RecentlyWatchedTab.java | 4 +- .../java/ctbrec/ui/tabs/RecordingsTab.java | 2 +- .../recorded/AbstractRecordedModelsTab.java | 2 +- .../ui/tabs/recorded/RecordedModelsTab.java | 4 +- common/src/main/java/ctbrec/Config.java | 53 +++++++++--------- common/src/main/java/ctbrec/Settings.java | 2 + 11 files changed, 122 insertions(+), 71 deletions(-) diff --git a/client/src/main/java/ctbrec/ui/CamrecApplication.java b/client/src/main/java/ctbrec/ui/CamrecApplication.java index 7dd22304..e8467040 100644 --- a/client/src/main/java/ctbrec/ui/CamrecApplication.java +++ b/client/src/main/java/ctbrec/ui/CamrecApplication.java @@ -252,7 +252,7 @@ public class CamrecApplication extends Application { tabPane.getTabs().add(new RecentlyWatchedTab(recorder, sites)); } tabPane.getTabs().add(new SettingsTab(sites, recorder)); - tabPane.getTabs().add(new NewsTab()); + tabPane.getTabs().add(new NewsTab(config)); tabPane.getTabs().add(new DonateTabFx()); tabPane.getTabs().add(new HelpTab()); tabPane.getTabs().add(new LoggingTab()); diff --git a/client/src/main/java/ctbrec/ui/controls/DateTimeCellFactory.java b/client/src/main/java/ctbrec/ui/controls/DateTimeCellFactory.java index 41c0d4da..50f5f0ad 100644 --- a/client/src/main/java/ctbrec/ui/controls/DateTimeCellFactory.java +++ b/client/src/main/java/ctbrec/ui/controls/DateTimeCellFactory.java @@ -1,16 +1,22 @@ package ctbrec.ui.controls; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.time.format.FormatStyle; - import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.util.Callback; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; + public class DateTimeCellFactory implements Callback, TableCell> { + + private final DateTimeFormatter formatter; + + public DateTimeCellFactory (DateTimeFormatter formatter) { + this.formatter = formatter; + } + @Override public TableCell call(TableColumn param) { return new TableCell() { @@ -20,7 +26,6 @@ public class DateTimeCellFactory implements Callback, setText(""); } else { var dateTime = LocalDateTime.ofInstant(item, ZoneId.systemDefault()); - var formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT); String formattedDateTime = formatter.format(dateTime); setText(item.equals(Instant.EPOCH) ? "" : formattedDateTime); } diff --git a/client/src/main/java/ctbrec/ui/news/NewsTab.java b/client/src/main/java/ctbrec/ui/news/NewsTab.java index 3baacdf8..e615ebc0 100644 --- a/client/src/main/java/ctbrec/ui/news/NewsTab.java +++ b/client/src/main/java/ctbrec/ui/news/NewsTab.java @@ -5,6 +5,7 @@ import static ctbrec.io.HttpConstants.*; import java.io.IOException; import java.util.Objects; +import ctbrec.Config; import org.json.JSONObject; import com.squareup.moshi.JsonAdapter; @@ -26,10 +27,11 @@ import okhttp3.Request; public class NewsTab extends Tab implements TabSelectionListener { private static final String ACCESS_TOKEN = "a2804d73a89951a22e0f8483a6fcec8943afd88b7ba17c459c095aa9e6f94fd0"; private static final String URL = "https://mastodon.cloud/api/v1/accounts/480960/statuses?limit=20&exclude_replies=true"; + private final Config config; + private final VBox layout = new VBox(); - private VBox layout = new VBox(); - - public NewsTab() { + public NewsTab(Config config) { + this.config = config; setText("News"); layout.setMaxWidth(800); layout.setAlignment(Pos.CENTER); @@ -46,11 +48,11 @@ public class NewsTab extends Tab implements TabSelectionListener { var request = new Request.Builder() .url(URL) .header("Authorization", "Bearer " + ACCESS_TOKEN) - .header(USER_AGENT, "ctbrec " + CamrecApplication.getVersion().toString()) + .header(USER_AGENT, "ctbrec " + CamrecApplication.getVersion()) .build(); try (var response = CamrecApplication.httpClient.execute(request)) { if (response.isSuccessful()) { - var body = response.body().string(); + var body = Objects.requireNonNull(response.body(), "HTTP response body is null").string(); if (body.startsWith("[")) { onSuccess(body); } else if (body.startsWith("{")) { @@ -79,12 +81,12 @@ public class NewsTab extends Tab implements TabSelectionListener { private void onSuccess(String body) throws IOException { var moshi = new Moshi.Builder().build(); JsonAdapter statusListAdapter = moshi.adapter(Status[].class); - Status[] statusArray = statusListAdapter.fromJson(body); + Status[] statusArray = Objects.requireNonNull(statusListAdapter.fromJson(body)); Platform.runLater(() -> { layout.getChildren().clear(); for (Status status : statusArray) { if (status.getInReplyToId() == null && !Objects.equals("direct", status.getVisibility())) { - var stp = new StatusPane(status); + var stp = new StatusPane(status, config.getDateTimeFormatter()); layout.getChildren().add(stp); VBox.setMargin(stp, new Insets(10)); } diff --git a/client/src/main/java/ctbrec/ui/news/StatusPane.java b/client/src/main/java/ctbrec/ui/news/StatusPane.java index ce15f721..afb4b763 100644 --- a/client/src/main/java/ctbrec/ui/news/StatusPane.java +++ b/client/src/main/java/ctbrec/ui/news/StatusPane.java @@ -1,10 +1,5 @@ package ctbrec.ui.news; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.FormatStyle; -import java.util.Set; - import ctbrec.io.HtmlParser; import ctbrec.ui.DesktopIntegration; import javafx.collections.ObservableList; @@ -12,20 +7,20 @@ import javafx.geometry.Insets; import javafx.geometry.Orientation; import javafx.geometry.Pos; import javafx.scene.Node; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.ScrollBar; -import javafx.scene.control.ScrollPane; -import javafx.scene.control.TextArea; +import javafx.scene.control.*; import javafx.scene.layout.StackPane; import javafx.scene.shape.Rectangle; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Set; + public class StatusPane extends StackPane { TextArea content; Button reply; - public StatusPane(Status status) { + public StatusPane(Status status, DateTimeFormatter formatter) { String text = HtmlParser.getText("
" + status.getContent() + "
", "div"); content = new TextArea(text); @@ -35,7 +30,7 @@ public class StatusPane extends StackPane { getChildren().add(content); ZonedDateTime createdAt = status.getCreationTime(); - String creationTime = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT).format(createdAt); + String creationTime = formatter.format(createdAt); var time = new Label(creationTime); time.setStyle("-fx-background-color: -fx-base"); time.setOpacity(.7); @@ -63,18 +58,14 @@ public class StatusPane extends StackPane { protected void layoutChildren() { ObservableList childrenUnmodifiable = content.getChildrenUnmodifiable(); for (Node node : childrenUnmodifiable) { - if (node instanceof ScrollPane) { - var scrollPane = (ScrollPane) node; + if (node instanceof ScrollPane scrollPane) { Set nodes = scrollPane.lookupAll(".scroll-bar"); for (final Node child : nodes) { - if (child instanceof ScrollBar) { - ScrollBar sb = (ScrollBar) child; - if (sb.getOrientation() == Orientation.VERTICAL) { - if (sb.isVisible()) { - StackPane.setMargin(reply, new Insets(5, 22, 5, 5)); - } else { - StackPane.setMargin(reply, new Insets(5, 5, 5, 5)); - } + if (child instanceof ScrollBar sb && sb.getOrientation() == Orientation.VERTICAL) { + if (sb.isVisible()) { + StackPane.setMargin(reply, new Insets(5, 22, 5, 5)); + } else { + StackPane.setMargin(reply, new Insets(5, 5, 5, 5)); } } } diff --git a/client/src/main/java/ctbrec/ui/settings/SettingsTab.java b/client/src/main/java/ctbrec/ui/settings/SettingsTab.java index e7fadb0b..bb0b70b0 100644 --- a/client/src/main/java/ctbrec/ui/settings/SettingsTab.java +++ b/client/src/main/java/ctbrec/ui/settings/SettingsTab.java @@ -119,6 +119,7 @@ public class SettingsTab extends Tab implements TabSelectionListener { private LocalTimeProperty timeoutRecordingStartingAt; private LocalTimeProperty timeoutRecordingEndingAt; private SimpleLongProperty recordUntilDefaultDurationInMinutes; + private SimpleStringProperty dateTimeFormat; public SettingsTab(List sites, Recorder recorder) { this.sites = sites; @@ -189,6 +190,7 @@ public class SettingsTab extends Tab implements TabSelectionListener { timeoutRecordingStartingAt = new LocalTimeProperty(null, "timeoutRecordingStartingAt", settings.timeoutRecordingStartingAt); timeoutRecordingEndingAt = new LocalTimeProperty(null, "timeoutRecordingEndingAt", settings.timeoutRecordingEndingAt); recordUntilDefaultDurationInMinutes = new SimpleLongProperty(null, "recordUntilDefaultDurationInMinutes", settings.recordUntilDefaultDurationInMinutes); + dateTimeFormat = new SimpleStringProperty(null, "dateTimeFormat", settings.dateTimeFormat); } private void createGui() { @@ -229,6 +231,7 @@ public class SettingsTab extends Tab implements TabSelectionListener { Group.of("Look & Feel", Setting.of("Colors (Base / Accent)", new ColorSettingsPane(Config.getInstance())).needsRestart(), Setting.of("Font", new FontSettingsPane(this, config)).needsRestart(), + Setting.of("Date format (empty = system default)", dateTimeFormat, DATE_FORMATTER_TOOLTIP).needsRestart(), Setting.of("Display stream resolution in overview", determineResolution), Setting.of("Total model count in title", totalModelCountInTitle, "Show the total number of models in the title bar"), Setting.of("Show grid lines in tables", showGridLinesInTables, "Show grid lines in tables").needsRestart(), @@ -588,5 +591,54 @@ public class SettingsTab extends Tab implements TabSelectionListener { } } - + private static final String DATE_FORMATTER_TOOLTIP = """ + Leave empty for system default + + Symbol Meaning Presentation Examples + ------ ------- ------------ ------- + G era text AD; Anno Domini; A + u year year 2004; 04 + y year-of-era year 2004; 04 + D day-of-year number 189 + M/L month-of-year number/text 7; 07; Jul; July; J + d day-of-month number 10 + + Q/q quarter-of-year number/text 3; 03; Q3; 3rd quarter + Y week-based-year year 1996; 96 + w week-of-week-based-year number 27 + W week-of-month number 4 + E day-of-week text Tue; Tuesday; T + e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T + F week-of-month number 3 + + a am-pm-of-day text PM + h clock-hour-of-am-pm (1-12) number 12 + K hour-of-am-pm (0-11) number 0 + k clock-hour-of-am-pm (1-24) number 0 + + H hour-of-day (0-23) number 0 + m minute-of-hour number 30 + s second-of-minute number 55 + S fraction-of-second fraction 978 + A milli-of-day number 1234 + n nano-of-second number 987654321 + N nano-of-day number 1234000000 + + V time-zone ID zone-id America/Los_Angeles; Z; -08:30 + z time-zone name zone-name Pacific Standard Time; PST + O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00; + X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15; + x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15; + Z zone-offset offset-Z +0000; -0800; -08:00; + + p pad next pad modifier 1 + + ' escape for text delimiter + '' single quote literal ' + [ optional section start + ] optional section end + # reserved for future use + { reserved for future use + } reserved for future use + """; } diff --git a/client/src/main/java/ctbrec/ui/tabs/RecentlyWatchedTab.java b/client/src/main/java/ctbrec/ui/tabs/RecentlyWatchedTab.java index d8164ad9..cfcb1769 100644 --- a/client/src/main/java/ctbrec/ui/tabs/RecentlyWatchedTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/RecentlyWatchedTab.java @@ -147,8 +147,8 @@ public class RecentlyWatchedTab extends Tab implements ShutdownListener { TableColumn timestamp = createTableColumn("Timestamp", 150, idx); timestamp.setId("timestamp"); - timestamp.setCellValueFactory(cdf -> new SimpleObjectProperty(cdf.getValue().getTimestamp())); - timestamp.setCellFactory(new DateTimeCellFactory<>()); + timestamp.setCellValueFactory(cdf -> new SimpleObjectProperty<>(cdf.getValue().getTimestamp())); + timestamp.setCellFactory(new DateTimeCellFactory<>(config.getDateTimeFormatter())); timestamp.setEditable(false); timestamp.setSortType(SortType.DESCENDING); table.getColumns().add(timestamp); diff --git a/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java b/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java index f4e27b73..b3061c09 100644 --- a/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java @@ -128,7 +128,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener, Shutdown var instant = cdf.getValue().getStartDate(); return new SimpleObjectProperty<>(instant); }); - date.setCellFactory(new DateTimeCellFactory<>()); + date.setCellFactory(new DateTimeCellFactory<>(config.getDateTimeFormatter())); date.setPrefWidth(200); TableColumn status = new TableColumn<>("Status"); status.setId("status"); diff --git a/client/src/main/java/ctbrec/ui/tabs/recorded/AbstractRecordedModelsTab.java b/client/src/main/java/ctbrec/ui/tabs/recorded/AbstractRecordedModelsTab.java index 6a3f7c61..c543599c 100644 --- a/client/src/main/java/ctbrec/ui/tabs/recorded/AbstractRecordedModelsTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/recorded/AbstractRecordedModelsTab.java @@ -315,7 +315,7 @@ public abstract class AbstractRecordedModelsTab extends Tab implements TabSelect protected void addAddedTimestampColumn(int columnIdx) { TableColumn tc = addTableColumn("addedTimestamp", "added at", columnIdx, 400); - tc.setCellFactory(new DateTimeCellFactory<>()); + tc.setCellFactory(new DateTimeCellFactory<>(config.getDateTimeFormatter())); tc.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue().getAddedTimestamp())); tc.setPrefWidth(150); tc.setEditable(false); 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 21a45b16..331c9175 100644 --- a/client/src/main/java/ctbrec/ui/tabs/recorded/RecordedModelsTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/recorded/RecordedModelsTab.java @@ -124,7 +124,7 @@ public class RecordedModelsTab extends AbstractRecordedModelsTab implements TabS table.getColumns().add(priority); TableColumn lastSeen = new TableColumn<>("last seen"); lastSeen.setCellValueFactory(cdf -> cdf.getValue().lastSeenProperty()); - lastSeen.setCellFactory(new DateTimeCellFactory<>()); + lastSeen.setCellFactory(new DateTimeCellFactory<>(config.getDateTimeFormatter())); lastSeen.setPrefWidth(150); lastSeen.setEditable(false); lastSeen.setId("lastSeen"); @@ -134,7 +134,7 @@ public class RecordedModelsTab extends AbstractRecordedModelsTab implements TabS table.getColumns().add(lastSeen); TableColumn lastRecorded = new TableColumn<>("last recorded"); lastRecorded.setCellValueFactory(cdf -> cdf.getValue().lastRecordedProperty()); - lastRecorded.setCellFactory(new DateTimeCellFactory<>()); + lastRecorded.setCellFactory(new DateTimeCellFactory<>(config.getDateTimeFormatter())); lastRecorded.setPrefWidth(150); lastRecorded.setEditable(false); lastRecorded.setId("lastRecorded"); diff --git a/common/src/main/java/ctbrec/Config.java b/common/src/main/java/ctbrec/Config.java index 7470de4f..66a12ec2 100644 --- a/common/src/main/java/ctbrec/Config.java +++ b/common/src/main/java/ctbrec/Config.java @@ -1,7 +1,18 @@ package ctbrec; -import static java.nio.charset.StandardCharsets.*; -import static java.nio.file.StandardOpenOption.*; +import com.squareup.moshi.JsonAdapter; +import com.squareup.moshi.Moshi; +import ctbrec.Settings.SplitStrategy; +import ctbrec.io.*; +import ctbrec.recorder.postprocessing.DeleteTooShort; +import ctbrec.recorder.postprocessing.PostProcessor; +import ctbrec.recorder.postprocessing.RemoveKeepFile; +import ctbrec.recorder.postprocessing.Script; +import ctbrec.sites.Site; +import ctbrec.sites.cam4.Cam4Model; +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; @@ -13,34 +24,13 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; -import java.util.Date; -import java.util.Iterator; -import java.util.List; +import java.time.format.FormatStyle; +import java.util.*; import java.util.Map.Entry; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; import java.util.stream.Collectors; -import org.apache.commons.io.FileUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.squareup.moshi.JsonAdapter; -import com.squareup.moshi.Moshi; - -import ctbrec.Settings.SplitStrategy; -import ctbrec.io.FileJsonAdapter; -import ctbrec.io.LocalTimeJsonAdapter; -import ctbrec.io.ModelJsonAdapter; -import ctbrec.io.PostProcessorJsonAdapter; -import ctbrec.io.UuidJSonAdapter; -import ctbrec.recorder.postprocessing.DeleteTooShort; -import ctbrec.recorder.postprocessing.PostProcessor; -import ctbrec.recorder.postprocessing.RemoveKeepFile; -import ctbrec.recorder.postprocessing.Script; -import ctbrec.sites.Site; -import ctbrec.sites.cam4.Cam4Model; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.nio.file.StandardOpenOption.*; // TODO don't use singleton pattern public class Config { @@ -305,6 +295,15 @@ public class Config { return context; } + public DateTimeFormatter getDateTimeFormatter() { + var dtf = getSettings().dateTimeFormat; + if (StringUtil.isNotBlank(dtf)) { + return DateTimeFormatter.ofPattern(dtf); + } else { + return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT); + } + } + public String getModelNotes(Model m) { return Config.getInstance().getSettings().modelNotes.getOrDefault(m.getUrl(), ""); } diff --git a/common/src/main/java/ctbrec/Settings.java b/common/src/main/java/ctbrec/Settings.java index a58ad1c7..c244bd62 100644 --- a/common/src/main/java/ctbrec/Settings.java +++ b/common/src/main/java/ctbrec/Settings.java @@ -2,6 +2,7 @@ package ctbrec; import java.io.File; import java.time.LocalTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -66,6 +67,7 @@ public class Settings { public int concurrentRecordings = 0; public boolean confirmationForDangerousActions = false; public String contactsheetTimestampLook = "font=sans-serif:fontcolor=white:fontsize=60:box=1:boxcolor=black@0.5:boxborderw=5"; + public String dateTimeFormat = ""; public int defaultPriority = 50; public boolean determineResolution = false; public List disabledSites = new ArrayList<>();