Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Hotcue Color Replace Dialog #2547

Merged
merged 66 commits into from
Apr 21, 2020
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
9c719ec
widget/wmainmenubar: Add menu entry for color replacer
Holzhaus Mar 10, 2020
febe1ae
dialog: Add (non-working) replace color dialog
Holzhaus Mar 12, 2020
14c507e
dialog/dlgreplacecolor: Add initial support for replacing hotcue colors
Holzhaus Mar 13, 2020
a05150e
dialog/dlgreplacetrackcolor: Improve user feedback after DB update
Holzhaus Mar 15, 2020
d9872fe
dialog/dlgreplacecuecolor: Use better default colors
Holzhaus Mar 16, 2020
1fece29
dialog/dlgreplacecuecolor: Use color picker for color selection
Holzhaus Mar 16, 2020
f3c66ac
dialog/dlgreplacecuecolor: Add support for hotcue indices
Holzhaus Mar 17, 2020
4c74f27
dialog/dlgreplacecuecolordlg: Check current color checkbox by default
Holzhaus Mar 17, 2020
fd40737
dialog/dlgreplacecuecolor: Use enum class instead of plain enum
Holzhaus Mar 17, 2020
882a2b3
dialog/dlgreplacecuecolor: Make updateCueColors() a private class member
Holzhaus Mar 17, 2020
40cc703
dialog/dlgreplacecuecolor: Select IDs before doing actual update
Holzhaus Mar 17, 2020
b466ce9
dialog/dlgreplacecuecolor: Notify TrackDAO after DB change
Holzhaus Mar 17, 2020
0c5fcf2
dialog/dlgreplacecuecolor: Hide button menus after picking a color
Holzhaus Mar 17, 2020
c2d85eb
dialog/dlgreplacecuecolor: Add space before comment string
Holzhaus Mar 17, 2020
355c2c3
dialog/dlgreplacecuecolor: Add class comment
Holzhaus Mar 17, 2020
8802755
dialog/dlgreplacecuecolor: Don't forward create QVariantLists for query
Holzhaus Mar 17, 2020
badf2e7
dialog/dlgreplacecuecolor: Reword DB pooler comment
Holzhaus Mar 17, 2020
0163061
dialog/dlgreplacecuecolor: Move TrackDAO connection out of dialog class
Holzhaus Mar 17, 2020
abff8d6
dialog/dlgreplacecuecolor: Check that color didn't change between que…
Holzhaus Mar 17, 2020
9b9b14e
dialog/dlgreplacecuecolor: Move all DB operations into the main thread
Holzhaus Mar 18, 2020
9b6f6b4
dialog/dlgreplacecuecolor: Move private type out of header file
Holzhaus Mar 18, 2020
15fe173
dialog/dlgreplacecuecolor: Use ColorPicker for custom colors
Holzhaus Mar 21, 2020
4947400
dialog/dlgreplacecuecolor: Set dialog result depending on DB change
Holzhaus Mar 26, 2020
991668d
dialog/dlgreplacecuecolor: Switch to using PredefinedColorPalettes
Holzhaus Mar 29, 2020
2942034
dialog/dlgreplacecuecolor: Disable apply button if DB change is a no-op
Holzhaus Mar 27, 2020
dd53429
dialog/dlgreplacecuecolor: Don't invoke DBConnectionPooler again
Holzhaus Mar 29, 2020
a33ff49
dialog/dlgreplacecuecolor: Flush track changes to the database
Holzhaus Mar 29, 2020
1304b39
dialog/dlgreplacecuecolor: Update in-memory cue points after DB change
Holzhaus Mar 29, 2020
ed349bf
dialog/dlgreplacecuecolor: Use Color::isDimmColor for buttons
Holzhaus Mar 29, 2020
502dc8a
dialog/dlgreplacecuecolor: Check palette size
Holzhaus Mar 29, 2020
86f60a5
dialog/dlgreplacecuecolor: Acquire track pointer directly after DB ch…
Holzhaus Mar 29, 2020
e9656a6
dialog/dlgreplacecuecolor: Fix dimm color detection
Holzhaus Apr 2, 2020
3aed78c
dialog/dlgreplacecuecolor: Force "fusion" style for colored buttons
Holzhaus Apr 2, 2020
04225aa
dialog/dlgreplacecuecolordlg: Improve labels
Holzhaus Apr 2, 2020
8eef253
dialog/dlgreplacecuecolor: Remove unused lightness threshold constant
Holzhaus Apr 2, 2020
102290d
dialog/dlgreplacecuecolor: Use black text for light color buttons
Holzhaus Apr 2, 2020
7a6b366
dialog/dlgreplacecuecolor: Improve confirmation text
Holzhaus Apr 2, 2020
66298d3
dialog/dlgreplacecuecolor: Check if cue doesn't already new dest color
Holzhaus Apr 3, 2020
47618c7
dialog/dlgreplacecuecolor: Wrap long lines manually
Holzhaus Apr 3, 2020
a020105
dialog/dlgreplacecuecolor: Use parented_ptr for WColorPickerAction
Holzhaus Apr 3, 2020
4e57904
dialog/dlgreplacecuecolor: Close dialog after database update
Holzhaus Apr 3, 2020
62bc3db
Merge branch 'master' of github.com:mixxxdj/mixxx into mass-color-rep…
Holzhaus Apr 5, 2020
3f20479
Move replace cue color dialog from main menu bar to preferences
Holzhaus Apr 5, 2020
6dea4c0
widget/wmainmenubar: Remove "Replace Cue Colors' action from menu
Holzhaus Apr 6, 2020
a76d0ed
dialog/dlgreplacecuecolor: Fix translation issue for progress dialog
Holzhaus Apr 8, 2020
6a92df0
dialog/dlgreplacecuecolordlg: Don't capitalize ALL
Holzhaus Apr 9, 2020
49a8dcf
Merge branch 'master' of github.com:mixxxdj/mixxx into mass-color-rep…
Holzhaus Apr 12, 2020
8959be1
dialog/dlgreplacecuecolor: Use new palette after palette change
Holzhaus Apr 12, 2020
cfbd266
Merge branch 'master' of github.com:mixxxdj/mixxx into mass-color-rep…
Holzhaus Apr 13, 2020
d924fe6
dialog/dlgreplacecuecolordlg: Reorder widgets in Replace dialog
Holzhaus Apr 13, 2020
294f8b5
dialog/dlgreplacecuecolor: Add warning if no condition was selected
Holzhaus Apr 13, 2020
18336aa
dialog/dlgreplacecuecolor: Fix "slotApplyButton" misnomer
Holzhaus Apr 13, 2020
a01675b
dialog/dlgreplacecuecolordlg: Set minimumSize for "replace all" warning
Holzhaus Apr 13, 2020
d256c6b
dialog/dlgreplacecuecolor: Use a separate label for "replace all" icon
Holzhaus Apr 13, 2020
1048c8c
dialog/dlgreplacecuecolordlg: Fix "replace all" text and size policy
Holzhaus Apr 13, 2020
0080967
dialog/dlgreplacecuecolordlg: Set dialog vertical size policy to minimum
Holzhaus Apr 13, 2020
9ea0f26
dialog/dlgreplacecuecolor: Wrap long source lines
Holzhaus Apr 14, 2020
900fb36
preferences/dialog/dlgprefcolorsdlg: Replace "..." with unicode elipsis
Holzhaus Apr 14, 2020
3b1f02f
dialog/dlgreplacecuecolor: Pre-fill some colors
Holzhaus Apr 14, 2020
89c58c0
dialog/dlgreplacecuecolor: Do not overwrite user-selected colors
Holzhaus Apr 14, 2020
415b432
Merge branch 'master' of github.com:mixxxdj/mixxx into mass-color-rep…
Holzhaus Apr 15, 2020
b84825e
dialog/dlgreplacecuecolor: Resize color menus after changing palette
Holzhaus Apr 15, 2020
e8a363e
dialog/dlgreplacecuecolor: Fix show/hide of "replace all" warning label
Holzhaus Apr 16, 2020
c92a823
dialog/dlgreplacecuecolor: Add documentation for slotUpdateWidgets()
Holzhaus Apr 16, 2020
37065cc
dialog/dlgreplacecuecolor: Remove orphaned private method declaration
Holzhaus Apr 16, 2020
b6cfccc
Merge branch 'master' of github.com:mixxxdj/mixxx into mass-color-rep…
Holzhaus Apr 17, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL
src/dialog/dlgaboutdlg.ui
src/dialog/dlgdevelopertools.cpp
src/dialog/dlgdevelopertoolsdlg.ui
src/dialog/dlgreplacecuecolor.cpp
src/dialog/dlgreplacecuecolordlg.ui
src/effects/builtin/autopaneffect.cpp
src/effects/builtin/balanceeffect.cpp
src/effects/builtin/bessel4lvmixeqeffect.cpp
Expand Down
2 changes: 2 additions & 0 deletions build/depends.py
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,7 @@ def sources(self, build):
"src/controllers/dlgprefcontrollers.cpp",
"src/dialog/dlgabout.cpp",
"src/dialog/dlgdevelopertools.cpp",
"src/dialog/dlgreplacecuecolor.cpp",

