diff --git a/CMakeLists.txt b/CMakeLists.txt index c1cac0e47f7..6bab9a363cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -235,6 +235,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 diff --git a/build/depends.py b/build/depends.py index d4752340f95..2c6598ab7a7 100644 --- a/build/depends.py +++ b/build/depends.py @@ -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", @@ -1346,6 +1347,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', diff --git a/src/dialog/dlgreplacecuecolor.cpp b/src/dialog/dlgreplacecuecolor.cpp new file mode 100644 index 00000000000..7c09bccef59 --- /dev/null +++ b/src/dialog/dlgreplacecuecolor.cpp @@ -0,0 +1,462 @@ +#include "dialog/dlgreplacecuecolor.h" + +#include +#include +#include +#include +#include +#include + +#include "engine/controls/cuecontrol.h" +#include "library/dao/cuedao.h" +#include "library/queryutil.h" +#include "preferences/colorpalettesettings.h" +#include "util/color/predefinedcolorpalettes.h" + +namespace { + +const QString kColorButtonStyleSheetLight = QStringLiteral( + "QPushButton { background-color: %1; color: black; }"); +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) + ? kColorButtonStyleSheetDark + : kColorButtonStyleSheetLight) + .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_pNewColorMenu(new QMenu(this)), + m_pCurrentColorMenu(new QMenu(this)), + m_lastAutoSetNewColor(std::nullopt), + m_lastAutoSetCurrentColor(std::nullopt), + m_pStyle(QStyleFactory::create(QStringLiteral("fusion"))) { + setupUi(this); + setWindowModality(Qt::ApplicationModal); + + spinBoxHotcueIndex->setMaximum(NUM_HOT_CUES); + + QIcon icon = QIcon::fromTheme("dialog-warning"); + if (!icon.isNull()) { + labelReplaceAllColorsIcon->setPixmap(icon.pixmap(QSize(32, 32))); + } else { + labelReplaceAllColorsIcon->hide(); + } + + // Unfortunately, not all styles supported by Qt support setting a + // background color for QPushButtons (see + // https://bugreports.qt.io/browse/QTBUG-11089). For example, when using + // the gtk2 style all color buttons would be just grey. It's possible to + // work around this by modifying the button border with a QSS stylesheet, + // so that the QStyle will be overwritten, but as a sane default for skins + // without styles for WColorPicker, we're setting the platform-independent + // "Fusion" style here. This will make the buttons look slightly different + // from the rest of the application (when not styled via QSS), but that's + // better than having buttons without any colors (which would make the + // color picker unusable). + pushButtonNewColor->setStyle(m_pStyle); + pushButtonCurrentColor->setStyle(m_pStyle); + + // Set up new color button + ColorPaletteSettings colorPaletteSettings(pConfig); + ColorPalette hotcuePalette = colorPaletteSettings.getHotcueColorPalette(); + mixxx::RgbColor firstColor = mixxx::PredefinedColorPalettes::kDefaultCueColor; + DEBUG_ASSERT(hotcuePalette.size() > 0); + 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 = 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)); + slotUpdateWidgets(); + } + 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)); + + // Update apply button when the current color comparison combobox is + // modified + connect(comboBoxCurrentColorCompare, + &QComboBox::currentTextChanged, + this, + &DlgReplaceCueColor::slotUpdateWidgets); + + // Add menu for current color button + m_pCurrentColorPickerAction = make_parented( + 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)); + slotUpdateWidgets(); + } + m_pCurrentColorMenu->hide(); + }); + m_pCurrentColorMenu->addAction(m_pCurrentColorPickerAction); + pushButtonCurrentColor->setMenu(m_pCurrentColorMenu); + + // Update dialog widgets when conditions checkboxes are (un)checked + connect(checkBoxCurrentColorCondition, + &QCheckBox::stateChanged, + this, + &DlgReplaceCueColor::slotUpdateWidgets); + connect(checkBoxHotcueIndexCondition, + &QCheckBox::stateChanged, + this, + &DlgReplaceCueColor::slotUpdateWidgets); + + connect(buttonBox, + &QDialogButtonBox::clicked, + [this](QAbstractButton* button) { + switch (buttonBox->buttonRole(button)) { + case QDialogButtonBox::RejectRole: + reject(); + break; + case QDialogButtonBox::ApplyRole: + slotApply(); + break; + default: + break; + }; + }); + + slotUpdateWidgets(); +} + +DlgReplaceCueColor::~DlgReplaceCueColor() { +} + +void DlgReplaceCueColor::setColorPalette(const ColorPalette& palette) { + m_pNewColorPickerAction->setColorPalette(palette); + QResizeEvent resizeNewColorMenuEvent(QSize(), m_pNewColorMenu->size()); + qApp->sendEvent(m_pNewColorMenu, &resizeNewColorMenuEvent); + m_pCurrentColorPickerAction->setColorPalette(palette); + QResizeEvent resizeCurrentColorMenuEvent(QSize(), m_pCurrentColorMenu->size()); + qApp->sendEvent(m_pCurrentColorMenu, &resizeCurrentColorMenuEvent); +} + +void DlgReplaceCueColor::setNewColor(mixxx::RgbColor color) { + mixxx::RgbColor::optional_t buttonColor = mixxx::RgbColor::fromQString( + pushButtonNewColor->text()); + + // Make sure we don't overwrite colors selected by the user + if (!m_lastAutoSetNewColor || m_lastAutoSetNewColor == buttonColor) { + setButtonColor(pushButtonNewColor, mixxx::RgbColor::toQColor(color)); + m_pNewColorPickerAction->setSelectedColor(color); + slotUpdateWidgets(); + } + + m_lastAutoSetNewColor = color; +} + +void DlgReplaceCueColor::setCurrentColor(mixxx::RgbColor color) { + mixxx::RgbColor::optional_t buttonColor = mixxx::RgbColor::fromQString( + pushButtonCurrentColor->text()); + + // Make sure we don't overwrite colors selected by the user + if (!m_lastAutoSetCurrentColor || m_lastAutoSetCurrentColor == buttonColor) { + setButtonColor(pushButtonCurrentColor, mixxx::RgbColor::toQColor(color)); + m_pCurrentColorPickerAction->setSelectedColor(color); + slotUpdateWidgets(); + } + + m_lastAutoSetCurrentColor = color; +} + +void DlgReplaceCueColor::slotApply() { + m_bDatabaseChangeInProgress = false; + slotUpdateWidgets(); + + // Get values for SELECT query + QProgressDialog progress(this); + progress.setWindowModality(Qt::ApplicationModal); + progress.setAutoReset(false); + progress.setLabelText(tr("Selecting database rows...")); + progress.setMaximum(0); + 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; + slotUpdateWidgets(); + return; + } + + mixxx::RgbColor::optional_t newColor = + mixxx::RgbColor::fromQString(pushButtonNewColor->text()); + VERIFY_OR_DEBUG_ASSERT(newColor) { + m_bDatabaseChangeInProgress = false; + slotUpdateWidgets(); + 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; + slotUpdateWidgets(); + return; + } + + // Build SELECT query string + QMap 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("=")) + + QStringLiteral(":hotcue")); + queryValues.insert(QStringLiteral(":hotcue"), QVariant(hotcueIndex)); + } + + QString queryString = + QStringLiteral("SELECT id, track_id, color FROM " CUE_TABLE + " WHERE color != :new_color"); + queryValues.insert(QStringLiteral(":new_color"), + mixxx::RgbColor::toQVariant(newColor)); + if (!queryStringConditions.isEmpty()) { + queryString += QStringLiteral(" AND ") + + queryStringConditions.join(QStringLiteral(" AND ")); + } + + // Select affected queues + QSqlQuery selectQuery(database); + selectQuery.prepare(queryString); + for (auto i = queryValues.constBegin(); i != queryValues.constEnd(); i++) { + selectQuery.bindValue(i.key(), i.value()); + } + + // Flush cached tracks to database + QSet 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; + slotUpdateWidgets(); + return; + } + int idColumn = selectQuery.record().indexOf("id"); + int trackIdColumn = selectQuery.record().indexOf("track_id"); + int colorColumn = selectQuery.record().indexOf("color"); + + QList rows; + QSet trackIds; + while (selectQuery.next()) { + QCoreApplication::processEvents(); + if (progress.wasCanceled()) { + m_bDatabaseChangeInProgress = false; + slotUpdateWidgets(); + 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; + slotUpdateWidgets(); + return; + } + + if (QMessageBox::question(this, + tr("Confirm Color Replacement"), + tr("The colors of %1 cues in %2 tracks will be replaced. This " + "change cannot be undone! Are you sure?") + .arg(QString::number(rows.size()), + QString::number(trackIds.size()))) == + QMessageBox::No) { + m_bDatabaseChangeInProgress = false; + slotUpdateWidgets(); + 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 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); + } + + progress.reset(); + m_bDatabaseChangeInProgress = false; + slotUpdateWidgets(); + accept(); +} + +void DlgReplaceCueColor::slotUpdateWidgets() { + 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; + } + } + + if (checkBoxCurrentColorCondition->isChecked() || checkBoxHotcueIndexCondition->isChecked()) { + frameReplaceAllColors->hide(); + } else { + frameReplaceAllColors->show(); + } + + QPushButton* button = buttonBox->button(QDialogButtonBox::Apply); + if (button) { + button->setEnabled(bEnabled); + } +} diff --git a/src/dialog/dlgreplacecuecolor.h b/src/dialog/dlgreplacecuecolor.h new file mode 100644 index 00000000000..9ded791ef23 --- /dev/null +++ b/src/dialog/dlgreplacecuecolor.h @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "dialog/ui_dlgreplacecuecolordlg.h" +#include "library/dao/trackdao.h" +#include "library/trackcollectionmanager.h" +#include "preferences/usersettings.h" +#include "track/trackid.h" +#include "util/color/rgbcolor.h" +#include "util/db/dbconnectionpooled.h" +#include "util/db/dbconnectionpooler.h" +#include "widget/wcolorpickeraction.h" + +/// Dialog for bulk replacing colors of cues in the Database. +class DlgReplaceCueColor : public QDialog, public Ui::DlgReplaceCueColor { + Q_OBJECT + public: + enum class ConditionFlag { + NoConditions = 0, + CurrentColorCheck = 1, + CurrentColorNotEqual = 1 << 1, + HotcueIndexCheck = 1 << 2, + HotcueIndexNotEqual = 1 << 3, + }; + Q_DECLARE_FLAGS(Conditions, ConditionFlag); + + DlgReplaceCueColor( + UserSettingsPointer pConfig, + mixxx::DbConnectionPoolPtr dbConnectionPool, + TrackCollectionManager* pTrackCollectionManager, + QWidget* pParent); + ~DlgReplaceCueColor(); + + void setColorPalette(const ColorPalette& palette); + + void setNewColor(mixxx::RgbColor color); + void setCurrentColor(mixxx::RgbColor color); + + signals: + void databaseTracksChanged(QSet Trackids); + + private slots: + void slotApply(); + + /// Update the dialog widgets based on the currently selected values. + // + // This may disable the Apply button if the replacement would be a no-op + // (i.e. if the new color is the same as the current color). This slot also + // shows/hides the "replaces all colors" warning. + void slotUpdateWidgets(); + + private: + const UserSettingsPointer m_pConfig; + mixxx::DbConnectionPoolPtr m_pDbConnectionPool; + TrackCollectionManager* m_pTrackCollectionManager; + bool m_bDatabaseChangeInProgress; + QMenu* m_pNewColorMenu; + QMenu* m_pCurrentColorMenu; + parented_ptr m_pNewColorPickerAction; + parented_ptr m_pCurrentColorPickerAction; + mixxx::RgbColor::optional_t m_lastAutoSetNewColor; + mixxx::RgbColor::optional_t m_lastAutoSetCurrentColor; + QStyle* m_pStyle; +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(DlgReplaceCueColor::Conditions); diff --git a/src/dialog/dlgreplacecuecolordlg.ui b/src/dialog/dlgreplacecuecolordlg.ui new file mode 100644 index 00000000000..3d6afd09494 --- /dev/null +++ b/src/dialog/dlgreplacecuecolordlg.ui @@ -0,0 +1,218 @@ + + + DlgReplaceCueColor + + + + 0 + 0 + 315 + 249 + + + + + 0 + 0 + + + + Replace Hotcue Color + + + + + + Replace cue color if … + + + + + + true + + + 1 + + + + + + + true + + + Hotcue index + + + + + + + true + + + + is + + + + + is not + + + + + + + + Current cue color + + + true + + + + + + + + is + + + + + is not + + + + + + + + + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + 1 + + + If you don't specify any conditions, the colors of all cues in the library will be replaced. + + + Qt::PlainText + + + false + + + true + + + + + + + + + + + + + … by: + + + + + + New cue color + + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Apply|QDialogButtonBox::Close + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + diff --git a/src/mixxx.cpp b/src/mixxx.cpp index caaaa2fbe4b..586cc151016 100644 --- a/src/mixxx.cpp +++ b/src/mixxx.cpp @@ -18,67 +18,68 @@ #include "mixxx.h" #include -#include #include #include +#include #include -#include -#include -#include #include #include -#include +#include #include +#include +#include +#include #include "dialog/dlgabout.h" -#include "preferences/dialog/dlgpreferences.h" -#include "preferences/dialog/dlgprefeq.h" -#include "preferences/constants.h" #include "dialog/dlgdevelopertools.h" -#include "engine/enginemaster.h" -#include "effects/effectsmanager.h" #include "effects/builtin/builtinbackend.h" +#include "effects/effectsmanager.h" +#include "engine/enginemaster.h" +#include "preferences/constants.h" +#include "preferences/dialog/dlgprefeq.h" +#include "preferences/dialog/dlgpreferences.h" #ifdef __LILV__ #include "effects/lv2/lv2backend.h" #endif +#include "broadcast/broadcastmanager.h" +#include "control/controlpushbutton.h" +#include "controllers/controllermanager.h" +#include "controllers/keyboard/keyboardeventfilter.h" +#include "database/mixxxdb.h" #include "library/coverartcache.h" #include "library/library.h" #include "library/library_preferences.h" +#include "library/trackcollection.h" #include "library/trackcollectionmanager.h" -#include "controllers/controllermanager.h" -#include "controllers/keyboard/keyboardeventfilter.h" +#include "mixer/playerinfo.h" #include "mixer/playermanager.h" +#include "preferences/settingsmanager.h" #include "recording/recordingmanager.h" -#include "broadcast/broadcastmanager.h" +#include "skin/launchimage.h" #include "skin/legacyskinparser.h" #include "skin/skinloader.h" #include "soundio/soundmanager.h" #include "sources/soundsourceproxy.h" #include "track/track.h" -#include "waveform/waveformwidgetfactory.h" -#include "waveform/visualsmanager.h" -#include "waveform/sharedglcontext.h" -#include "database/mixxxdb.h" +#include "util/compatibility.h" +#include "util/db/dbconnectionpooled.h" #include "util/debug.h" +#include "util/experiment.h" +#include "util/font.h" +#include "util/logger.h" +#include "util/math.h" +#include "util/sandbox.h" +#include "util/screensaver.h" #include "util/statsmanager.h" -#include "util/timer.h" #include "util/time.h" +#include "util/timer.h" +#include "util/translations.h" #include "util/version.h" -#include "control/controlpushbutton.h" -#include "util/sandbox.h" -#include "mixer/playerinfo.h" #include "waveform/guitick.h" -#include "util/math.h" -#include "util/experiment.h" -#include "util/font.h" -#include "util/translations.h" -#include "skin/launchimage.h" -#include "preferences/settingsmanager.h" +#include "waveform/sharedglcontext.h" +#include "waveform/visualsmanager.h" +#include "waveform/waveformwidgetfactory.h" #include "widget/wmainmenubar.h" -#include "util/compatibility.h" -#include "util/screensaver.h" -#include "util/logger.h" -#include "util/db/dbconnectionpooled.h" #ifdef __VINYLCONTROL__ #include "vinylcontrol/vinylcontrolmanager.h" diff --git a/src/preferences/dialog/dlgprefcolors.cpp b/src/preferences/dialog/dlgprefcolors.cpp index 2b9779ac32f..0f10c9a1f5d 100644 --- a/src/preferences/dialog/dlgprefcolors.cpp +++ b/src/preferences/dialog/dlgprefcolors.cpp @@ -6,6 +6,9 @@ #include #include "control/controlobject.h" +#include "dialog/dlgreplacecuecolor.h" +#include "library/library.h" +#include "library/trackcollection.h" #include "util/color/predefinedcolorpalettes.h" #include "util/compatibility.h" #include "util/math.h" @@ -15,18 +18,30 @@ namespace { constexpr int kHotcueDefaultColorIndex = -1; constexpr QSize kPalettePreviewSize = QSize(108, 16); - +const ConfigKey kAutoHotcueColorsConfigKey("[Controls]", "auto_hotcue_colors"); +const ConfigKey kHotcueDefaultColorIndexConfigKey("[Controls]", "HotcueDefaultColorIndex"); } // anonymous namespace DlgPrefColors::DlgPrefColors( - QWidget* parent, UserSettingsPointer pConfig) + QWidget* parent, UserSettingsPointer pConfig, Library* pLibrary) : DlgPreferencePage(parent), m_pConfig(pConfig), - m_colorPaletteSettings(ColorPaletteSettings(pConfig)) { + m_colorPaletteSettings(ColorPaletteSettings(pConfig)), + m_pReplaceCueColorDlg(new DlgReplaceCueColor( + pConfig, + pLibrary->dbConnectionPool(), + pLibrary->trackCollections(), + this)) { setupUi(this); comboBoxHotcueColors->setIconSize(kPalettePreviewSize); comboBoxTrackColors->setIconSize(kPalettePreviewSize); + m_pReplaceCueColorDlg->setHidden(true); + connect(m_pReplaceCueColorDlg, + &DlgReplaceCueColor::databaseTracksChanged, + &(pLibrary->trackCollections()->internalCollection()->getTrackDAO()), + &TrackDAO::databaseTracksChanged); + connect(comboBoxHotcueColors, QOverload::of(&QComboBox::currentIndexChanged), this, @@ -42,6 +57,11 @@ DlgPrefColors::DlgPrefColors( this, &DlgPrefColors::slotEditTrackPaletteClicked); + connect(pushButtonReplaceCueColor, + &QPushButton::clicked, + this, + &DlgPrefColors::slotReplaceCueColorClicked); + slotUpdate(); } @@ -89,25 +109,30 @@ void DlgPrefColors::slotUpdate() { hotcuePalette.getName()); slotHotcuePaletteChanged(hotcuePalette.getName()); - bool autoHotcueColors = - m_pConfig->getValue(ConfigKey("[Controls]", "auto_hotcue_colors"), false); + bool autoHotcueColors = m_pConfig->getValue(kAutoHotcueColorsConfigKey, false); if (autoHotcueColors) { comboBoxHotcueDefaultColor->setCurrentIndex(0); } else { - int hotcueDefaultColorIndex = m_pConfig->getValue(ConfigKey("[Controls]", "HotcueDefaultColorIndex"), kHotcueDefaultColorIndex); - if (hotcueDefaultColorIndex < 0 || hotcueDefaultColorIndex >= hotcuePalette.size()) { - hotcueDefaultColorIndex = hotcuePalette.size() - 1; // default to last color (orange) + int hotcueDefaultColorIndex = m_pConfig->getValue( + kHotcueDefaultColorIndexConfigKey, kHotcueDefaultColorIndex); + if (hotcueDefaultColorIndex < 0 || + hotcueDefaultColorIndex >= hotcuePalette.size()) { + hotcueDefaultColorIndex = + hotcuePalette.size() - 1; // default to last color (orange) } - comboBoxHotcueDefaultColor->setCurrentIndex(hotcueDefaultColorIndex + 1); + comboBoxHotcueDefaultColor->setCurrentIndex( + hotcueDefaultColorIndex + 1); } } // Set the default values for all the widgets void DlgPrefColors::slotResetToDefaults() { comboBoxHotcueColors->setCurrentText( - mixxx::PredefinedColorPalettes::kDefaultHotcueColorPalette.getName()); + mixxx::PredefinedColorPalettes::kDefaultHotcueColorPalette + .getName()); comboBoxTrackColors->setCurrentText( - mixxx::PredefinedColorPalettes::kDefaultTrackColorPalette.getName()); + mixxx::PredefinedColorPalettes::kDefaultTrackColorPalette + .getName()); comboBoxHotcueDefaultColor->setCurrentIndex( mixxx::PredefinedColorPalettes::kDefaultTrackColorPalette.size()); slotApply(); @@ -120,12 +145,15 @@ void DlgPrefColors::slotApply() { bool bHotcueColorPaletteFound = false; bool bTrackColorPaletteFound = false; - for (const auto& palette : qAsConst(mixxx::PredefinedColorPalettes::kPalettes)) { - if (!bHotcueColorPaletteFound && hotcueColorPaletteName == palette.getName()) { + for (const auto& palette : + qAsConst(mixxx::PredefinedColorPalettes::kPalettes)) { + if (!bHotcueColorPaletteFound && + hotcueColorPaletteName == palette.getName()) { m_colorPaletteSettings.setHotcueColorPalette(palette); bHotcueColorPaletteFound = true; } - if (!bTrackColorPaletteFound && trackColorPaletteName == palette.getName()) { + if (!bTrackColorPaletteFound && + trackColorPaletteName == palette.getName()) { m_colorPaletteSettings.setTrackColorPalette(palette); bTrackColorPaletteFound = true; } @@ -146,14 +174,46 @@ void DlgPrefColors::slotApply() { int index = comboBoxHotcueDefaultColor->currentIndex(); if (index > 0) { - m_pConfig->setValue(ConfigKey("[Controls]", "auto_hotcue_colors"), false); - m_pConfig->setValue(ConfigKey("[Controls]", "HotcueDefaultColorIndex"), index - 1); + m_pConfig->setValue(kAutoHotcueColorsConfigKey, false); + m_pConfig->setValue(kHotcueDefaultColorIndexConfigKey, index - 1); } else { - m_pConfig->setValue(ConfigKey("[Controls]", "auto_hotcue_colors"), true); - m_pConfig->setValue(ConfigKey("[Controls]", "HotcueDefaultColorIndex"), -1); + m_pConfig->setValue(kAutoHotcueColorsConfigKey, true); + m_pConfig->setValue(kHotcueDefaultColorIndexConfigKey, -1); } } +void DlgPrefColors::slotReplaceCueColorClicked() { + ColorPaletteSettings colorPaletteSettings(m_pConfig); + + ColorPalette savedPalette = colorPaletteSettings.getHotcueColorPalette(); + ColorPalette newPalette = colorPaletteSettings.getColorPalette( + comboBoxHotcueColors->currentText(), savedPalette); + m_pReplaceCueColorDlg->setColorPalette(newPalette); + + int savedDefaultColorIndex = m_pConfig->getValue( + kHotcueDefaultColorIndexConfigKey, savedPalette.size() - 1); + if (savedDefaultColorIndex < 0) { + savedDefaultColorIndex = 0; + } + mixxx::RgbColor savedDefaultColor = + savedPalette.at(savedDefaultColorIndex % savedPalette.size()); + + int newDefaultColorIndex = comboBoxHotcueDefaultColor->currentIndex() - 1; + if (newDefaultColorIndex < 0) { + newDefaultColorIndex = 0; + } + mixxx::RgbColor newDefaultColor = newPalette.at(newDefaultColorIndex % newPalette.size()); + + m_pReplaceCueColorDlg->setCurrentColor(savedDefaultColor); + if (savedDefaultColor != newDefaultColor) { + m_pReplaceCueColorDlg->setNewColor(newDefaultColor); + } + + m_pReplaceCueColorDlg->show(); + m_pReplaceCueColorDlg->raise(); + m_pReplaceCueColorDlg->activateWindow(); +} + QPixmap DlgPrefColors::drawPalettePreview(const QString& paletteName) { ColorPalette palette = m_colorPaletteSettings.getHotcueColorPalette(paletteName); if (paletteName == palette.getName()) { diff --git a/src/preferences/dialog/dlgprefcolors.h b/src/preferences/dialog/dlgprefcolors.h index b17e544aaa8..029894af903 100644 --- a/src/preferences/dialog/dlgprefcolors.h +++ b/src/preferences/dialog/dlgprefcolors.h @@ -10,10 +10,13 @@ #include "preferences/usersettings.h" #include "util/parented_ptr.h" +class DlgReplaceCueColor; +class Library; + class DlgPrefColors : public DlgPreferencePage, public Ui::DlgPrefColorsDlg { Q_OBJECT public: - DlgPrefColors(QWidget* parent, UserSettingsPointer pConfig); + DlgPrefColors(QWidget* parent, UserSettingsPointer pConfig, Library* pLibrary); virtual ~DlgPrefColors(); public slots: @@ -32,6 +35,7 @@ class DlgPrefColors : public DlgPreferencePage, public Ui::DlgPrefColorsDlg { void trackPaletteUpdated(const QString& palette); void hotcuePaletteUpdated(const QString& palette); void palettesUpdated(); + void slotReplaceCueColorClicked(); void slotEditTrackPaletteClicked(); void slotEditHotcuePaletteClicked(); @@ -48,4 +52,6 @@ class DlgPrefColors : public DlgPreferencePage, public Ui::DlgPrefColorsDlg { const UserSettingsPointer m_pConfig; ColorPaletteSettings m_colorPaletteSettings; + // Pointer to color replace dialog + DlgReplaceCueColor* m_pReplaceCueColorDlg; }; diff --git a/src/preferences/dialog/dlgprefcolorsdlg.ui b/src/preferences/dialog/dlgprefcolorsdlg.ui index c9d3f25a375..3c7ea1b7e8e 100644 --- a/src/preferences/dialog/dlgprefcolorsdlg.ui +++ b/src/preferences/dialog/dlgprefcolorsdlg.ui @@ -26,6 +26,30 @@ Colors + + + + Edit… + + + + + + + + + + Edit… + + + + + + + Track palette + + + @@ -43,27 +67,13 @@ - - - - Track palette - - - - + Hotcue default color - - - - Edit… - - - @@ -74,13 +84,10 @@ - - - - - + + - Edit… + Replace… diff --git a/src/preferences/dialog/dlgpreferences.cpp b/src/preferences/dialog/dlgpreferences.cpp index 61b7b1828e6..9e911b283eb 100644 --- a/src/preferences/dialog/dlgpreferences.cpp +++ b/src/preferences/dialog/dlgpreferences.cpp @@ -122,7 +122,7 @@ DlgPreferences::DlgPreferences(MixxxMainWindow * mixxx, SkinLoader* pSkinLoader, addPageWidget(m_waveformPage); m_deckPage = new DlgPrefDeck(this, mixxx, pPlayerManager, m_pConfig); addPageWidget(m_deckPage); - m_colorsPage = new DlgPrefColors(this, m_pConfig); + m_colorsPage = new DlgPrefColors(this, m_pConfig, pLibrary); addPageWidget(m_colorsPage); m_equalizerPage = new DlgPrefEQ(this, pEffectsManager, m_pConfig); addPageWidget(m_equalizerPage); diff --git a/src/track/globaltrackcache.cpp b/src/track/globaltrackcache.cpp index 054ae673758..257c58af5b4 100644 --- a/src/track/globaltrackcache.cpp +++ b/src/track/globaltrackcache.cpp @@ -145,6 +145,11 @@ TrackPointer GlobalTrackCacheLocker::lookupTrackByRef( return m_pInstance->lookupByRef(trackRef); } +QSet GlobalTrackCacheLocker::getCachedTrackIds() const { + DEBUG_ASSERT(m_pInstance); + return m_pInstance->getCachedTrackIds(); +} + GlobalTrackCacheResolver::GlobalTrackCacheResolver( TrackFile fileInfo, SecurityTokenPointer pSecurityToken) @@ -467,6 +472,14 @@ TrackPointer GlobalTrackCache::lookupByRef( } } +QSet GlobalTrackCache::getCachedTrackIds() const { + QSet trackIds; + for (const auto& entry : m_tracksById) { + trackIds << entry.first; + } + return trackIds; +} + TrackPointer GlobalTrackCache::revive( GlobalTrackCacheEntryPointer entryPtr) { diff --git a/src/track/globaltrackcache.h b/src/track/globaltrackcache.h index 4cabed92097..e7fb049ad42 100644 --- a/src/track/globaltrackcache.h +++ b/src/track/globaltrackcache.h @@ -112,8 +112,9 @@ class GlobalTrackCacheLocker { const TrackId& trackId) const; TrackPointer lookupTrackByRef( const TrackRef& trackRef) const; + QSet getCachedTrackIds() const; -private: + private: friend class GlobalTrackCache; void lockCache(); @@ -220,6 +221,7 @@ private slots: const TrackId& trackId); TrackPointer lookupByRef( const TrackRef& trackRef); + QSet getCachedTrackIds() const; TrackPointer revive(GlobalTrackCacheEntryPointer entryPtr); diff --git a/src/track/track.cpp b/src/track/track.cpp index ae118da2c0f..222e27955c3 100644 --- a/src/track/track.cpp +++ b/src/track/track.cpp @@ -767,6 +767,16 @@ CuePointer Track::findCueByType(mixxx::CueType type) const { return CuePointer(); } +CuePointer Track::findCueById(int id) const { + QMutexLocker lock(&m_qMutex); + for (const CuePointer& pCue : m_cuePoints) { + if (pCue->getId() == id) { + return pCue; + } + } + return CuePointer(); +} + void Track::removeCue(const CuePointer& pCue) { if (pCue == nullptr) { return; diff --git a/src/track/track.h b/src/track/track.h index cec23953f98..7d5cabad05e 100644 --- a/src/track/track.h +++ b/src/track/track.h @@ -251,6 +251,7 @@ class Track : public QObject { // Calls for managing the track's cue points CuePointer createAndAddCue(); CuePointer findCueByType(mixxx::CueType type) const; // NOTE: Cannot be used for hotcues. + CuePointer findCueById(int id) const; void removeCue(const CuePointer& pCue); void removeCuesOfType(mixxx::CueType); QList getCuePoints() const;