Something went wrong. Try again.
Mixxx DJ software with MCP, Subsonic/Navidrome streaming library support — browse and load tracks from your self-hosted music server mixxx.org
subsonic navidrome streaming audio dj mac linux midi
Something went wrong. Try again.
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635#include "mixxxmainwindow.h"
#include <QCheckBox>#include <QCloseEvent>#include <QDebug>#include <QFileDialog>#include <QOpenGLContext>#include <QUrl>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)#include <QGLFormat>#endif
#if defined(__LINUX__) && !defined(__ANDROID__)#include <QDBusConnection>#include <QDBusConnectionInterface>#endif
#ifdef MIXXX_USE_QOPENGL#include <QGuiApplication>
#include "widget/tooltipqopengl.h"#include "widget/winitialglwidget.h"#endif
#include "controllers/keyboard/keyboardeventfilter.h"#include "coreservices.h"#include "defs_urls.h"#include "dialog/dlgabout.h"#include "dialog/dlgdevelopertools.h"#include "dialog/dlgkeywheel.h"#include "moc_mixxxmainwindow.cpp"#include "preferences/dialog/dlgpreferences.h"#ifdef __BROADCAST__#include "broadcast/broadcastmanager.h"#endif#include "control/controlindicatortimer.h"#include "library/library.h"#include "library/library_decl.h"#include "library/library_prefs.h"#ifdef __ENGINEPRIME__#include "library/export/libraryexporter.h"#endif#include "library/library_prefs.h"#include "library/trackcollectionmanager.h"#include "mixer/playerinfo.h"#include "mixer/playermanager.h"#include "recording/recordingmanager.h"#include "skin/legacy/launchimage.h"#include "skin/skinloader.h"#include "soundio/soundmanager.h"#include "sources/soundsourceproxy.h"#include "track/track.h"#include "util/debug.h"#include "util/desktophelper.h"#include "util/menubarhelper.h"#include "util/sandbox.h"#include "util/scopedoverridecursor.h"#include "util/timer.h"#include "util/versionstore.h"#include "waveform/guitick.h"#include "waveform/sharedglcontext.h"#include "waveform/visualsmanager.h"#include "waveform/waveformwidgetfactory.h"#include "widget/wglwidget.h"#include "widget/wmainmenubar.h"
#ifdef __VINYLCONTROL__#include "vinylcontrol/vinylcontrolmanager.h"#endif
namespace {const ConfigKey kHideMenuBarConfigKey = ConfigKey("[Config]", "hide_menubar");const ConfigKey kMenuBarHintConfigKey = ConfigKey("[Config]", "show_menubar_hint");} // namespace
MixxxMainWindow::MixxxMainWindow(std::shared_ptr<mixxx::CoreServices> pCoreServices) : m_pCoreServices(pCoreServices), m_pCentralWidget(nullptr), m_pLaunchImage(nullptr),#ifndef __APPLE__ m_prevState(Qt::WindowNoState),#endif m_noVinylInputDialog(nullptr), m_noPassthroughInputDialog(nullptr), m_noMicInputDialog(nullptr), m_noAuxInputDialog(nullptr), m_pGuiTick(nullptr),#ifdef __LINUX__ m_supportsGlobalMenuBar(mixxx::desktopSupportsGlobalMenuBar()),#endif m_inRebootMixxxView(false), m_pDeveloperToolsDlg(nullptr), m_pPrefDlg(nullptr), m_toolTipsCfg(mixxx::preferences::Tooltips::On) { DEBUG_ASSERT(pCoreServices); // These depend on the settings#ifdef __LINUX__ // If the desktop features a global menubar and we'll go fullscreen during // startup, set Qt::AA_DontUseNativeMenuBar so the menubar is placed in the // window like it's done in slotViewFullScreen(). On other desktops this // attribute has no effect. This is a safe alternative to setNativeMenuBar() // which can cause a crash when using menu shortcuts like Alt+F after resetting // the menubar. See https://github.com/mixxxdj/mixxx/issues/11320 if (m_supportsGlobalMenuBar) { bool fullscreenPref = m_pCoreServices->getSettings()->getValue<bool>( ConfigKey("[Config]", "StartInFullscreen")); QApplication::setAttribute( Qt::AA_DontUseNativeMenuBar, CmdlineArgs::Instance().getStartInFullscreen() || fullscreenPref); }#endif // __LINUX__
connect(m_pCoreServices.get(), &mixxx::CoreServices::libraryScanSummary, this, &MixxxMainWindow::slotLibraryScanSummaryDlg);
createMenuBar(); m_pMenuBar->hide();
initializeWindow();
// Show launch image immediately so the user knows Mixxx is starting m_pSkinLoader = std::make_unique<mixxx::skin::SkinLoader>(m_pCoreServices->getSettings()); m_pLaunchImage = m_pSkinLoader->loadLaunchImage(this); m_pCentralWidget = (QWidget*)m_pLaunchImage; setCentralWidget(m_pCentralWidget);
show();
m_pGuiTick = new GuiTick(); m_pVisualsManager = new VisualsManager();}
#ifdef MIXXX_USE_QOPENGLvoid MixxxMainWindow::initializeQOpenGL() {#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // Qt 6 will nno longer crash if no GL is available and // QGLFormat::hasOpenGL() has been removed. if (!CmdlineArgs::Instance().getSafeMode() && QGLFormat::hasOpenGL()) {#else // With EGLFS there is always exactly one native window and one EGL window surface // OpenGL windows cannot be embedded into our QWidgets main window we already have. // https://doc.qt.io/qt-6/embedded-linux.html bool isEglfs = QGuiApplication::platformName() == "eglfs";
if (!CmdlineArgs::Instance().getSafeMode() && !isEglfs) {#endif QOpenGLContext context; context.setFormat(WaveformWidgetFactory::getSurfaceFormat(m_pCoreServices->getSettings())); if (context.create()) { std::pair version = context.format().version(); qDebug().noquote() << "QOpenGLContext created:" << QGuiApplication::platformName() << context.format().renderableType() << QString("V%1.%2").arg(QString::number(version.first), QString::number(version.second)) << context.format().profile(); // This widget and its QOpenGLWindow will be used to query QOpenGL // information (version, driver, etc) in WaveformWidgetFactory. // The "SharedGLContext" terminology here doesn't really apply, // but allows us to take advantage of the existing classes. auto pWidget = make_parented<WInitialGLWidget>(this); pWidget->setGeometry(QRect(0, 0, 3, 3)); SharedGLContext::setWidget(pWidget); // When the widget's QOpenGLWindow has been initialized, we continue // with the actual initialization connect(pWidget, &WInitialGLWidget::onInitialized, this, &MixxxMainWindow::initialize); pWidget->show(); return; } qDebug() << "QOpenGLContext::create() failed"; } qInfo() << "Initializing without OpenGL"; initialize();}#endif
void MixxxMainWindow::initialize() { m_pCoreServices->getControlIndicatorTimer()->setLegacyVsyncEnabled(true);
UserSettingsPointer pConfig = m_pCoreServices->getSettings();
// Set the visibility of tooltips, default "1" = ON m_toolTipsCfg = pConfig->getValue( ConfigKey("[Controls]", "Tooltips"), mixxx::preferences::Tooltips::On);#ifdef MIXXX_USE_QOPENGL ToolTipQOpenGL::singleton().setActive( m_toolTipsCfg == mixxx::preferences::Tooltips::On);#endif
#ifdef __ENGINEPRIME__ // Initialise library exporter // This has to be done before switching to fullscreen m_pLibraryExporter = m_pCoreServices->getLibrary()->makeLibraryExporter(this); connect(m_pCoreServices->getLibrary().get(), &Library::exportLibrary, m_pLibraryExporter.get(), &mixxx::LibraryExporter::slotRequestExport); connect(m_pCoreServices->getLibrary().get(), &Library::exportCrate, m_pLibraryExporter.get(), &mixxx::LibraryExporter::slotRequestExportWithInitialCrate); connect(m_pCoreServices->getLibrary().get(), &Library::exportPlaylist, m_pLibraryExporter.get(), &mixxx::LibraryExporter::slotRequestExportWithInitialPlaylist);#endif
// Turn on fullscreen mode // if we were told to start in fullscreen mode on the command-line // or if the user chose to always start in fullscreen mode. // The Fullscreen menu item is refreshed in connectMenuBar() bool fullscreenPref = m_pCoreServices->getSettings()->getValue<bool>( ConfigKey("[Config]", "StartInFullscreen")); if ((CmdlineArgs::Instance().getStartInFullscreen() || fullscreenPref) && // could be we're fullscreen already after setGeomtery(previousGeometry) !isFullScreen()) { showFullScreen(); }
initializationProgressUpdate(65, tr("skin"));
// Install an event filter to catch certain QT events, such as tooltips. // This allows us to turn off tooltips. installEventFilter(m_pCoreServices->getKeyboardEventFilter().get());
auto pPlayerManager = m_pCoreServices->getPlayerManager(); DEBUG_ASSERT(pPlayerManager); const QStringList visualGroups = pPlayerManager->getVisualPlayerGroups(); for (const QString& group : visualGroups) { m_pVisualsManager->addDeck(group); } connect(pPlayerManager.get(), &PlayerManagerInterface::numberOfDecksChanged, this, [this](int decks) { for (int i = 0; i < decks; ++i) { QString group = PlayerManager::groupForDeck(i); m_pVisualsManager->addDeckIfNotExist(group); } }); connect(pPlayerManager.get(), &PlayerManagerInterface::numberOfSamplersChanged, this, [this](int decks) { for (int i = 0; i < decks; ++i) { QString group = PlayerManager::groupForSampler(i); m_pVisualsManager->addDeckIfNotExist(group); } });
#ifndef MIXXX_USE_QOPENGL // Before creating the first skin we need to create a QGLWidget so that all // the QGLWidget's we create can use it as a shared QGLContext. if (!CmdlineArgs::Instance().getSafeMode() && QGLFormat::hasOpenGL()) { QGLFormat glFormat; glFormat.setDirectRendering(true); glFormat.setDoubleBuffer(true); glFormat.setDepth(false); // Disable waiting for vertical Sync // This can be enabled when using a single Threads for each QGLContext // Setting 1 causes QGLContext::swapBuffer to sleep until the next VSync#if defined(__APPLE__) // On OS X, syncing to vsync has good performance FPS-wise and // eliminates tearing. glFormat.setSwapInterval(1);#else // Otherwise, turn VSync off because it could cause horrible FPS on // Linux. // TODO(XXX): Make this configurable. // TODO(XXX): What should we do on Windows? glFormat.setSwapInterval(0);#endif glFormat.setRgba(true); QGLFormat::setDefaultFormat(glFormat);
WGLWidget* pContextWidget = new WGLWidget(this); pContextWidget->setGeometry(QRect(0, 0, 3, 3)); pContextWidget->hide(); SharedGLContext::setWidget(pContextWidget); }#endif
WaveformWidgetFactory::createInstance(); // takes a long time WaveformWidgetFactory::instance()->setConfig(m_pCoreServices->getSettings()); WaveformWidgetFactory::instance()->startVSync(m_pGuiTick, m_pVisualsManager, false);
connect(this, &MixxxMainWindow::skinLoaded, m_pCoreServices->getLibrary().get(), &Library::onSkinLoadFinished);
connect(this, &MixxxMainWindow::skinLoaded, WaveformWidgetFactory::instance(), &WaveformWidgetFactory::slotSkinLoaded);
// Initialize preference dialog m_pPrefDlg = new DlgPreferences( m_pCoreServices->getScreensaverManager(), m_pSkinLoader, m_pCoreServices->getSoundManager(), m_pCoreServices->getControllerManager(), m_pCoreServices->getVinylControlManager(), m_pCoreServices->getEffectsManager(), m_pCoreServices->getSettingsManager(), m_pCoreServices->getLibrary()); m_pPrefDlg->setWindowIcon(QIcon(MIXXX_ICON_PATH)); m_pPrefDlg->setHidden(true); connect(m_pPrefDlg, &DlgPreferences::tooltipModeChanged, this, &MixxxMainWindow::slotTooltipModeChanged); connect(m_pPrefDlg, &DlgPreferences::reloadUserInterface, this, &MixxxMainWindow::rebootMixxxView, Qt::DirectConnection);#ifndef __APPLE__ connect(m_pPrefDlg, &DlgPreferences::menuBarAutoHideChanged, this, &MixxxMainWindow::slotUpdateMenuBarAltKeyConnection, Qt::DirectConnection);#endif
// Connect signals to the menubar. Should be done before emit skinLoaded. connectMenuBar();
QWidget* oldWidget = m_pCentralWidget;
tryParseAndSetDefaultStyleSheet();
if (!loadConfiguredSkin()) { reportCriticalErrorAndQuit( "default skin cannot be loaded - see <b>mixxx</b> trace for more information"); m_pCentralWidget = oldWidget; //TODO (XXX) add dialog to warn user and launch skin choice page } else { m_pMenuBar->setStyleSheet(m_pCentralWidget->styleSheet()); }
// Check direct rendering and warn user if they don't have it if (!CmdlineArgs::Instance().getSafeMode()) { checkDirectRendering(); }
// Sound hardware setup // Try to open configured devices. If that fails, display dialogs // that allow to either retry, reconfigure devices or exit. bool retryClicked; do { retryClicked = false; SoundDeviceStatus result = m_pCoreServices->getSoundManager()->setupDevices(); if (result == SoundDeviceStatus::ErrorDeviceCount || result == SoundDeviceStatus::ErrorExcessiveOutputChannel) { if (soundDeviceBusyDlg(&retryClicked) != QDialog::Accepted) { exit(0); } } else if (result != SoundDeviceStatus::Ok) { if (soundDeviceErrorMsgDlg(result, &retryClicked) != QDialog::Accepted) { exit(0); } } } while (retryClicked);
// Test for at least one output device. If none, display another dialog // that says "mixxx will barely work with no outs". // In case of persisting errors, the user has already received a message // above. So we can just check the output count here. while (m_pCoreServices->getSoundManager() ->getConfig() .getOutputs() .isEmpty() && !m_pCoreServices->getSoundManager()->pipewireSkipConfig()) { // Exit when we press the Exit button in the noSoundDlg dialog // only call it if result != OK bool continueClicked = false; if (noOutputDlg(&continueClicked) != QDialog::Accepted) { exit(0); } if (continueClicked) { break; } }
// The user has either reconfigured devices or accepted no outputs, // so it's now safe to write the new config to disk. m_pCoreServices->getSoundManager()->getConfig().writeToDisk();
// this has to be after the OpenGL widgets are created or depending on a // million different variables the first waveform may be horribly // corrupted. See bug 521509 -- bkgood ?? -- vrince setCentralWidget(m_pCentralWidget);
#ifndef __APPLE__ // Ask for permission to auto-hide the menu bar if applicable.#ifdef __LINUX__ // This makes no sense when starting in windowed mode with a global menu, // we'll ask when going fullscreen. if (!m_supportsGlobalMenuBar || isFullScreen()) { alwaysHideMenuBarDlg(); slotUpdateMenuBarAltKeyConnection(); }#else alwaysHideMenuBarDlg(); slotUpdateMenuBarAltKeyConnection();#endif#endif
// Show the menubar after the launch image is replaced by the skin widget, // otherwise it would shift the launch image shortly before the skin is visible. m_pMenuBar->show();
// The launch image widget is automatically disposed, but we still have a // pointer to it. m_pLaunchImage = nullptr;
connect(pPlayerManager.get(), &PlayerManager::noMicrophoneInputConfigured, this, &MixxxMainWindow::slotNoMicrophoneInputConfigured); connect(pPlayerManager.get(), &PlayerManager::noAuxiliaryInputConfigured, this, &MixxxMainWindow::slotNoAuxiliaryInputConfigured); connect(pPlayerManager.get(), &PlayerManager::noDeckPassthroughInputConfigured, this, &MixxxMainWindow::slotNoDeckPassthroughInputConfigured); connect(pPlayerManager.get(), &PlayerManager::noVinylControlInputConfigured, this, &MixxxMainWindow::slotNoVinylControlInputConfigured);
connect(&PlayerInfo::instance(), &PlayerInfo::currentPlayingTrackChanged, this, &MixxxMainWindow::slotUpdateWindowTitle);
// Start Auto DJ if the cmdline arg is passed. if (CmdlineArgs::Instance().getStartAutoDJ()) { qDebug("Enabling Auto DJ from CLI flag."); ControlObject::set(ConfigKey("[AutoDJ]", "enabled"), 1.0); // Switch to Auto DJ feature auto* pLibrary = m_pCoreServices->getLibrary().get(); // Note: auto-scroll is disabled but that doesn't really matter here // because the sidebar is still in its initial state (top feature visible, // AutoDj is second from the top by default, all features collapsed). pLibrary->showAutoDJ(); }}
MixxxMainWindow::~MixxxMainWindow() { Timer t("~MixxxMainWindow"); t.start();
// Save the current window state (position, maximized, etc) // Note(ronso0): Unfortunately saveGeometry() also stores the fullscreen state. // On next start restoreGeometry would enable fullscreen mode even though that // might not be requested (no '--fullscreen' command line arg and // [Config],StartInFullscreen is '0'. // https://github.com/mixxxdj/mixxx/issues/10005 // So let's quit fullscreen if StartInFullscreen is not checked in Preferences. bool fullscreenPref = m_pCoreServices->getSettings()->getValue<bool>( ConfigKey("[Config]", "StartInFullscreen")); if (isFullScreen() && !fullscreenPref) { // Simply maximize the window so we can store a geometry that fits the screen. // Don't call slotViewFullScreen(false) (calls showNormal()) because that // can make the main window incl. window decoration too large for the screen.#ifndef __APPLE__ // Before, store the expected window state so eventFilter() will ignore // the following QWindowChangeEvent and not recreate & re-sync the menu bar. m_prevState = Qt::WindowMaximized;#endif showMaximized(); } m_pCoreServices->getSettings()->set(ConfigKey("[MainWindow]", "geometry"), QString(saveGeometry().toBase64())); m_pCoreServices->getSettings()->set(ConfigKey("[MainWindow]", "state"), QString(saveState().toBase64()));
// GUI depends on KeyboardEventFilter, PlayerManager, Library qDebug() << t.elapsed(false).debugMillisWithUnit() << "deleting skin"; // Clear widget pointer list and destroy all update connections before we // delete the main widget (ie. all WBaseWidgets) to prevent KeyboardEventFilter // accessing dangling pointers. m_pCoreServices->getKeyboardEventFilter()->clearWidgets(); m_pCentralWidget = nullptr; QPointer<QWidget> pSkin(centralWidget()); setCentralWidget(nullptr); if (!pSkin.isNull()) { QCoreApplication::sendPostedEvents(pSkin, QEvent::DeferredDelete); } // Our central widget is now deleted. VERIFY_OR_DEBUG_ASSERT(pSkin.isNull()) { qWarning() << "Central widget was not deleted by our sendPostedEvents trick."; }
// Delete Controls created by skins qDeleteAll(m_skinCreatedControls); m_skinCreatedControls.clear();
// TODO() Verify if this comment still applies: // WMainMenuBar holds references to controls so we need to delete it // before MixxxMainWindow is destroyed. QMainWindow calls deleteLater() in // setMenuBar() but we need to delete it now so we can ask for // DeferredDelete events to be processed for it. Once Mixxx shutdown lives // outside of MixxxMainWindow the parent relationship will directly destroy // the WMainMenuBar and this will no longer be a problem. qDebug() << t.elapsed(false).debugMillisWithUnit() << "deleting menubar"; // Clear action pointer list before we delete the menubar // to prevent KeyboardEventFilter accessing dangling pointers. m_pCoreServices->getKeyboardEventFilter()->clearMenuBarActions(); QPointer<WMainMenuBar> pMenuBar = m_pMenuBar.toWeakRef(); DEBUG_ASSERT(menuBar() == m_pMenuBar.get()); // We need to reset the parented pointer here that it does not become a // dangling pointer after the object has been deleted. m_pMenuBar = nullptr; setMenuBar(nullptr); if (!pMenuBar.isNull()) { QCoreApplication::sendPostedEvents(pMenuBar, QEvent::DeferredDelete); } // Our main menu is now deleted. VERIFY_OR_DEBUG_ASSERT(pMenuBar.isNull()) { qWarning() << "WMainMenuBar was not deleted by our sendPostedEvents trick."; }
qDebug() << t.elapsed(false).debugMillisWithUnit() << "deleting DeveloperToolsDlg"; delete m_pDeveloperToolsDlg;
#ifdef __ENGINEPRIME__ qDebug() << t.elapsed(false).debugMillisWithUnit() << "deleting LibraryExporter"; m_pLibraryExporter.reset();#endif
qDebug() << t.elapsed(false).debugMillisWithUnit() << "deleting DlgPreferences"; delete m_pPrefDlg;
m_pCoreServices->getControlIndicatorTimer()->setLegacyVsyncEnabled(false);
qDebug() << t.elapsed(false).debugMillisWithUnit() << "deleting ControllerManager";
WaveformWidgetFactory::destroy();
delete m_pGuiTick; delete m_pVisualsManager;}
void MixxxMainWindow::initializeWindow() { // be sure createMenuBar() is called first DEBUG_ASSERT(m_pMenuBar);
QPalette Pal(palette()); // safe default QMenuBar background QColor MenuBarBackground(m_pMenuBar->palette().color(QPalette::Window)); Pal.setColor(QPalette::Window, QColor(0x202020)); setAutoFillBackground(true); setPalette(Pal); // restore default QMenuBar background Pal.setColor(QPalette::Window, MenuBarBackground); m_pMenuBar->setPalette(Pal);
// Restore the current window state (position, maximized, etc). // This will also restore fullscreen and thereby create a seamless // start if we did shut down while in fullscreen mode and with // [Config],StartInFullscreen = 1 // (slotViewFullScreen(true) in initialize() is a no-op then) restoreGeometry(QByteArray::fromBase64( m_pCoreServices->getSettings() ->getValueString(ConfigKey("[MainWindow]", "geometry")) .toUtf8())); restoreState(QByteArray::fromBase64( m_pCoreServices->getSettings() ->getValueString(ConfigKey("[MainWindow]", "state")) .toUtf8()));
setWindowIcon(QIcon(MIXXX_ICON_PATH)); slotUpdateWindowTitle(TrackPointer());}
#ifndef __APPLE__void MixxxMainWindow::alwaysHideMenuBarDlg() { // Don't show the dialog if the user unchecked "Ask me again" if (!m_pCoreServices->getSettings()->getValue<bool>( kMenuBarHintConfigKey, true)) { return; } QString title = tr("Allow Mixxx to hide the menu bar?"); //: Always show the menu bar? QString hideBtnLabel = tr("Hide"); QString showBtnLabel = tr("Always show"); //: Keep formatting tags <b> (bold text) and <br> (linebreak). //: %1 is the placeholder for the 'Always show' button label QString desc = tr( "The Mixxx menu bar is hidden and can be toggled with a single press " "of the <b>Alt</b> key.<br><br>" "Click <b>%1</b> to agree.<br><br>" "Click <b>%2</b> to disable that, for example if you don't use Mixxx " "with a keyboard.<br><br>" "You can change this setting any time in Preferences -> Interface." "<br>") // line break for some extra margin to the checkbox .arg(hideBtnLabel, showBtnLabel);
QMessageBox msg; msg.setIcon(QMessageBox::Question); msg.setWindowTitle(title); msg.setText(desc); QCheckBox askAgainCheckBox; askAgainCheckBox.setText(tr("Ask me again")); askAgainCheckBox.setCheckState(Qt::Checked); msg.setCheckBox(&askAgainCheckBox); QPushButton* pHideBtn = msg.addButton(hideBtnLabel, QMessageBox::AcceptRole); QPushButton* pShowBtn = msg.addButton(showBtnLabel, QMessageBox::RejectRole); msg.setDefaultButton(pShowBtn); msg.exec();
m_pCoreServices->getSettings()->setValue( kMenuBarHintConfigKey, askAgainCheckBox.checkState() == Qt::Checked ? 1 : 0);
m_pCoreServices->getSettings()->setValue( kHideMenuBarConfigKey, msg.clickedButton() == pHideBtn ? 1 : 0);}#endif
QDialog::DialogCode MixxxMainWindow::soundDeviceErrorDlg( const QString &title, const QString &text, bool* retryClicked) { QMessageBox msgBox; msgBox.setIcon(QMessageBox::Warning); msgBox.setWindowTitle(title); msgBox.setText(text);
QPushButton* retryButton = msgBox.addButton(tr("Retry"), QMessageBox::ActionRole); QPushButton* reconfigureButton = msgBox.addButton(tr("Reconfigure"), QMessageBox::ActionRole); QPushButton* wikiButton = msgBox.addButton(tr("Help"), QMessageBox::ActionRole); QPushButton* exitButton = msgBox.addButton(tr("Exit"), QMessageBox::ActionRole);
while (true) { msgBox.exec();
if (msgBox.clickedButton() == retryButton) { m_pCoreServices->getSoundManager()->clearAndQueryDevices(); *retryClicked = true; return QDialog::Accepted; } else if (msgBox.clickedButton() == wikiButton) { mixxx::DesktopHelper::openUrl(QUrl(MIXXX_WIKI_TROUBLESHOOTING_SOUND_URL)); wikiButton->setEnabled(false); } else if (msgBox.clickedButton() == reconfigureButton) { msgBox.hide();
m_pCoreServices->getSoundManager()->clearAndQueryDevices(); // This way of opening the dialog allows us to use it synchronously m_pPrefDlg->setWindowModality(Qt::ApplicationModal); // Open preferences, sound hardware page is selected (default on first call) m_pPrefDlg->exec(); if (m_pPrefDlg->result() == QDialog::Accepted) { return QDialog::Accepted; }
msgBox.show(); } else if (msgBox.clickedButton() == exitButton) { // Will finally quit Mixxx return QDialog::Rejected; } }}
QDialog::DialogCode MixxxMainWindow::soundDeviceBusyDlg(bool* retryClicked) { QString title(tr("Sound Device Busy")); QString text( "<html> <p>" % tr("Mixxx was unable to open all the configured sound devices.") + "</p> <p>" % m_pCoreServices->getSoundManager()->getErrorDeviceName() % " is used by another application or not plugged in." "</p><ul>" "<li>" % tr("<b>Retry</b> after closing the other application " "or reconnecting a sound device") % "</li>" "<li>" % tr("<b>Reconfigure</b> Mixxx's sound device settings.") % "</li>" "<li>" % tr("Get <b>Help</b> from the Mixxx Wiki.") % "</li>" "<li>" % tr("<b>Exit</b> Mixxx.") % "</li>" "</ul></html>"); return soundDeviceErrorDlg(title, text, retryClicked);}
QDialog::DialogCode MixxxMainWindow::soundDeviceErrorMsgDlg( SoundDeviceStatus status, bool* retryClicked) { QString title(tr("Sound Device Error")); QString text("<html> <p>" % tr("Mixxx was unable to open all the configured sound " "devices.") + "</p> <p>" % m_pCoreServices->getSoundManager() ->getLastErrorMessage(status) .replace("\n", "<br/>") % "</p><ul>" "<li>" % tr("<b>Retry</b> after fixing an issue") % "</li>" "<li>" % tr("<b>Reconfigure</b> Mixxx's sound device settings.") % "</li>" "<li>" % tr("Get <b>Help</b> from the Mixxx Wiki.") % "</li>" "<li>" % tr("<b>Exit</b> Mixxx.") % "</li>" "</ul></html>"); return soundDeviceErrorDlg(title, text, retryClicked);}
QDialog::DialogCode MixxxMainWindow::noOutputDlg(bool* continueClicked) { QMessageBox msgBox; msgBox.setIcon(QMessageBox::Warning); msgBox.setWindowTitle(tr("No Output Devices")); msgBox.setText( "<html>" + tr("Mixxx was configured without any output sound devices. " "Audio processing will be disabled without a configured output device.") + "<ul>" "<li>" + tr("<b>Continue</b> without any outputs.") + "</li>" "<li>" + tr("<b>Reconfigure</b> Mixxx's sound device settings.") + "</li>" "<li>" + tr("<b>Exit</b> Mixxx.") + "</li>" "</ul></html>" );
QPushButton* continueButton = msgBox.addButton(tr("Continue"), QMessageBox::ActionRole); QPushButton* reconfigureButton = msgBox.addButton(tr("Reconfigure"), QMessageBox::ActionRole); QPushButton* exitButton = msgBox.addButton(tr("Exit"), QMessageBox::ActionRole);
while (true) { msgBox.exec();
if (msgBox.clickedButton() == continueButton) { *continueClicked = true; return QDialog::Accepted; } else if (msgBox.clickedButton() == reconfigureButton) { msgBox.hide();
// This way of opening the dialog allows us to use it synchronously m_pPrefDlg->setWindowModality(Qt::ApplicationModal); m_pPrefDlg->showSoundHardwarePage(mixxx::preferences::SoundHardwareTab::Output); m_pPrefDlg->exec(); if (m_pPrefDlg->result() == QDialog::Accepted) { return QDialog::Accepted; }
msgBox.show();
} else if (msgBox.clickedButton() == exitButton) { // Will finally quit Mixxx return QDialog::Rejected; } }}
void MixxxMainWindow::slotUpdateWindowTitle(TrackPointer pTrack) { QString appTitle = VersionStore::applicationName(); QString filePath;
// If we have a track, use getInfo() to format a summary string and prepend // it to the title. // TODO(rryan): Does this violate Mac App Store policies? if (pTrack) { QString trackInfo = pTrack->getInfo(); if (!trackInfo.isEmpty()) { appTitle = QString("%1 | %2").arg(trackInfo, appTitle); } filePath = pTrack->getLocation(); } setWindowTitle(appTitle);
// Display a draggable proxy icon for the track in the title bar on // platforms that support it, e.g. macOS setWindowFilePath(filePath);}
void MixxxMainWindow::createMenuBar() { ScopedTimer t(QStringLiteral("MixxxMainWindow::createMenuBar")); DEBUG_ASSERT(m_pCoreServices->getKeyboardEventFilter()); m_pMenuBar = make_parented<WMainMenuBar>( this, m_pCoreServices->getSettings(), m_pCoreServices->getKeyboardEventFilter()); if (m_pCentralWidget) { m_pMenuBar->setStyleSheet(m_pCentralWidget->styleSheet()); } setMenuBar(m_pMenuBar);}
void MixxxMainWindow::connectMenuBar() { // This function might be invoked multiple times on startup // so all connections must be unique!
ScopedTimer t(QStringLiteral("MixxxMainWindow::connectMenuBar")); connect(this, &MixxxMainWindow::skinLoaded, m_pMenuBar, &WMainMenuBar::onNewSkinLoaded, Qt::UniqueConnection);
// Misc connect(m_pMenuBar, &WMainMenuBar::quit, this, &MixxxMainWindow::close, Qt::UniqueConnection); connect(m_pMenuBar, &WMainMenuBar::showPreferences, this, &MixxxMainWindow::slotOptionsPreferences, Qt::UniqueConnection); connect(m_pMenuBar, &WMainMenuBar::loadTrackToDeck, this, &MixxxMainWindow::slotFileLoadSongPlayer, Qt::UniqueConnection);
connect(m_pMenuBar, &WMainMenuBar::showKeywheel, this, &MixxxMainWindow::slotShowKeywheel, Qt::UniqueConnection);#ifndef __APPLE__ // Menubar auto-hide connect(m_pMenuBar, &WMainMenuBar::menubarAutoHideChanged, this, &MixxxMainWindow::slotUpdateMenuBarAltKeyConnection, Qt::UniqueConnection);#endif
// Fullscreen connect(m_pMenuBar, &WMainMenuBar::toggleFullScreen, this, &MixxxMainWindow::slotViewFullScreen, Qt::UniqueConnection); connect(this, &MixxxMainWindow::fullScreenChanged, m_pMenuBar, &WMainMenuBar::onFullScreenStateChange, Qt::UniqueConnection); // Refresh the Fullscreen checkbox for the case we went fullscreen earlier m_pMenuBar->onFullScreenStateChange(isFullScreen());
// Help connect(m_pMenuBar, &WMainMenuBar::showAbout, this, &MixxxMainWindow::slotHelpAbout, Qt::UniqueConnection);
// Developer connect(m_pMenuBar, &WMainMenuBar::reloadSkin, this, &MixxxMainWindow::rebootMixxxView, Qt::UniqueConnection); connect(m_pMenuBar, &WMainMenuBar::toggleDeveloperTools, this, &MixxxMainWindow::slotDeveloperTools, Qt::UniqueConnection);
if (m_pCoreServices->getRecordingManager()) { connect(m_pCoreServices->getRecordingManager().get(), &RecordingManager::isRecording, m_pMenuBar, &WMainMenuBar::onRecordingStateChange, Qt::UniqueConnection); connect(m_pMenuBar, &WMainMenuBar::toggleRecording, m_pCoreServices->getRecordingManager().get(), &RecordingManager::slotSetRecording, Qt::UniqueConnection); m_pMenuBar->onRecordingStateChange( m_pCoreServices->getRecordingManager()->isRecordingActive()); }
#ifdef __BROADCAST__ if (m_pCoreServices->getBroadcastManager()) { connect(m_pCoreServices->getBroadcastManager().get(), &BroadcastManager::broadcastEnabled, m_pMenuBar, &WMainMenuBar::onBroadcastingStateChange, Qt::UniqueConnection); connect(m_pMenuBar, &WMainMenuBar::toggleBroadcasting, m_pCoreServices->getBroadcastManager().get(), &BroadcastManager::setEnabled, Qt::UniqueConnection); m_pMenuBar->onBroadcastingStateChange(m_pCoreServices->getBroadcastManager()->isEnabled()); }#endif
#ifdef __VINYLCONTROL__ if (m_pCoreServices->getVinylControlManager()) { connect(m_pMenuBar, &WMainMenuBar::toggleVinylControl, m_pCoreServices->getVinylControlManager().get(), &VinylControlManager::toggleVinylControl, Qt::UniqueConnection); connect(m_pCoreServices->getVinylControlManager().get(), &VinylControlManager::vinylControlDeckEnabled, m_pMenuBar, &WMainMenuBar::onVinylControlDeckEnabledStateChange, Qt::UniqueConnection); }#endif
auto pPlayerManager = m_pCoreServices->getPlayerManager(); if (pPlayerManager) { connect(pPlayerManager.get(), &PlayerManager::numberOfDecksChanged, m_pMenuBar, &WMainMenuBar::onNumberOfDecksChanged, Qt::UniqueConnection); m_pMenuBar->onNumberOfDecksChanged(pPlayerManager->numberOfDecks()); }
if (m_pCoreServices->getTrackCollectionManager()) { connect(m_pMenuBar, &WMainMenuBar::rescanLibrary, m_pCoreServices->getTrackCollectionManager().get(), &TrackCollectionManager::startLibraryScan, Qt::UniqueConnection); connect(m_pCoreServices->getTrackCollectionManager().get(), &TrackCollectionManager::libraryScanStarted, m_pMenuBar, &WMainMenuBar::onLibraryScanStarted, Qt::UniqueConnection); connect(m_pCoreServices->getTrackCollectionManager().get(), &TrackCollectionManager::libraryScanFinished, m_pMenuBar, &WMainMenuBar::onLibraryScanFinished, Qt::UniqueConnection); }
if (m_pCoreServices->getLibrary()) { connect(m_pMenuBar, &WMainMenuBar::searchInCurrentView, m_pCoreServices->getLibrary().get(), &Library::slotSearchInCurrentView, Qt::UniqueConnection); connect(m_pMenuBar, &WMainMenuBar::searchInAllTracks, m_pCoreServices->getLibrary().get(), &Library::slotSearchInAllTracks, Qt::UniqueConnection); connect(m_pMenuBar, &WMainMenuBar::createCrate, m_pCoreServices->getLibrary().get(), &Library::slotCreateCrate, Qt::UniqueConnection); connect(m_pMenuBar, &WMainMenuBar::createPlaylist, m_pCoreServices->getLibrary().get(), &Library::slotCreatePlaylist, Qt::UniqueConnection); connect(m_pMenuBar, &WMainMenuBar::showAutoDJ, m_pCoreServices->getLibrary().get(), &Library::showAutoDJ, Qt::UniqueConnection); }
#ifdef __ENGINEPRIME__ DEBUG_ASSERT(m_pLibraryExporter); connect(m_pMenuBar, &WMainMenuBar::exportLibrary, m_pLibraryExporter.get(), &mixxx::LibraryExporter::slotRequestExport, Qt::UniqueConnection);#endif}
/// Enable/disable listening to Alt key press for toggling the menubar.#ifndef __APPLE__void MixxxMainWindow::slotUpdateMenuBarAltKeyConnection() { if (!m_pCoreServices->getKeyboardEventFilter() || !m_pMenuBar) { return; }
if (m_pCoreServices->getSettings()->getValue<bool>(kHideMenuBarConfigKey, false)) { // with Qt::UniqueConnection we don't need to check whether we're already connected connect(m_pCoreServices->getKeyboardEventFilter().get(), &KeyboardEventFilter::altPressedWithoutKeys, m_pMenuBar, &WMainMenuBar::slotToggleMenuBar, Qt::UniqueConnection); m_pMenuBar->hideMenuBar(); } else { disconnect(m_pCoreServices->getKeyboardEventFilter().get(), &KeyboardEventFilter::altPressedWithoutKeys, m_pMenuBar, &WMainMenuBar::slotToggleMenuBar); m_pMenuBar->showMenuBar(); }}#endif
void MixxxMainWindow::slotFileLoadSongPlayer(int deck) { QString group = PlayerManager::groupForDeck(deck - 1);
QString loadTrackText = tr("Load track to Deck %1").arg(QString::number(deck)); QString deckWarningMessage = tr("Deck %1 is currently playing a track.") .arg(QString::number(deck)); QString areYouSure = tr("Are you sure you want to load a new track?");
if (ControlObject::get(ConfigKey(group, "play")) > 0.0) { int ret = QMessageBox::warning(this, VersionStore::applicationName(), deckWarningMessage + "\n" + areYouSure, QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if (ret != QMessageBox::Yes) { return; } }
UserSettingsPointer pConfig = m_pCoreServices->getSettings(); QString trackPath = QFileDialog::getOpenFileName( this, loadTrackText, pConfig->getValueString(mixxx::library::prefs::kLegacyDirectoryConfigKey), QString("Audio (%1)") .arg(SoundSourceProxy::getSupportedFileNamePatterns().join(" ")));
if (!trackPath.isNull()) { // The user has picked a file via a file dialog. This means the system // sandboxer (if we are sandboxed) has granted us permission to this // folder. Create a security bookmark while we have permission so that // we can access the folder on future runs. We need to canonicalize the // path so we first wrap the directory string with a QDir. mixxx::FileInfo fileInfo(trackPath); Sandbox::createSecurityToken(&fileInfo);
m_pCoreServices->getPlayerManager()->slotLoadToDeck(trackPath, deck); }}
void MixxxMainWindow::slotDeveloperTools(bool visible) { if (visible) { if (m_pDeveloperToolsDlg == nullptr) { UserSettingsPointer pConfig = m_pCoreServices->getSettings(); m_pDeveloperToolsDlg = new DlgDeveloperTools(this, pConfig); connect(m_pDeveloperToolsDlg, &DlgDeveloperTools::destroyed, this, &MixxxMainWindow::slotDeveloperToolsClosed); connect(this, &MixxxMainWindow::closeDeveloperToolsDlgChecked, m_pDeveloperToolsDlg, &DlgDeveloperTools::done); connect(m_pDeveloperToolsDlg, &DlgDeveloperTools::destroyed, m_pMenuBar, &WMainMenuBar::onDeveloperToolsHidden); } m_pMenuBar->onDeveloperToolsShown(); m_pDeveloperToolsDlg->show(); m_pDeveloperToolsDlg->activateWindow(); } else { emit closeDeveloperToolsDlgChecked(0); }}
void MixxxMainWindow::slotDeveloperToolsClosed() { m_pDeveloperToolsDlg = nullptr;}
void MixxxMainWindow::slotViewFullScreen(bool toggle) { if (isFullScreen() == toggle) { return; }
// Just switch the window state here. eventFilter() will catch the // QWindowStateChangeEvent and inform the menu bar that fullscreen changed. if (toggle) { showFullScreen(); } else { showNormal(); }}
void MixxxMainWindow::slotOptionsPreferences() { m_pPrefDlg->show(); m_pPrefDlg->raise(); m_pPrefDlg->activateWindow();}
void MixxxMainWindow::slotNoVinylControlInputConfigured() { if (m_noVinylInputDialog && m_noVinylInputDialog->isVisible()) { // Don't show redundant dialogs. // They might be triggered be repeated controller or keyboard and // can lockup the GUI. return; }
if (!m_noVinylInputDialog) { m_noVinylInputDialog = make_parented<QMessageBox>( QMessageBox::Warning, VersionStore::applicationName(), tr("There is no input device selected for this vinyl control.\n" "Please select an input device in the sound hardware preferences first."), QMessageBox::Ok | QMessageBox::Cancel, this); m_noVinylInputDialog->setWindowModality(Qt::ApplicationModal); m_noVinylInputDialog->setDefaultButton(QMessageBox::Cancel); } m_noVinylInputDialog->exec(); if (m_noVinylInputDialog->clickedButton() == m_noVinylInputDialog->button(QMessageBox::Ok)) { m_pPrefDlg->show(); m_pPrefDlg->showSoundHardwarePage(mixxx::preferences::SoundHardwareTab::Input); }}
void MixxxMainWindow::slotNoDeckPassthroughInputConfigured() { if (m_noPassthroughInputDialog && m_noPassthroughInputDialog->isVisible()) { // Don't show redundant dialogs. // They might be triggered be repeated controller or keyboard and // can lockup the GUI. return; }
if (!m_noPassthroughInputDialog) { m_noPassthroughInputDialog = make_parented<QMessageBox>( QMessageBox::Warning, VersionStore::applicationName(), tr("There is no input device selected for this passthrough control.\n" "Please select an input device in the sound hardware preferences first."), QMessageBox::Ok | QMessageBox::Cancel, this); m_noPassthroughInputDialog->setWindowModality(Qt::ApplicationModal); m_noPassthroughInputDialog->setDefaultButton(QMessageBox::Cancel); } m_noPassthroughInputDialog->exec(); if (m_noPassthroughInputDialog->clickedButton() == m_noPassthroughInputDialog->button(QMessageBox::Ok)) { m_pPrefDlg->show(); m_pPrefDlg->showSoundHardwarePage(mixxx::preferences::SoundHardwareTab::Input); }}
void MixxxMainWindow::slotNoMicrophoneInputConfigured() { if (m_noMicInputDialog && m_noMicInputDialog->isVisible()) { // Don't show redundant dialogs. // They might be triggered be repeated controller or keyboard and // can lockup the GUI. return; }
if (!m_noMicInputDialog) { m_noMicInputDialog = make_parented<QMessageBox>( QMessageBox::Warning, VersionStore::applicationName(), tr("There is no input device selected for this microphone.\n" "Do you want to select an input device?"), QMessageBox::Ok | QMessageBox::Cancel, this); m_noMicInputDialog->setWindowModality(Qt::ApplicationModal); m_noMicInputDialog->setDefaultButton(QMessageBox::Cancel); } m_noMicInputDialog->exec(); if (m_noMicInputDialog->clickedButton() == m_noMicInputDialog->button(QMessageBox::Ok)) { m_pPrefDlg->show(); m_pPrefDlg->showSoundHardwarePage(mixxx::preferences::SoundHardwareTab::Input); }}
void MixxxMainWindow::slotNoAuxiliaryInputConfigured() { if (m_noAuxInputDialog && m_noAuxInputDialog->isVisible()) { // Don't show redundant dialogs. // They might be triggered be repeated controller or keyboard and // can lockup the GUI. return; }
if (!m_noAuxInputDialog) { m_noAuxInputDialog = make_parented<QMessageBox>( QMessageBox::Warning, VersionStore::applicationName(), tr("There is no input device selected for this auxiliary.\n" "Do you want to select an input device?"), QMessageBox::Ok | QMessageBox::Cancel, this); m_noAuxInputDialog->setWindowModality(Qt::ApplicationModal); m_noAuxInputDialog->setDefaultButton(QMessageBox::Cancel); } m_noAuxInputDialog->exec(); if (m_noAuxInputDialog->clickedButton() == m_noAuxInputDialog->button(QMessageBox::Ok)) { m_pPrefDlg->show(); m_pPrefDlg->showSoundHardwarePage(mixxx::preferences::SoundHardwareTab::Input); }}
void MixxxMainWindow::slotHelpAbout() { DlgAbout* about = new DlgAbout; about->show();}
void MixxxMainWindow::slotLibraryScanSummaryDlg(const LibraryScanResultSummary& result) { if (!m_pCoreServices->getSettings()->getValue<bool>( mixxx::library::prefs::kShowScanSummaryConfigKey, true)) { return; }
// Don't show the report dialog when the scan is run during startup and no // noteworthy changes have been detected. if (result.autoscan && result.numNewTracks == 0 && result.numNewMissingTracks == 0 && result.numRediscoveredTracks == 0) { return; }
QMessageBox* pMsg = new QMessageBox(); pMsg->setAttribute(Qt::WA_DeleteOnClose); pMsg->setTextFormat(Qt::RichText); // required to get bold text with <b> tags pMsg->setWindowTitle(tr("Library scan finished"));
if (result.noDirectoriesConfigured) { pMsg->setText(tr("No music directories configured for scanning.") + QStringLiteral("<br>") + tr("Add directories in the library preferences.")); pMsg->show(); return; }
QString summary = tr("Scan took %1").arg(result.durationString) + QStringLiteral("<br><br>"); if (result.numNewTracks == 0 && result.numMovedTracks == 0 && result.numNewMissingTracks == 0 && result.numRediscoveredTracks == 0) { summary += tr("No changes detected.") + QStringLiteral("<br><b>") + tr("%n track(s) in total", nullptr, result.tracksTotal) + QStringLiteral("</b>"); } else { if (result.numNewTracks != 0) { summary += tr("%n new track(s) found", nullptr, result.numNewTracks) + QStringLiteral("<br>"); } if (result.numMovedTracks != 0) { summary += tr("%n moved track(s) detected", nullptr, result.numMovedTracks) + QStringLiteral("<br>"); } if (result.numNewMissingTracks != 0) { summary += tr("%n track(s) missing (%1 total)", nullptr, result.numNewMissingTracks) .arg(result.numMissingTracks); } if (result.numRediscoveredTracks != 0) { summary += QStringLiteral("<br>") + tr("%n track(s) rediscovered", nullptr, result.numRediscoveredTracks); } summary += QStringLiteral("<br><br><b>") + tr("%n track(s) in total", nullptr, result.tracksTotal) + QStringLiteral("</b>"); }
pMsg->setText(summary); pMsg->show();}
void MixxxMainWindow::slotShowKeywheel(bool toggle) { if (!m_pKeywheel) { m_pKeywheel = make_parented<DlgKeywheel>(this, m_pCoreServices->getSettings()); // uncheck the menu item on window close connect(m_pKeywheel.get(), &DlgKeywheel::finished, m_pMenuBar, &WMainMenuBar::onKeywheelChange); } if (toggle) { m_pKeywheel->show(); m_pKeywheel->raise(); } else { m_pKeywheel->hide(); }}
void MixxxMainWindow::slotTooltipModeChanged(mixxx::preferences::Tooltips tt) { m_toolTipsCfg = tt; m_pCoreServices->getKeyboardEventFilter()->setShowOnlyKbdShortcuts( tt == mixxx::preferences::Tooltips::OnlyKbdShortcuts);#ifdef MIXXX_USE_QOPENGL ToolTipQOpenGL::singleton().setActive( m_toolTipsCfg == mixxx::preferences::Tooltips::On);#endif}
void MixxxMainWindow::rebootMixxxView() { qDebug() << "Now in rebootMixxxView..."; m_inRebootMixxxView = true;
ScopedWaitCursor cursor; // safe geometry for later restoration const QRect initGeometry = geometry();
// We need to tell the menu bar that we are about to delete the old skin and // create a new one. It holds "visibility" controls (e.g. "Show Samplers") // that need to be deleted -- otherwise we can't tell what features the skin // supports since the controls from the previous skin will be left over. m_pMenuBar->onNewSkinAboutToLoad();
if (m_pCentralWidget) { // Clear widget pointer list and destroy all update connections before // we delete the main widget (ie. all WBaseWidgets) to prevent // KeyboardEventFilter accessing dangling pointers, just in case a // shortcuts/tooltip update is triggered while we re/load a skin. m_pCoreServices->getKeyboardEventFilter()->clearWidgets(); m_pCentralWidget->hide(); WaveformWidgetFactory::instance()->destroyWidgets(); delete m_pCentralWidget; m_pCentralWidget = nullptr; }
// Workaround for changing skins while fullscreen, just go out of fullscreen // mode. If you change skins while in fullscreen (on Linux, at least) the // window returns to 0,0 but and the backdrop disappears so it looks as if // it is not fullscreen, but acts as if it is. bool wasFullScreen = isFullScreen(); if (wasFullScreen) { showMaximized(); }
tryParseAndSetDefaultStyleSheet();
if (!loadConfiguredSkin()) { QMessageBox::critical(this, tr("Error in skin file"), tr("The selected skin cannot be loaded.")); m_inRebootMixxxView = false; // m_pWidgetParent is NULL, we can't continue. return; } m_pMenuBar->setStyleSheet(m_pCentralWidget->styleSheet());
setCentralWidget(m_pCentralWidget);#ifdef __LINUX__ // don't adjustSize() on Linux as this wouldn't use the entire available area // to paint the new skin with X11 // https://github.com/mixxxdj/mixxx/issues/9309#else adjustSize();#endif
if (wasFullScreen) { showFullScreen(); } else { // Programmatic placement at this point is very problematic. // The screen() method returns stale data (primary screen) // until the user interacts with mixxx again. Keyboard shortcuts // do not count, moving window, opening menu etc does // Therefore the placement logic was removed by a simple geometry restore. // If the minimum size of the new skin is larger then the restored // geometry, the window will be enlarged right & bottom which is // safe as the menu is still reachable. setGeometry(initGeometry); }
m_inRebootMixxxView = false; qDebug() << "rebootMixxxView DONE";}
bool MixxxMainWindow::loadConfiguredSkin() { // TODO: use std::shared_ptr throughout skin widgets instead of these hacky get() calls m_pCentralWidget = m_pSkinLoader->loadConfiguredSkin(this, &m_skinCreatedControls, m_pCoreServices.get()); if (centralWidget() == m_pLaunchImage) { initializationProgressUpdate(100, ""); } emit skinLoaded(); return m_pCentralWidget != nullptr;}
/// Try to load default styles that can be overridden by skinsvoid MixxxMainWindow::tryParseAndSetDefaultStyleSheet() { const QString resPath = m_pCoreServices->getSettings()->getResourcePath(); QFile file(resPath + "/skins/default.qss"); if (file.open(QIODevice::ReadOnly)) { QByteArray fileBytes = file.readAll(); QString style = QString::fromUtf8(fileBytes); setStyleSheet(style); } else { qWarning() << "Failed to load default skin styles /skins/default.qss!"; }}
/// Catch ToolTip and WindowStateChange eventsbool MixxxMainWindow::eventFilter(QObject* obj, QEvent* event) { if (event->type() == QEvent::ToolTip) { // Always show tooltips if Ctrl is held down if (QApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) { return QMainWindow::eventFilter(obj, event); } // Always show tooltips for cue type buttons in the Cue menu if (QLatin1String(obj->metaObject()->className()) == "CueMenuPushButton") { return QMainWindow::eventFilter(obj, event); } // Always show tooltips in Preferences QWidget* activeWindow = QApplication::activeWindow(); if (activeWindow && QLatin1String(activeWindow->metaObject()->className()) == "DlgPreferences") { return QMainWindow::eventFilter(obj, event); }
// For all other we follow the tooltip sett8ing. // Return true for no tool tips switch (m_toolTipsCfg) { case mixxx::preferences::Tooltips::OnlyInLibrary: // WLibrary's stacked widgets are not derived from WBaseWidget if (dynamic_cast<WBaseWidget*>(obj) != nullptr) { return true; } break; case mixxx::preferences::Tooltips::OnlyKbdShortcuts: if (dynamic_cast<WBaseWidget*>(obj) == nullptr) { return true; } break; case mixxx::preferences::Tooltips::On: break; case mixxx::preferences::Tooltips::Off: return true; default: DEBUG_ASSERT(!"m_toolTipsCfg value unknown"); return true; } } else if (event->type() == QEvent::WindowStateChange) {#ifndef __APPLE__ if (windowState() == m_prevState) { // Ignore no-op. This happens if another window is raised above // MixxxMianWindow, e.g. DlgPeferences. In such a case event->oldState() // will be Qt::WindowNoState which is wrong anyway, so there is nothing // to do internally. return QMainWindow::eventFilter(obj, event); } m_prevState = windowState();#endif // Detect if we entered or quit fullscreen mode. QWindowStateChangeEvent* changeEvent = static_cast<QWindowStateChangeEvent*>(event); const bool wasFullScreen = changeEvent->oldState() & Qt::WindowFullScreen; const bool isFullScreenNow = windowState() & Qt::WindowFullScreen; if ((isFullScreenNow && !wasFullScreen) || (!isFullScreenNow && wasFullScreen)) {#ifdef __LINUX__ // Fix for "No menu bar with ubuntu unity in full screen mode" // (issues #6072 and #6689). Before touching anything here, please // read those bugs. // Set this attribute instead of calling setNativeMenuBar(false), // see https://github.com/mixxxdj/mixxx/issues/11320 if (m_supportsGlobalMenuBar) { QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, isFullScreenNow); createMenuBar(); connectMenuBar(); }#endif
#ifndef __APPLE__#ifdef __LINUX__ // Only show the dialog if we are able to have the menubar in the // main window, only then we're able to hide it. if (!m_supportsGlobalMenuBar || isFullScreenNow)#endif { if (!m_inRebootMixxxView) { alwaysHideMenuBarDlg(); } slotUpdateMenuBarAltKeyConnection(); }#endif
// This will toggle the Fullscreen checkbox and hide the menubar if // we go fullscreen. // Skip this during startup or the launchimage will be shifted // up & down when the menu is shown menu and 'hidden'. The menu // will be updated when the skin finished loading. if (centralWidget() != m_pLaunchImage) { emit fullScreenChanged(isFullScreen()); } } } // standard event processing return QMainWindow::eventFilter(obj, event);}
void MixxxMainWindow::closeEvent(QCloseEvent *event) { // WARNING: We can receive a CloseEvent while only partially // initialized. This is because we call QApplication::processEvents to // render LaunchImage progress in the constructor. if (!confirmExit()) { event->ignore(); return; } QMainWindow::closeEvent(event);}
void MixxxMainWindow::checkDirectRendering() { // IF // * A waveform viewer exists // AND // * The waveform viewer is an OpenGL waveform viewer // AND // * The waveform viewer does not have direct rendering enabled. // THEN // * Warn user
WaveformWidgetFactory* factory = WaveformWidgetFactory::instance(); if (!factory) { return; }
UserSettingsPointer pConfig = m_pCoreServices->getSettings();
if (!factory->isOpenGlAvailable() && !factory->isOpenGlesAvailable() && pConfig->getValueString(ConfigKey("[Direct Rendering]", "Warned")) != QString("yes")) { QMessageBox::warning(nullptr, tr("OpenGL Direct Rendering"), tr("Direct rendering is not enabled on your machine.<br><br>" "This means that the waveform displays will be very<br>" "<b>slow and may tax your CPU heavily</b>. Either update " "your<br>" "configuration to enable direct rendering, or disable<br>" "the waveform displays in the Mixxx preferences by " "selecting<br>" "\"Empty\" as the waveform display in the 'Interface' " "section.")); pConfig->set(ConfigKey("[Direct Rendering]", "Warned"), QString("yes")); }}
bool MixxxMainWindow::confirmExit() { bool playing(false); bool playingSampler(false); auto pPlayerManager = m_pCoreServices->getPlayerManager(); int deckCount = pPlayerManager->numberOfDecks(); int samplerCount = pPlayerManager->numberOfSamplers(); for (int i = 0; i < deckCount; ++i) { if (ControlObject::toBool( ConfigKey(PlayerManager::groupForDeck(i), "play"))) { playing = true; break; } } for (int i = 0; i < samplerCount; ++i) { if (ControlObject::toBool( ConfigKey(PlayerManager::groupForSampler(i), "play"))) { playingSampler = true; break; } } if (playing) { QMessageBox::StandardButton btn = QMessageBox::question(this, tr("Confirm Exit"), tr("A deck is currently playing. Exit Mixxx?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (btn == QMessageBox::No) { return false; } } else if (playingSampler) { QMessageBox::StandardButton btn = QMessageBox::question(this, tr("Confirm Exit"), tr("A sampler is currently playing. Exit Mixxx?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (btn == QMessageBox::No) { return false; } } if (m_pPrefDlg && m_pPrefDlg->isVisible()) { QMessageBox::StandardButton btn = QMessageBox::question( this, tr("Confirm Exit"), tr("The preferences window is still open.") + "<br>" + tr("Discard any changes and exit Mixxx?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (btn == QMessageBox::No) { return false; } else { m_pPrefDlg->close(); } }
return true;}
void MixxxMainWindow::initializationProgressUpdate(int progress, const QString& serviceName) { if (m_pLaunchImage) { m_pLaunchImage->progress(progress, serviceName); } qApp->processEvents();}