From 27fe4e6f46547c45d19fa39c175fa2104a5feb28 Mon Sep 17 00:00:00 2001 From: Shane Synan Date: Fri, 2 Dec 2016 20:05:20 -0600 Subject: [PATCH] Optionally use system locale for chat timestamp Using QLocale::system().timeFormat(), check if the AM/PM designator exists; if so, assume a 12-hour style timestamp, otherwise assume the previous 24-hour style timestamp. Generate a timestamp of either " hh:mm:ss" or " h:mm:ss ap" in order to include seconds (QLocale::ShortFormat does not specify seconds). Add new setting UseCustomTimestampFormat to switch between system or user timestamp string. Bump settings version minor to keep old behavior for upgrades. --- src/qtui/chatscene.cpp | 54 +++++++++----- src/qtui/chatscene.h | 6 ++ src/qtui/chatviewsettings.h | 15 +++- src/qtui/qtuiapplication.cpp | 27 +++++-- src/qtui/qtuistyle.cpp | 8 +++ src/qtui/qtuistyle.h | 9 +++ .../settingspages/chatviewsettingspage.ui | 33 ++++++++- src/uisupport/uistyle.cpp | 72 ++++++++++++++++++- src/uisupport/uistyle.h | 58 ++++++++++++++- 9 files changed, 251 insertions(+), 31 deletions(-) diff --git a/src/qtui/chatscene.cpp b/src/qtui/chatscene.cpp index 188eba6c..dbe4220e 100644 --- a/src/qtui/chatscene.cpp +++ b/src/qtui/chatscene.cpp @@ -134,6 +134,9 @@ ChatScene::ChatScene(QAbstractItemModel *model, const QString &idString, qreal w _showSenderBrackets = defaultSettings.showSenderBrackets(); defaultSettings.notify("ShowSenderBrackets", this, SLOT(showSenderBracketsChanged())); + _useCustomTimestampFormat = defaultSettings.useCustomTimestampFormat(); + defaultSettings.notify("UseCustomTimestampFormat", this, SLOT(useCustomTimestampFormatChanged())); + _timestampFormatString = defaultSettings.timestampFormatString(); defaultSettings.notify("TimestampFormat", this, SLOT(timestampFormatStringChanged())); updateTimestampHasBrackets(); @@ -1346,6 +1349,13 @@ void ChatScene::showSenderBracketsChanged() _showSenderBrackets = settings.showSenderBrackets(); } +void ChatScene::useCustomTimestampFormatChanged() +{ + ChatViewSettings settings; + _useCustomTimestampFormat = settings.useCustomTimestampFormat(); + updateTimestampHasBrackets(); +} + void ChatScene::timestampFormatStringChanged() { ChatViewSettings settings; @@ -1357,23 +1367,29 @@ void ChatScene::updateTimestampHasBrackets() { // Calculate these parameters only as needed, rather than on-demand - // Does the timestamp format contain brackets? For example: - // Classic: "[hh:mm:ss]" - // Modern: " hh:mm:ss" - // - // Match groups of any opening or closing brackets - (), {}, [], <>, (>, {], etc: - // ^\s*[({[<].+[)}\]>]\s*$ - // [...] is a character group containing ... - // ^ matches start of string - // \s* matches any amount of whitespace - // [({[<] matches (, {, [, or < - // .+ matches one or more characters - // [)}\]>] matches ), }, ], or >, escaping the ] - // $ matches end of string - // Alternatively, if opening and closing brackets must be in pairs, use this: - // (^\s*\(.+\)\s*$)|(^\s*\{.+\}\s*$)|(^\s*\[.+\]\s*$)|(^\s*<.+>\s*$) - // Note that '\' must be escaped as '\\' - // Helpful interactive website for debugging and explaining: https://regex101.com/ - const QRegExp regExpMatchBrackets("^\\s*[({[<].+[)}\\]>]\\s*$"); - _timestampHasBrackets = regExpMatchBrackets.exactMatch(_timestampFormatString); + if (!_useCustomTimestampFormat) { + // The default timestamp format string does not have brackets, no need to check. + // If UiStyle::updateSystemTimestampFormat() has brackets added, change this, too. + _timestampHasBrackets = false; + } else { + // Does the timestamp format contain brackets? For example: + // Classic: "[hh:mm:ss]" + // Modern: " hh:mm:ss" + // + // Match groups of any opening or closing brackets - (), {}, [], <>, (>, {], etc: + // ^\s*[({[<].+[)}\]>]\s*$ + // [...] is a character group containing ... + // ^ matches start of string + // \s* matches any amount of whitespace + // [({[<] matches (, {, [, or < + // .+ matches one or more characters + // [)}\]>] matches ), }, ], or >, escaping the ] + // $ matches end of string + // Alternatively, if opening and closing brackets must be in pairs, use this: + // (^\s*\(.+\)\s*$)|(^\s*\{.+\}\s*$)|(^\s*\[.+\]\s*$)|(^\s*<.+>\s*$) + // Note that '\' must be escaped as '\\' + // Helpful interactive website for debugging and explaining: https://regex101.com/ + const QRegExp regExpMatchBrackets("^\\s*[({[<].+[)}\\]>]\\s*$"); + _timestampHasBrackets = regExpMatchBrackets.exactMatch(_timestampFormatString); + } } diff --git a/src/qtui/chatscene.h b/src/qtui/chatscene.h index e06d22df..4e909517 100644 --- a/src/qtui/chatscene.h +++ b/src/qtui/chatscene.h @@ -185,6 +185,11 @@ private slots: */ void showSenderBracketsChanged(); + /** + * Updates the local setting cache of whether or not to use the custom timestamp format + */ + void useCustomTimestampFormatChanged(); + /** * Updates the local setting cache of the timestamp format string */ @@ -247,6 +252,7 @@ private: bool _showSenderBrackets; /// If true, show brackets around sender names + bool _useCustomTimestampFormat; /// If true, use the custom timestamp format QString _timestampFormatString; /// Format of the timestamp string bool _timestampHasBrackets; /// If true, timestamp format has [brackets] of some sort diff --git a/src/qtui/chatviewsettings.h b/src/qtui/chatviewsettings.h index cb57b68a..b727ab8a 100644 --- a/src/qtui/chatviewsettings.h +++ b/src/qtui/chatviewsettings.h @@ -46,7 +46,20 @@ public: inline void enableWebPreview(bool enabled) { setLocalValue("ShowWebPreview", enabled); } /** - * Gets the format string for chat log timestamps + * Gets if a custom timestamp format is used. + * + * @returns True if custom timestamp format used, otherwise false + */ + inline bool useCustomTimestampFormat() { return localValue("UseCustomTimestampFormat", false).toBool(); } + /** + * Sets whether a custom timestamp format is used. + * + * @param[in] enabled True if custom timestamp format used, otherwise false + */ + inline void setUseCustomTimestampFormat(bool enabled) { setLocalValue("UseCustomTimestampFormat", enabled); } + + /** + * Gets the format string for chat log timestamps. * * @returns String representing timestamp format, e.g. "[hh:mm:ss]" or " hh:mm:ss" */ diff --git a/src/qtui/qtuiapplication.cpp b/src/qtui/qtuiapplication.cpp index 36bb1a45..b77d0af2 100644 --- a/src/qtui/qtuiapplication.cpp +++ b/src/qtui/qtuiapplication.cpp @@ -210,7 +210,7 @@ bool QtUiApplication::migrateSettings() // -------- // Check minor settings version, handling upgrades/downgrades as needed // Current minor version - const uint VERSION_MINOR_CURRENT = 3; + const uint VERSION_MINOR_CURRENT = 4; // Stored minor version uint versionMinor = s.versionMinor(); @@ -270,10 +270,29 @@ bool QtUiApplication::applySettingsMigration(QtUiSettings settings, const uint n // Each missed version will be called in sequence. E.g. to upgrade from '1' to '3', this // function will be called with '2', then '3'. // Use explicit scope via { ... } to avoid cross-initialization + // + // In most cases, the goal is to preserve the older default values for keys that haven't been + // saved. Exceptions will be noted below. + case 4: + { + // New default changes: system locale used to generate a timestamp format string, deciding + // 24-hour or 12-hour timestamp. + + // -------- + // ChatView settings + const QString useCustomTimestampFormatId = "ChatView/__default__/UseCustomTimestampFormat"; + if (!settings.valueExists(useCustomTimestampFormatId)) { + // New default value is false, preserve previous behavior by setting to true + settings.setValue(useCustomTimestampFormatId, true); + } + // -------- + + // Migration complete! + return true; + } case 3: { - // New default changes: per-chat history and line wrapping enabled by default. Preserve - // the older default values for keys that haven't been saved. + // New default changes: per-chat history and line wrapping enabled by default. // -------- // InputWidget settings @@ -297,7 +316,7 @@ bool QtUiApplication::applySettingsMigration(QtUiSettings settings, const uint n case 2: { // New default changes: sender brackets disabled, sender colors and sender CTCP - // colors enabled. Preserve the older default values for keys that haven't been saved. + // colors enabled. // -------- // ChatView settings diff --git a/src/qtui/qtuistyle.cpp b/src/qtui/qtuistyle.cpp index ebf1947a..1e8cc354 100644 --- a/src/qtui/qtuistyle.cpp +++ b/src/qtui/qtuistyle.cpp @@ -27,6 +27,8 @@ QtUiStyle::QtUiStyle(QObject *parent) : UiStyle(parent) { ChatViewSettings s; + s.notify("UseCustomTimestampFormat", this, SLOT(updateUseCustomTimestampFormat())); + updateUseCustomTimestampFormat(); s.notify("TimestampFormat", this, SLOT(updateTimestampFormatString())); updateTimestampFormatString(); s.notify("ShowSenderBrackets", this, SLOT(updateShowSenderBrackets())); @@ -39,6 +41,12 @@ QtUiStyle::QtUiStyle(QObject *parent) : UiStyle(parent) QtUiStyle::~QtUiStyle() {} +void QtUiStyle::updateUseCustomTimestampFormat() +{ + ChatViewSettings s; + setUseCustomTimestampFormat(s.useCustomTimestampFormat()); +} + void QtUiStyle::updateTimestampFormatString() { ChatViewSettings s; diff --git a/src/qtui/qtuistyle.h b/src/qtui/qtuistyle.h index d8247401..2aaceb66 100644 --- a/src/qtui/qtuistyle.h +++ b/src/qtui/qtuistyle.h @@ -50,7 +50,16 @@ public slots: void generateSettingsQss() const; private slots: + /** + * Updates knowledge of whether or not to use the custom timestamp format + */ + void updateUseCustomTimestampFormat(); + + /** + * Updates knowledge of the current timestamp format + */ void updateTimestampFormatString(); + /** * Updates knowledge of whether or not to show sender brackets */ diff --git a/src/qtui/settingspages/chatviewsettingspage.ui b/src/qtui/settingspages/chatviewsettingspage.ui index b68c8726..80c6cff3 100644 --- a/src/qtui/settingspages/chatviewsettingspage.ui +++ b/src/qtui/settingspages/chatviewsettingspage.ui @@ -17,14 +17,26 @@ - + + + Use a custom format for the timestamp + - Timestamp format: + Custom timestamp format: + + + false + + + UseCustomTimestampFormat + + false + <html><head/><body><p>Usage examples:</p> <table cellpadding="2"> @@ -220,6 +232,7 @@ + customTimestampFormat timestampFormat showSenderBrackets customChatViewFont @@ -246,5 +259,21 @@ + + customTimestampFormat + toggled(bool) + timestampFormat + setEnabled(bool) + + + 116 + 22 + + + 301 + 23 + + + diff --git a/src/uisupport/uistyle.cpp b/src/uisupport/uistyle.cpp index f55b1831..fd2bee52 100644 --- a/src/uisupport/uistyle.cpp +++ b/src/uisupport/uistyle.cpp @@ -29,8 +29,10 @@ #include "util.h" QHash UiStyle::_formatCodes; -QString UiStyle::_timestampFormatString; /// Timestamp format -bool UiStyle::_showSenderBrackets; /// If true, show brackets around sender names +bool UiStyle::_useCustomTimestampFormat; /// If true, use the custom timestamp format +QString UiStyle::_timestampFormatString; /// Timestamp format +QString UiStyle::_systemTimestampFormatString; /// Cached copy of system locale timestamp format +bool UiStyle::_showSenderBrackets; /// If true, show brackets around sender names UiStyle::UiStyle(QObject *parent) : QObject(parent), @@ -70,6 +72,7 @@ UiStyle::UiStyle(QObject *parent) // Initialize fallback defaults // NOTE: If you change this, update qtui/chatviewsettings.h, too. More explanations available // in there. + setUseCustomTimestampFormat(false); setTimestampFormatString(" hh:mm:ss"); enableSenderBrackets(true); @@ -168,7 +171,52 @@ QString UiStyle::loadStyleSheet(const QString &styleSheet, bool shouldExist) return ss; } + +void UiStyle::updateSystemTimestampFormat() +{ + // Does the system locale use AM/PM designators? For example: + // AM/PM: h:mm AP + // AM/PM: hh:mm a + // 24-hour: h:mm + // 24-hour: hh:mm ADD things + // For timestamp format, see https://doc.qt.io/qt-5/qdatetime.html#toString + // This won't update if the system locale is changed while Quassel is running. If need be, + // Quassel could hook into notifications of changing system locale to update this. + // + // Match any AP or A designation if on a word boundary, including underscores. + // .*(\b|_)(A|AP)(\b|_).* + // .* Match any number of characters + // \b Match a word boundary, i.e. "AAA.BBB", "." is matched + // _ Match the literal character '_' (not considered a word boundary) + // (X|Y) Match either X or Y, exactly + // + // Note that '\' must be escaped as '\\' + // QRegExp does not support (?> ...), so it's replaced with standard matching, (...) + // Helpful interactive website for debugging and explaining: https://regex101.com/ + const QRegExp regExpMatchAMPM(".*(\\b|_)(A|AP)(\\b|_).*", Qt::CaseInsensitive); + + if (regExpMatchAMPM.exactMatch(QLocale::system().timeFormat(QLocale::ShortFormat))) { + // AM/PM style used + _systemTimestampFormatString = " h:mm:ss ap"; + } else { + // 24-hour style used + _systemTimestampFormatString = " hh:mm:ss"; + } + // Include a space to give the timestamp a small bit of padding between the border of the chat + // buffer window and the numbers. Helps with readability. + // If you change this to include brackets, e.g. "[hh:mm:ss]", also update + // ChatScene::updateTimestampHasBrackets() to true or false as needed! +} + + // FIXME The following should trigger a reload/refresh of the chat view. +void UiStyle::setUseCustomTimestampFormat(bool enabled) +{ + if (_useCustomTimestampFormat != enabled) { + _useCustomTimestampFormat = enabled; + } +} + void UiStyle::setTimestampFormatString(const QString &format) { if (_timestampFormatString != format) { @@ -663,6 +711,26 @@ QString UiStyle::mircToInternal(const QString &mirc_) } +QString UiStyle::systemTimestampFormatString() +{ + if (_systemTimestampFormatString.isEmpty()) { + // Calculate and cache the system timestamp format string + updateSystemTimestampFormat(); + } + return _systemTimestampFormatString; +} + + +QString UiStyle::timestampFormatString() +{ + if (useCustomTimestampFormat()) { + return _timestampFormatString; + } else { + return systemTimestampFormatString(); + } +} + + /***********************************************************************************/ UiStyle::StyledMessage::StyledMessage(const Message &msg) : Message(msg) diff --git a/src/uisupport/uistyle.h b/src/uisupport/uistyle.h index b179147b..0b4589a1 100644 --- a/src/uisupport/uistyle.h +++ b/src/uisupport/uistyle.h @@ -178,7 +178,33 @@ public: static FormatType formatType(Message::Type msgType); static StyledString styleString(const QString &string, quint32 baseFormat = Base); static QString mircToInternal(const QString &); - static inline QString timestampFormatString() { return _timestampFormatString; } + + /** + * Gets if a custom timestamp format is used. + * + * @return True if custom timestamp format used, otherwise false + */ + static inline bool useCustomTimestampFormat() { return _useCustomTimestampFormat; } + + /** + * Gets the format string for chat log timestamps according to the system locale. + * + * This will return " hh:mm:ss" for system locales with 24-hour time or " h:mm:ss AP" for + * systems with 12-hour time. + * + * @return String representing timestamp format according to system locale, e.g. " hh:mm:ss" + */ + static QString systemTimestampFormatString(); + + /** + * Gets the format string for chat log timestamps, either system locale or custom. + * + * Depending on useCustomTimestampFormat(), this will return either the system locale based + * time format, or the custom user-specified string. + * + * @return String representing timestamp format, e.g. "[hh:mm:ss]" or " hh:mm:ss" + */ + static QString timestampFormatString(); QTextCharFormat format(quint32 formatType, quint32 messageLabel) const; QFontMetricsF *fontMetrics(quint32 formatType, quint32 messageLabel) const; @@ -209,7 +235,31 @@ protected: static FormatType formatType(const QString &code); static QString formatCode(FormatType); + + /** + * Cache the system locale timestamp format string + * + * Based on whether or not AM/PM designators are used in the QLocale::system().timeFormat(), + * this extends the system locale timestamp format string to include seconds. + * + * @see UiStyle::systemTimestampFormatString() + */ + static void updateSystemTimestampFormat(); + + /** + * Updates the local setting cache of whether or not to use the custom timestamp format + * + * @param[in] enabled If true, custom timestamp format used, otherwise false + */ + static void setUseCustomTimestampFormat(bool enabled); + + /** + * Updates the local setting cache of the timestamp format string + * + * @param[in] format Timestamp format string + */ static void setTimestampFormatString(const QString &format); + /** * Updates the local setting cache of whether or not to show sender brackets * @@ -231,8 +281,10 @@ private: mutable QHash _metricsCache; QHash _listItemFormats; static QHash _formatCodes; - static QString _timestampFormatString; - static bool _showSenderBrackets; /// If true, show brackets around sender names + static bool _useCustomTimestampFormat; /// If true, use the custom timestamp format + static QString _systemTimestampFormatString; /// Cached copy of system locale timestamp format + static QString _timestampFormatString; /// Timestamp format string + static bool _showSenderBrackets; /// If true, show brackets around sender names QIcon _channelJoinedIcon; QIcon _channelPartedIcon; -- 2.20.1