Skip to content

Commit 55460ff

Browse files
committed
Implement persistent custom Date Format using Q_ENUM and QVariant
1 parent 33a4e39 commit 55460ff

9 files changed

Lines changed: 223 additions & 18 deletions

File tree

src/library/basetracktablemodel.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,13 @@ void BaseTrackTableModel::setApplyPlayedTrackColor(bool apply) {
115115
s_bApplyPlayedTrackColor = apply;
116116
}
117117

118+
const QString BaseTrackTableModel::kDateFormatDefault = QString();
119+
QString BaseTrackTableModel::s_dateFormat = BaseTrackTableModel::kDateFormatDefault;
120+
121+
void BaseTrackTableModel::setDateFormat(const QString& format) {
122+
s_dateFormat = format;
123+
}
124+
118125
BaseTrackTableModel::BaseTrackTableModel(
119126
QObject* parent,
120127
TrackCollectionManager* pTrackCollectionManager,
@@ -689,10 +696,10 @@ QVariant BaseTrackTableModel::roleValue(
689696
if (field == ColumnCache::COLUMN_PLAYLISTTRACKSTABLE_DATETIMEADDED) {
690697
// Timestamp column in history feature:
691698
// Use localized date/time format without text: "5/20/98 03:40 AM"
692-
return mixxx::displayLocalDateTime(dt);
699+
return mixxx::displayLocalDateTime(dt, s_dateFormat);
693700
}
694701
// For Date Added, use just the date: "5/20/98"
695-
return dt.date();
702+
return mixxx::formatDate(dt.date(), s_dateFormat);
696703
}
697704
case ColumnCache::COLUMN_LIBRARYTABLE_LAST_PLAYED_AT: {
698705
QDateTime lastPlayedAt;
@@ -718,7 +725,7 @@ QVariant BaseTrackTableModel::roleValue(
718725
if (role == Qt::ToolTipRole || role == kDataExportRole) {
719726
return dt;
720727
}
721-
return dt.date();
728+
return mixxx::formatDate(dt.date(), s_dateFormat);
722729
}
723730
case ColumnCache::COLUMN_LIBRARYTABLE_BPM: {
724731
mixxx::Bpm bpm;

src/library/basetracktablemodel.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "library/trackmodel.h"
1010
#include "track/track_decl.h"
1111
#include "util/color/colorpalette.h"
12+
#include "util/datetime.h"
1213

1314
class TrackCollectionManager;
1415

@@ -132,6 +133,18 @@ class BaseTrackTableModel : public QAbstractTableModel, public TrackModel {
132133
static constexpr bool kApplyPlayedTrackColorDefault = true;
133134
static void setApplyPlayedTrackColor(bool apply);
134135

136+
enum class DateFormat {
137+
Native = 0, // System Default
138+
ISO8601 = 1, // yyyy-MM-dd
139+
RegionalShort = 2, // d/M/yy
140+
RegionalLong = 3, // dd.MM.yyyy
141+
Custom = 4,
142+
};
143+
Q_ENUM(DateFormat)
144+
145+
static const QString kDateFormatDefault;
146+
static void setDateFormat(const QString& format);
147+
135148
protected:
136149
// Build a map from the column names to their indices
137150
// used by fieldIndex().
@@ -307,4 +320,5 @@ class BaseTrackTableModel : public QAbstractTableModel, public TrackModel {
307320
static std::optional<ColorPalette> s_keyColorPalette;
308321

309322
static bool s_bApplyPlayedTrackColor;
323+
static QString s_dateFormat;
310324
};

src/library/library_prefs.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,8 @@ const ConfigKey mixxx::library::prefs::kTagFetcherApplyCoverConfigKey =
114114
ConfigKey{
115115
mixxx::library::prefs::kConfigGroup,
116116
QStringLiteral("TagFetcherApplyCover")};
117+
118+
const ConfigKey mixxx::library::prefs::kDateFormatConfigKey =
119+
ConfigKey{
120+
mixxx::library::prefs::kConfigGroup,
121+
QStringLiteral("DateFormat")};

src/library/library_prefs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ extern const ConfigKey kTagFetcherApplyTagsConfigKey;
5858

5959
extern const ConfigKey kTagFetcherApplyCoverConfigKey;
6060

61+
extern const ConfigKey kDateFormatConfigKey;
62+
6163
} // namespace prefs
6264

6365
} // namespace library

src/preferences/dialog/dlgpreflibrary.cpp

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,12 @@ void DlgPrefLibrary::slotHide() {
205205
QMessageBox msgBox;
206206
msgBox.setIcon(QMessageBox::Warning);
207207
msgBox.setWindowTitle(tr("Music Directory Added"));
208-
msgBox.setText(tr("You added one or more music directories. The tracks in "
209-
"these directories won't be available until you rescan "
210-
"your library. Would you like to rescan now?"));
208+
msgBox.setText(tr(
209+
"You added one or more music directories. The tracks in "
210+
"these directories won't be available until you rescan "
211+
"your library. Would you like to rescan now?"));
211212
QPushButton* scanButton = msgBox.addButton(
212-
tr("Scan"), QMessageBox::AcceptRole);
213+
tr("Scan"), QMessageBox::AcceptRole);
213214
msgBox.addButton(QMessageBox::Cancel);
214215
msgBox.setDefaultButton(scanButton);
215216
msgBox.exec();
@@ -777,3 +778,66 @@ void DlgPrefLibrary::setSeratoMetadataEnabled(bool shouldSyncTrackMetadata) {
777778
checkBox_serato_metadata_export->setChecked(false);
778779
}
779780
}
781+
782+
void DlgPrefLibrary::slotDateFormatIndexChanged(int index) {
783+
auto type = comboBox_dateFormat->itemData(index)
784+
.value<BaseTrackTableModel::DateFormat>();
785+
786+
if (type == BaseTrackTableModel::DateFormat::Custom) {
787+
// Enable editing for Custom
788+
if (!comboBox_dateFormat->isEditable()) {
789+
comboBox_dateFormat->setEditable(true);
790+
comboBox_dateFormat->setEditText(m_lastCustomDateFormat);
791+
}
792+
} else {
793+
// Disable editing for Presets
794+
comboBox_dateFormat->setEditable(false);
795+
796+
QString format;
797+
switch (type) {
798+
case BaseTrackTableModel::DateFormat::Native:
799+
format = QString();
800+
break;
801+
case BaseTrackTableModel::DateFormat::ISO8601:
802+
format = QStringLiteral("yyyy-MM-dd");
803+
break;
804+
case BaseTrackTableModel::DateFormat::RegionalShort:
805+
format = QStringLiteral("d/M/yy");
806+
break;
807+
case BaseTrackTableModel::DateFormat::RegionalLong:
808+
format = QStringLiteral("dd.MM.yyyy");
809+
break;
810+
case BaseTrackTableModel::DateFormat::Custom:
811+
// Should not happen here given the if/else above
812+
break;
813+
}
814+
slotDateFormatChanged(format);
815+
}
816+
}
817+
818+
void DlgPrefLibrary::slotDateFormatChanged(const QString& text) {
819+
QString format = text;
820+
// If not editable, we are in a Preset mode, but 'text' might be the Item
821+
// Label (e.g. "Native ...") depending on how this was called. However, our
822+
// slotDateFormatIndexChanged calls this explicitly with the correct format
823+
// string. The editTextChanged signal only fires when editable. So 'text'
824+
// should be the correct format string in all valid cases.
825+
826+
if (comboBox_dateFormat->isEditable()) {
827+
int index = comboBox_dateFormat->currentIndex();
828+
if (index >= 0) {
829+
auto type = comboBox_dateFormat->itemData(index)
830+
.value<BaseTrackTableModel::DateFormat>();
831+
if (type == BaseTrackTableModel::DateFormat::Custom) {
832+
m_lastCustomDateFormat = format;
833+
}
834+
}
835+
}
836+
837+
// Update Preview
838+
const QString previewStr = mixxx::formatDate(QDate::currentDate(), format);
839+
label_dateFormatPreview->setText(previewStr);
840+
841+
m_pConfig->setValue(kDateFormatConfigKey, format);
842+
BaseTrackTableModel::setDateFormat(format);
843+
}

src/preferences/dialog/dlgpreflibrary.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
class QWidget;
1414
class ControlProxy;
1515

16-
class DlgPrefLibrary : public DlgPreferencePage, public Ui::DlgPrefLibraryDlg {
16+
class DlgPrefLibrary : public DlgPreferencePage, public Ui::DlgPrefLibraryDlg {
1717
Q_OBJECT
1818
public:
1919
enum class TrackDoubleClickAction : int {
@@ -67,6 +67,8 @@ class DlgPrefLibrary : public DlgPreferencePage, public Ui::DlgPrefLibraryDlg {
6767
void slotBpmRangeSelected(int index);
6868
void slotBpmColumnPrecisionChanged(int bpmPrecision);
6969
void slotSeratoMetadataExportClicked(bool);
70+
void slotDateFormatIndexChanged(int index);
71+
void slotDateFormatChanged(const QString& text);
7072

7173
private:
7274
void populateDirList();
@@ -83,4 +85,6 @@ class DlgPrefLibrary : public DlgPreferencePage, public Ui::DlgPrefLibraryDlg {
8385
int m_iOriginalTrackTableRowHeight;
8486
// Listen to rate range changes in order to update the fuzzy BPM range
8587
parented_ptr<ControlProxy> m_pRateRangeDeck1;
88+
89+
QString m_lastCustomDateFormat;
8690
};

src/preferences/dialog/dlgpreflibrarydlg.ui

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,15 +173,15 @@
173173
</property>
174174
<layout class="QGridLayout" name="gridLayout_track_table_view">
175175

176-
<item row="1" column="0" colspan="3">
176+
<item row="1" column="0" colspan="4">
177177
<widget class="QCheckBox" name="checkBox_edit_metadata_selected_clicked">
178178
<property name="text">
179179
<string>Edit metadata after clicking selected track</string>
180180
</property>
181181
</widget>
182182
</item>
183183

184-
<item row="2" column="0" colspan="3">
184+
<item row="2" column="0" colspan="4">
185185
<widget class="QLabel" name="label_doubeClickAction">
186186
<property name="text">
187187
<string>Track Double-Click Action:</string>
@@ -192,7 +192,7 @@
192192
</widget>
193193
</item>
194194

195-
<item row="3" column="0" colspan="3">
195+
<item row="3" column="0" colspan="4">
196196
<widget class="QRadioButton" name="radioButton_dbclick_deck">
197197
<property name="text">
198198
<string>Load track to next available deck</string>
@@ -202,21 +202,21 @@
202202
</property>
203203
</widget>
204204
</item>
205-
<item row="4" column="0" colspan="3">
205+
<item row="4" column="0" colspan="4">
206206
<widget class="QRadioButton" name="radioButton_dbclick_bottom">
207207
<property name="text">
208208
<string>Add track to Auto DJ queue (bottom)</string>
209209
</property>
210210
</widget>
211211
</item>
212-
<item row="5" column="0" colspan="3">
212+
<item row="5" column="0" colspan="4">
213213
<widget class="QRadioButton" name="radioButton_dbclick_top">
214214
<property name="text">
215215
<string>Add track to Auto DJ queue (top)</string>
216216
</property>
217217
</widget>
218218
</item>
219-
<item row="6" column="0" colspan="3">
219+
<item row="6" column="0" colspan="4">
220220
<widget class="QRadioButton" name="radioButton_dbclick_ignore">
221221
<property name="text">
222222
<string>Ignore</string>
@@ -290,14 +290,58 @@
290290
<widget class="QSpinBox" name="spinbox_bpm_precision"/>
291291
</item>
292292

293-
<item row="10" column="0" colspan="3">
293+
<item row="10" column="0" colspan="4">
294294
<widget class="QCheckBox" name="checkbox_played_track_color">
295295
<property name="text">
296296
<string>Grey out played tracks</string>
297297
</property>
298298
</widget>
299299
</item>
300300

301+
<item row="11" column="0">
302+
<widget class="QLabel" name="label_dateFormat">
303+
<property name="text">
304+
<string>Date Format:</string>
305+
</property>
306+
<property name="alignment">
307+
<set>Qt::AlignLeft|Qt::AlignVCenter</set>
308+
</property>
309+
</widget>
310+
</item>
311+
<item row="11" column="1" colspan="3">
312+
<layout class="QHBoxLayout" name="horizontalLayout_dateFormat">
313+
<property name="leftMargin">
314+
<number>0</number>
315+
</property>
316+
<property name="topMargin">
317+
<number>0</number>
318+
</property>
319+
<property name="rightMargin">
320+
<number>0</number>
321+
</property>
322+
<property name="bottomMargin">
323+
<number>0</number>
324+
</property>
325+
<item>
326+
<widget class="QComboBox" name="comboBox_dateFormat">
327+
<property name="sizePolicy">
328+
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
329+
<horstretch>0</horstretch>
330+
<verstretch>0</verstretch>
331+
</sizepolicy>
332+
</property>
333+
</widget>
334+
</item>
335+
<item>
336+
<widget class="QLabel" name="label_dateFormatPreview">
337+
<property name="text">
338+
<string>Preview</string>
339+
</property>
340+
</widget>
341+
</item>
342+
</layout>
343+
</item>
344+
301345
</layout>
302346
</widget>
303347
</item><!-- Track Table View -->
@@ -695,6 +739,7 @@
695739
<tabstop>btn_library_font</tabstop>
696740
<tabstop>spinbox_bpm_precision</tabstop>
697741
<tabstop>checkbox_played_track_color</tabstop>
742+
<tabstop>comboBox_dateFormat</tabstop>
698743
<tabstop>spinBox_search_debouncing_timeout</tabstop>
699744
<tabstop>checkBox_enable_search_completions</tabstop>
700745
<tabstop>checkBox_enable_search_history_shortcuts</tabstop>

src/test/datetime_test.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#include "util/datetime.h"
2+
3+
#include <gtest/gtest.h>
4+
5+
#include <QDate>
6+
#include <QLocale>
7+
8+
namespace mixxx {
9+
10+
TEST(DateTimeTest, FormatDate) {
11+
QDate date(2023, 10, 25);
12+
13+
// Test ISO
14+
EXPECT_EQ(formatDate(date, "yyyy-MM-dd"), "2023-10-25");
15+
16+
// Test Custom
17+
EXPECT_EQ(formatDate(date, "dd.MM.yy"), "25.10.23");
18+
19+
// Test Native (empty string)
20+
// Should fallback to QLocale::ShortFormat
21+
QString native = QLocale().toString(date, QLocale::ShortFormat);
22+
EXPECT_EQ(formatDate(date, ""), native);
23+
}
24+
25+
TEST(DateTimeTest, FormatDateTime) {
26+
QDateTime dt(QDate(2023, 10, 25), QTime(14, 30, 0));
27+
28+
// Test Custom
29+
EXPECT_EQ(formatDateTime(dt, "yyyy-MM-dd HH:mm"), "2023-10-25 14:30");
30+
31+
// Test Native (empty string)
32+
QString native = QLocale().toString(dt, QLocale::ShortFormat);
33+
EXPECT_EQ(formatDateTime(dt, ""), native);
34+
}
35+
36+
} // namespace mixxx

src/util/datetime.h

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,39 @@ inline QDateTime convertVariantToDateTime(
2828
return data.toDateTime();
2929
}
3030

31+
/// Helper to format a QDate according to a format string.
32+
/// If format is empty, uses the system's default locale short format.
33+
inline QString formatDate(
34+
const QDate& date,
35+
const QString& format = QString()) {
36+
if (!date.isValid()) {
37+
return QString();
38+
}
39+
if (format.isEmpty()) {
40+
return QLocale().toString(date, QLocale::ShortFormat);
41+
}
42+
return date.toString(format);
43+
}
44+
45+
/// Helper to format a QDateTime according to a format string.
46+
/// If format is empty, uses the system's default locale short format.
47+
inline QString formatDateTime(
48+
const QDateTime& dt,
49+
const QString& format = QString()) {
50+
if (!dt.isValid()) {
51+
return QString();
52+
}
53+
if (format.isEmpty()) {
54+
return QLocale().toString(dt, QLocale::ShortFormat);
55+
}
56+
return dt.toString(format);
57+
}
58+
3159
/// Format a QDateTime for display to the user using the
32-
/// application's locale settings.
60+
/// application's locale settings or specified preference.
3361
inline QString displayLocalDateTime(
34-
const QDateTime& dt) {
35-
return QLocale().toString(dt, QLocale::ShortFormat);
62+
const QDateTime& dt, const QString& format = QString()) {
63+
return formatDateTime(dt, format);
3664
}
3765

3866
} // namespace mixxx

0 commit comments

Comments
 (0)