"src/preferences/configobject.cpp",
"src/preferences/dialog/dlgprefautodj.cpp",
Expand Down Expand Up @@ -1340,6 +1341,7 @@ def sources(self, build):
'src/controllers/dlgprefcontrollersdlg.ui',
'src/dialog/dlgaboutdlg.ui',
'src/dialog/dlgdevelopertoolsdlg.ui',
'src/dialog/dlgreplacecuecolordlg.ui',
'src/library/autodj/dlgautodj.ui',
'src/library/dlganalysis.ui',
'src/library/dlgcoverartfullsize.ui',
Expand Down
367 changes: 367 additions & 0 deletions src/dialog/dlgreplacecuecolor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,367 @@
#include "dialog/dlgreplacecuecolor.h"

#include <QAbstractButton>
#include <QColor>
#include <QDialogButtonBox>
#include <QMessageBox>
#include <QtConcurrent>

#include "engine/controls/cuecontrol.h"
#include "library/dao/cuedao.h"
#include "library/queryutil.h"
#include "preferences/colorpalettesettings.h"
#include "util/color/predefinedcolorpalettes.h"

namespace {

constexpr int kColorButtonLightnessThreshold = 0x80;
Holzhaus marked this conversation as resolved.
Show resolved Hide resolved
const QString kColorButtonStyleSheetLight = QStringLiteral(
Holzhaus marked this conversation as resolved.
Show resolved Hide resolved
"QPushButton { background-color: %1; }");
const QString kColorButtonStyleSheetDark = QStringLiteral(
"QPushButton { background-color: %1; color: white; }");

void setButtonColor(QPushButton* button, const QColor& color) {
button->setText(color.name());
button->setStyleSheet(
(Color::isDimmColor(color)
? kColorButtonStyleSheetLight
: kColorButtonStyleSheetDark)
.arg(color.name()));
}

typedef struct {
int id;
TrackId trackId;
mixxx::RgbColor color;
} CueDatabaseRow;

} // namespace

DlgReplaceCueColor::DlgReplaceCueColor(
UserSettingsPointer pConfig,
mixxx::DbConnectionPoolPtr dbConnectionPool,
TrackCollectionManager* pTrackCollectionManager,
QWidget* pParent)
: QDialog(pParent),
m_pConfig(pConfig),
m_pDbConnectionPool(dbConnectionPool),
m_pTrackCollectionManager(pTrackCollectionManager),
m_bDatabaseChangeInProgress(false),
m_bDatabaseChanged(false),
m_pNewColorMenu(new QMenu(this)),
m_pCurrentColorMenu(new QMenu(this)) {
setupUi(this);
setWindowModality(Qt::ApplicationModal);

spinBoxHotcueIndex->setMaximum(NUM_HOT_CUES);

// Set up new color button
ColorPaletteSettings colorPaletteSettings(pConfig);
ColorPalette hotcuePalette = colorPaletteSettings.getHotcueColorPalette();
mixxx::RgbColor firstColor = mixxx::PredefinedColorPalettes::kDefaultCueColor;
DEBUG_ASSERT(hotcuePalette.size() > 0);
daschuer marked this conversation as resolved.
Show resolved Hide resolved
if (hotcuePalette.size() > 0) { // Should always be true
firstColor = hotcuePalette.at(0);
}
setButtonColor(pushButtonNewColor, mixxx::RgbColor::toQColor(firstColor));

// Add menu for new color button
m_pNewColorPickerAction = new WColorPickerAction(WColorPicker::Option::AllowCustomColor, colorPaletteSettings.getHotcueColorPalette(), this);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line length exceeds GitHub diff viewer. Please check the whole file.
Or wait until #2608 is merged :-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make_parented can be used here, to document the ownership. The same below.

Copy link
Member Author

@Holzhaus Holzhaus Mar 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make_parented can be used here, to document the ownership. The same below.

This results in:

DEBUG ASSERT: "!m_ptr || m_ptr->parent()" in function parented_ptr<T>::~parented_ptr() [with T = WColorPickerAction] at /home/jan/Projects/mixxx/src/util/parented_ptr.h:31

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot confirm that.
m_pNewColorPickerAction = make_parented(WColorPicker::Option::AllowCustomColor, colorPaletteSettings.getHotcueColorPalette(), this);

m_pNewColorPickerAction->setObjectName("HotcueColorPickerAction");
m_pNewColorPickerAction->setSelectedColor(firstColor);
connect(m_pNewColorPickerAction,
&WColorPickerAction::colorPicked,
[this](mixxx::RgbColor::optional_t color) {
if (color) {
setButtonColor(pushButtonNewColor, mixxx::RgbColor::toQColor(*color));
slotUpdateApplyButton();
}
m_pNewColorMenu->hide();
});
m_pNewColorMenu->addAction(m_pNewColorPickerAction);
pushButtonNewColor->setMenu(m_pNewColorMenu);

// Set up current color button
setButtonColor(pushButtonCurrentColor, mixxx::RgbColor::toQColor(mixxx::PredefinedColorPalettes::kDefaultCueColor));
Holzhaus marked this conversation as resolved.
Show resolved Hide resolved
connect(checkBoxCurrentColorCondition,
&QCheckBox::stateChanged,
this,
&DlgReplaceCueColor::slotUpdateApplyButton);
connect(comboBoxCurrentColorCompare,
&QComboBox::currentTextChanged,
this,
&DlgReplaceCueColor::slotUpdateApplyButton);

// Add menu for current color button
m_pCurrentColorPickerAction = new WColorPickerAction(WColorPicker::Option::AllowCustomColor, colorPaletteSettings.getHotcueColorPalette(), this);
m_pCurrentColorPickerAction->setObjectName("HotcueColorPickerAction");
m_pNewColorPickerAction->setSelectedColor(mixxx::PredefinedColorPalettes::kDefaultCueColor);
connect(m_pCurrentColorPickerAction,
&WColorPickerAction::colorPicked,
[this](mixxx::RgbColor::optional_t color) {
if (color) {
setButtonColor(pushButtonCurrentColor, mixxx::RgbColor::toQColor(*color));
slotUpdateApplyButton();
}
m_pCurrentColorMenu->hide();
});
m_pCurrentColorMenu->addAction(m_pCurrentColorPickerAction);
pushButtonCurrentColor->setMenu(m_pCurrentColorMenu);

connect(buttonBox,
&QDialogButtonBox::clicked,
[this](QAbstractButton* button) {
switch (buttonBox->buttonRole(button)) {
case QDialogButtonBox::RejectRole:
reject();
break;
case QDialogButtonBox::ApplyRole:
slotApply();
break;
default:
break;
};
});

slotUpdateApplyButton();
}

DlgReplaceCueColor::~DlgReplaceCueColor() {
}

void DlgReplaceCueColor::slotApply() {
m_bDatabaseChangeInProgress = false;
slotUpdateApplyButton();

// Get values for SELECT query
QProgressDialog progress("Selecting database rows...", "Cancel", 0, 0, this);
Holzhaus marked this conversation as resolved.
Show resolved Hide resolved
progress.setWindowModality(Qt::ApplicationModal);
progress.setAutoReset(false);
progress.setValue(0);

Conditions conditions = ConditionFlag::NoConditions;
if (checkBoxCurrentColorCondition->isChecked()) {
conditions |= ConditionFlag::CurrentColorCheck;
}
if (comboBoxCurrentColorCompare->currentText() == "is not") {
conditions |= ConditionFlag::CurrentColorNotEqual;
}

if (checkBoxHotcueIndexCondition->isChecked()) {
conditions |= ConditionFlag::HotcueIndexCheck;
}
if (comboBoxHotcueIndexCompare->currentText() == "is not") {
conditions |= ConditionFlag::HotcueIndexNotEqual;
}

mixxx::RgbColor::optional_t currentColor = mixxx::RgbColor::fromQString(pushButtonCurrentColor->text());
VERIFY_OR_DEBUG_ASSERT(currentColor) {
m_bDatabaseChangeInProgress = false;
slotUpdateApplyButton();
return;
}

mixxx::RgbColor::optional_t newColor = mixxx::RgbColor::fromQString(pushButtonNewColor->text());
VERIFY_OR_DEBUG_ASSERT(newColor) {
m_bDatabaseChangeInProgress = false;
slotUpdateApplyButton();
return;
}

int hotcueIndex = spinBoxHotcueIndex->value() - 1;

QSqlDatabase database = mixxx::DbConnectionPooled(m_pDbConnectionPool);

// Open the database connection in this thread.
VERIFY_OR_DEBUG_ASSERT(database.isOpen()) {
qWarning() << "Failed to open database for cue color replace dialog."
<< database.lastError();
m_bDatabaseChangeInProgress = false;
slotUpdateApplyButton();
return;
}

// Build SELECT query string
QMap<QString, QVariant> queryValues;
QStringList queryStringConditions;
if (conditions.testFlag(ConditionFlag::CurrentColorCheck)) {
queryStringConditions << QString(
QStringLiteral("color") +
(conditions.testFlag(ConditionFlag::CurrentColorNotEqual) ? QStringLiteral("!=") : QStringLiteral("=")) +
QStringLiteral(":current_color"));
queryValues.insert(QStringLiteral(":current_color"), mixxx::RgbColor::toQVariant(currentColor));
}
if (conditions.testFlag(ConditionFlag::HotcueIndexCheck)) {
queryStringConditions << QString(
QStringLiteral("hotcue") +
(conditions.testFlag(ConditionFlag::HotcueIndexNotEqual) ? QStringLiteral("!=") : QStringLiteral("=")) +
Holzhaus marked this conversation as resolved.
Show resolved Hide resolved
QStringLiteral(":hotcue"));
queryValues.insert(QStringLiteral(":hotcue"), QVariant(hotcueIndex));
}

QString queryString = QStringLiteral("SELECT id, track_id, color FROM " CUE_TABLE);
if (!queryStringConditions.isEmpty()) {
queryString += QStringLiteral(" WHERE ") + queryStringConditions.join(QStringLiteral(" AND "));
}

// Select affected queues
QSqlQuery selectQuery(database);
selectQuery.prepare(queryString);
for (auto i = queryValues.constBegin(); i != queryValues.constEnd(); i++) {
Holzhaus marked this conversation as resolved.
Show resolved Hide resolved
selectQuery.bindValue(i.key(), i.value());
}

// Flush cached tracks to database
QSet<TrackId> cachedTrackIds = GlobalTrackCacheLocker().getCachedTrackIds();
for (const TrackId& trackId : cachedTrackIds) {
TrackPointer pTrack = GlobalTrackCacheLocker().lookupTrackById(trackId);
if (pTrack) {
m_pTrackCollectionManager->saveTrack(pTrack);
}
}

if (!selectQuery.exec()) {
LOG_FAILED_QUERY(selectQuery);
m_bDatabaseChangeInProgress = false;
slotUpdateApplyButton();
return;
}
int idColumn = selectQuery.record().indexOf("id");
int trackIdColumn = selectQuery.record().indexOf("track_id");
int colorColumn = selectQuery.record().indexOf("color");

QList<CueDatabaseRow> rows;
QSet<TrackId> trackIds;
while (selectQuery.next()) {
QCoreApplication::processEvents();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bad idea, because every other GUI slot can be executed in between. Also other DB actions or even another color change attempt. The same below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was suggested by @uklotzde. Also, the dialog is modal so the impact is probably neglible. As a user, if I mess around with colors while the color update is happening I'd actually not be surprised if my color gets overwritten.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We really must not do this. This is one of the Qts anti patterns. We had some of these "solutions" in Mixxx and have removed them all. What is the issue you are trying to solve? There must be a better solution.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My initial attempt was doing the DB operations in another thread, but this can cause inconsistencies. Now I moved everything into the GUI thread. Maybe @uklotzde can elaborate why we need this, but I think the GUI would freeze and the user has no possibility to hit the cancel button without this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have other places in Mixxx where the GUI Freezes more badly because of all kind of Ctrl+A actions. This is nothing we can solve here. Safety over performance. Please just remove this call if we don't have a looking strategy.
I think we can effort a gui freeze here, because it is only a one time action.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree with @daschuer, see my comments (review comments appear at different places in GitHub)

@Holzhaus Please test what happens if you remove the processEvents() call. If the progress dialog becomes unresponsive then removing it is not an option imho.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My test database it not big enough to test this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A modal QProgressDialog does not require to call processEvents(). Please remove it, problem solved.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call is redundant but still happens behind the scenes:

Warning: If the progress dialog is modal (see QProgressDialog::QProgressDialog()), setValue() calls QCoreApplication::processEvents(), so take care that this does not cause undesirable re-entrancy in your code. For example, don't use a QProgressDialog inside a paintEvent()!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But as already discussed this is true for this simple dialog.

if (progress.wasCanceled()) {
m_bDatabaseChangeInProgress = false;
slotUpdateApplyButton();
return;
}
mixxx::RgbColor::optional_t color = mixxx::RgbColor::fromQVariant(selectQuery.value(colorColumn));
VERIFY_OR_DEBUG_ASSERT(color) {
continue;
}
CueDatabaseRow row = {
.id = selectQuery.value(idColumn).toInt(),
.trackId = TrackId(selectQuery.value(trackIdColumn).toInt()),
.color = *color,
};
rows << row;
trackIds << row.trackId;
}

// Cue Selection finished
progress.reset();

if (rows.size() == 0) {
QMessageBox::warning(this, tr("No colors changed!"), tr("No cues matched the specified criteria."));
m_bDatabaseChangeInProgress = false;
slotUpdateApplyButton();
return;
}

if (QMessageBox::question(
this,
tr("Really replace colors?"),
tr("Really replace the colors of %1 cues in %2 tracks? This change cannot be undone!").arg(QString::number(rows.size()), QString::number(trackIds.size()))) == QMessageBox::No) {
Holzhaus marked this conversation as resolved.
Show resolved Hide resolved
m_bDatabaseChangeInProgress = false;
slotUpdateApplyButton();
return;
}

// Update Cues
progress.setLabelText("Updating cues...");
progress.setValue(0);

ScopedTransaction transaction(database);
QSqlQuery query(database);
query.prepare("UPDATE " CUE_TABLE " SET color=:new_color WHERE id=:id AND track_id=:track_id AND color=:current_color");
query.bindValue(":new_color", mixxx::RgbColor::toQVariant(newColor));

bool canceled = false;

QMap<TrackPointer, int> cues;
for (const auto& row : rows) {
QCoreApplication::processEvents();
if (progress.wasCanceled()) {
canceled = true;
break;
}
query.bindValue(":id", row.id);
query.bindValue(":track_id", row.trackId.value());
query.bindValue(":current_color", mixxx::RgbColor::toQVariant(row.color));
if (!query.exec()) {
LOG_FAILED_QUERY(query);
canceled = true;
break;
}

if (query.numRowsAffected() > 0) {
// If the track that these cues belong to is cached, store a
// reference to them so that we can update the in-memory objects
// after committing the database changes
TrackPointer pTrack = GlobalTrackCacheLocker().lookupTrackById(row.trackId);
if (pTrack) {
cues.insertMulti(pTrack, row.id);
}
}
}

if (canceled) {
transaction.rollback();
} else {
transaction.commit();
trackIds.clear();

// Update the cue colors for in-memory track objects
for (auto it = cues.constBegin(); it != cues.constEnd(); it++) {
TrackPointer pTrack = it.key();
VERIFY_OR_DEBUG_ASSERT(pTrack) {
continue;
}
CuePointer pCue = pTrack->findCueById(it.value());
if (pCue) {
pCue->setColor(*newColor);
trackIds << pTrack->getId();
}
}
emit databaseTracksChanged(trackIds);
m_bDatabaseChanged = true;
}

progress.reset();
m_bDatabaseChangeInProgress = false;
slotUpdateApplyButton();
}

void DlgReplaceCueColor::slotUpdateApplyButton() {
bool bEnabled = !m_bDatabaseChangeInProgress;
if (bEnabled &&
checkBoxCurrentColorCondition->isChecked() &&
comboBoxCurrentColorCompare->currentText() == "is") {
mixxx::RgbColor::optional_t currentColor = mixxx::RgbColor::fromQString(
pushButtonCurrentColor->text());
mixxx::RgbColor::optional_t newColor = mixxx::RgbColor::fromQString(
pushButtonNewColor->text());
if (currentColor == newColor) {
bEnabled = false;
}
}

QPushButton* button = buttonBox->button(QDialogButtonBox::Apply);
if (button) {
button->setEnabled(bEnabled);
}
}

void DlgReplaceCueColor::reject() {
if (m_bDatabaseChanged) {
QDialog::accept();
qWarning() << "DlgReplaceCueColor::accept";
} else {
QDialog::reject();
qWarning() << "DlgReplaceCueColor::reject";
}
}
Loading