![]() |
Mixxx
|
00001 /*************************************************************************** 00002 mixxx.cpp - description 00003 ------------------- 00004 begin : Mon Feb 18 09:48:17 CET 2002 00005 copyright : (C) 2002 by Tue and Ken Haste Andersen 00006 email : 00007 ***************************************************************************/ 00008 00009 /*************************************************************************** 00010 * * 00011 * This program is free software; you can redistribute it and/or modify * 00012 * it under the terms of the GNU General Public License as published by * 00013 * the Free Software Foundation; either version 2 of the License, or * 00014 * (at your option) any later version. * 00015 * * 00016 ***************************************************************************/ 00017 00018 #include <QtDebug> 00019 #include <QtCore> 00020 #include <QtGui> 00021 #include <QTranslator> 00022 00023 #include "mixxx.h" 00024 #include "controlnull.h" 00025 #include "controlpotmeter.h" 00026 #include "controlobjectthreadmain.h" 00027 #include "engine/enginemaster.h" 00028 #include "engine/enginemicrophone.h" 00029 #include "trackinfoobject.h" 00030 #include "dlgabout.h" 00031 #include "waveformviewerfactory.h" 00032 #include "waveform/waveformrenderer.h" 00033 #include "soundsourceproxy.h" 00034 00035 #include "analyserqueue.h" 00036 #include "playermanager.h" 00037 00038 #include "library/library.h" 00039 #include "library/librarytablemodel.h" 00040 #include "library/libraryscanner.h" 00041 00042 #include "soundmanager.h" 00043 #include "soundmanagerutil.h" 00044 #include "defs_urls.h" 00045 #include "recording/defs_recording.h" 00046 00047 00048 #include "midi/mididevicemanager.h" 00049 00050 #include "upgrade.h" 00051 #include "mixxxkeyboard.h" 00052 #include "skin/skinloader.h" 00053 #include "skin/legacyskinparser.h" 00054 00055 #include "build.h" // #defines of details of the build set up (flags, 00056 // repo number, etc). This isn't a real file, SConscript generates it and it 00057 // probably gets placed in $PLATFORM_build/. By including this file here and 00058 // only here we make sure that updating src or changing the build flags doesn't 00059 // force a rebuild of everything 00060 00061 #include "defs_version.h" 00062 00063 #ifdef __VINYLCONTROL__ 00064 #include "vinylcontrol/vinylcontrol.h" 00065 #include "vinylcontrol/vinylcontrolmanager.h" 00066 #endif 00067 00068 #ifdef __C_METRICS__ 00069 #include <cmetrics.h> 00070 #include "defs_mixxxcmetrics.h" 00071 #endif 00072 00073 00074 extern "C" void crashDlg() 00075 { 00076 QMessageBox::critical(0, "Mixxx", 00077 "Mixxx has encountered a serious error and needs to close."); 00078 } 00079 00080 00081 MixxxApp::MixxxApp(QApplication *a, struct CmdlineArgs args) 00082 { 00083 m_pApp = a; 00084 00085 QString buildBranch, buildRevision, buildFlags; 00086 #ifdef BUILD_BRANCH 00087 buildBranch = BUILD_BRANCH; 00088 #endif 00089 00090 #ifdef BUILD_REV 00091 buildRevision = BUILD_REV; 00092 #endif 00093 00094 #ifdef BUILD_FLAGS 00095 buildFlags = BUILD_FLAGS; 00096 #endif 00097 00098 QStringList buildInfo; 00099 if (!buildBranch.isEmpty() && !buildRevision.isEmpty()) { 00100 buildInfo.append( 00101 QString("bzr %1 r%2").arg(buildBranch, buildRevision)); 00102 } else if (!buildRevision.isEmpty()) { 00103 buildInfo.append( 00104 QString("bzr r%2").arg(buildRevision)); 00105 } 00106 buildInfo.append("built on: " __DATE__ " @ " __TIME__); 00107 if (!buildFlags.isEmpty()) { 00108 buildInfo.append(QString("flags: %1").arg(buildFlags.trimmed())); 00109 } 00110 QString buildInfoFormatted = QString("(%1)").arg(buildInfo.join("; ")); 00111 00112 qDebug() << "Mixxx" << VERSION << buildInfoFormatted << "is starting..."; 00113 qDebug() << "Qt version is:" << qVersion(); 00114 00115 QCoreApplication::setApplicationName("Mixxx"); 00116 QCoreApplication::setApplicationVersion(VERSION); 00117 #ifdef __APPLE__ 00118 setWindowTitle(tr("Mixxx")); //App Store 00119 #elif defined(AMD64) || defined(EM64T) || defined(x86_64) 00120 setWindowTitle(tr("Mixxx " VERSION " x64")); 00121 #elif defined(IA64) 00122 setWindowTitle(tr("Mixxx " VERSION " Itanium")); 00123 #else 00124 setWindowTitle(tr("Mixxx " VERSION)); 00125 #endif 00126 setWindowIcon(QIcon(":/images/ic_mixxx_window.png")); 00127 00128 //Reset pointer to players 00129 m_pSoundManager = 0; 00130 m_pPrefDlg = 0; 00131 m_pMidiDeviceManager = 0; 00132 m_pRecordingManager = 0; 00133 00134 // Check to see if this is the first time this version of Mixxx is run 00135 // after an upgrade and make any needed changes. 00136 Upgrade upgrader; 00137 m_pConfig = upgrader.versionUpgrade(); 00138 bool bFirstRun = upgrader.isFirstRun(); 00139 bool bUpgraded = upgrader.isUpgraded(); 00140 QString qConfigPath = m_pConfig->getConfigPath(); 00141 QString translationsFolder = qConfigPath + "translations/"; 00142 00143 // Load Qt base translations 00144 QString locale = args.locale; 00145 if (locale == "") { 00146 locale = QLocale::system().name(); 00147 } 00148 00149 // Load Qt translations for this locale from the system translation 00150 // path. This is the lowest precedence QTranslator. 00151 QTranslator* qtTranslator = new QTranslator(a); 00152 if (qtTranslator->load("qt_" + locale, 00153 QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { 00154 a->installTranslator(qtTranslator); 00155 } else { 00156 delete qtTranslator; 00157 } 00158 00159 // Load Qt translations for this locale from the Mixxx translations 00160 // folder. 00161 QTranslator* mixxxQtTranslator = new QTranslator(a); 00162 if (mixxxQtTranslator->load("qt_" + locale, translationsFolder)) { 00163 a->installTranslator(mixxxQtTranslator); 00164 } else { 00165 delete mixxxQtTranslator; 00166 } 00167 00168 // Load Mixxx specific translations for this locale from the Mixxx 00169 // translations folder. 00170 QTranslator* mixxxTranslator = new QTranslator(a); 00171 bool mixxxLoaded = mixxxTranslator->load("mixxx_" + locale, 00172 translationsFolder); 00173 qDebug() << "Loading translations for locale" << locale 00174 << "from translations folder" << translationsFolder << ":" 00175 << (mixxxLoaded ? "success" : "fail"); 00176 if (mixxxLoaded) { 00177 a->installTranslator(mixxxTranslator); 00178 } else { 00179 delete mixxxTranslator; 00180 } 00181 00182 #ifdef __C_METRICS__ 00183 // Initialize Case Metrics if User is OK with that 00184 QString metricsAgree = 00185 m_pConfig->getValueString( 00186 ConfigKey("[User Experience]", "AgreedToUserExperienceProgram")); 00187 00188 if (metricsAgree.isEmpty() || (metricsAgree != "yes" && metricsAgree != "no")) { 00189 metricsAgree = "no"; 00190 int dlg = -1; 00191 while (dlg != 0 && dlg != 1) { 00192 dlg = QMessageBox::question(this, tr("Mixxx"), 00193 tr("Mixxx's development is driven by community feedback. At " 00194 "your discretion, Mixxx can automatically send data on your " 00195 "user experience back to the developers. Would you like to " 00196 "help us make Mixxx better by enabling this feature?"), 00197 tr("Yes"), tr("No"), tr("Privacy Policy"), 0, -1); 00198 switch (dlg) { 00199 case 0: metricsAgree = "yes"; 00200 case 1: break; 00201 default: //show privacy policy 00202 QMessageBox::information(this, tr("Mixxx: Privacy Policy"), 00203 tr("Mixxx's development is driven by community feedback. " 00204 "In order to help improve future versions Mixxx will with " 00205 "your permission collect information on your hardware and " 00206 "usage of Mixxx. This information will primarily be used " 00207 "to fix bugs, improve features, and determine the system " 00208 "requirements of later versions. Additionally this " 00209 "information may be used in aggregate for statistical " 00210 "purposes.\n\n" 00211 "The hardware information will include:\n" 00212 "\t- CPU model and features\n" 00213 "\t- Total/Available Amount of RAM\n" 00214 "\t- Available disk space\n" 00215 "\t- OS version\n\n" 00216 "Your usage information will include:\n" 00217 "\t- Settings/Preferences\n" 00218 "\t- Internal errors\n" 00219 "\t- Internal debugging messages\n" 00220 "\t- Performance statistics (average latency, CPU usage)\n" 00221 "\nThis information will not be used to personally " 00222 "identify you, contact you, advertise to you, or otherwise" 00223 " bother you in any way.\n")); 00224 break; 00225 } 00226 } 00227 } 00228 m_pConfig->set( 00229 ConfigKey("[User Experience]", "AgreedToUserExperienceProgram"), 00230 ConfigValue(metricsAgree) 00231 ); 00232 00233 // If the user agrees... 00234 if (metricsAgree == "yes") { 00235 // attempt to load the user ID from the config file 00236 if (m_pConfig->getValueString(ConfigKey("[User Experience]", "UID")) 00237 == "") { 00238 QString pUID = cm_generate_userid(); 00239 if (!pUID.isEmpty()) { 00240 m_pConfig->set( 00241 ConfigKey("[User Experience]", "UID"), ConigValue(pUID)); 00242 } 00243 } 00244 } 00245 // Initialize cmetrics 00246 cm_init(100,20, metricsAgree == "yes", MIXXCMETRICS_RELEASE_ID, 00247 m_pConfig->getValueString(ConfigKey("[User Experience]", "UID")) 00248 .ascii()); 00249 cm_set_crash_dlg(crashDlg); 00250 cm_writemsg_ascii(MIXXXCMETRICS_VERSION, VERSION); 00251 #endif 00252 00253 // Store the path in the config database 00254 m_pConfig->set(ConfigKey("[Config]", "Path"), ConfigValue(qConfigPath)); 00255 00256 // Read keyboard configuration and set kdbConfig object in WWidget 00257 // Check first in user's Mixxx directory 00258 QString userKeyboard = 00259 QDir::homePath().append("/").append(SETTINGS_PATH) 00260 .append("Custom.kbd.cfg"); 00261 00262 ConfigObject<ConfigValueKbd>* pKbdConfig = NULL; 00263 00264 if (QFile::exists(userKeyboard)) { 00265 qDebug() << "Found and will use custom keyboard preset" << userKeyboard; 00266 pKbdConfig = new ConfigObject<ConfigValueKbd>(userKeyboard); 00267 } 00268 else 00269 // Otherwise use the default 00270 pKbdConfig = 00271 new ConfigObject<ConfigValueKbd>( 00272 QString(qConfigPath) 00273 .append("keyboard/").append("Standard.kbd.cfg")); 00274 00275 // TODO(XXX) leak pKbdConfig, MixxxKeyboard owns it? Maybe roll all keyboard 00276 // initialization into MixxxKeyboard 00277 // Workaround for today: MixxxKeyboard calls delete 00278 m_pKeyboard = new MixxxKeyboard(pKbdConfig); 00279 00280 //create RecordingManager 00281 m_pRecordingManager = new RecordingManager(m_pConfig); 00282 00283 // Starting the master (mixing of the channels and effects): 00284 m_pEngine = new EngineMaster(m_pConfig, "[Master]"); 00285 00286 connect(m_pEngine, SIGNAL(isRecording(bool)), 00287 m_pRecordingManager,SLOT(slotIsRecording(bool))); 00288 connect(m_pEngine, SIGNAL(bytesRecorded(int)), 00289 m_pRecordingManager,SLOT(slotBytesRecorded(int))); 00290 00291 // Initialize player device 00292 // while this is created here, setupDevices needs to be called sometime 00293 // after the players are added to the engine (as is done currently) -- bkgood 00294 m_pSoundManager = new SoundManager(m_pConfig, m_pEngine); 00295 00296 EngineMicrophone* pMicrophone = new EngineMicrophone("[Microphone]"); 00297 AudioInput micInput = AudioInput(AudioPath::MICROPHONE, 0, 0); // What should channelbase be? 00298 m_pEngine->addChannel(pMicrophone); 00299 m_pSoundManager->registerInput(micInput, pMicrophone); 00300 00301 // Get Music dir 00302 bool hasChanged_MusicDir = false; 00303 QDir dir(m_pConfig->getValueString(ConfigKey("[Playlist]","Directory"))); 00304 if (m_pConfig->getValueString( 00305 ConfigKey("[Playlist]","Directory")).length() < 1 || !dir.exists()) 00306 { 00307 // TODO this needs to be smarter, we can't distinguish between an empty 00308 // path return value (not sure if this is normally possible, but it is 00309 // possible with the Windows 7 "Music" library, which is what 00310 // QDesktopServices::storageLocation(QDesktopServices::MusicLocation) 00311 // resolves to) and a user hitting 'cancel'. If we get a blank return 00312 // but the user didn't hit cancel, we need to know this and let the 00313 // user take some course of action -- bkgood 00314 QString fd = QFileDialog::getExistingDirectory( 00315 this, tr("Choose music library directory"), 00316 QDesktopServices::storageLocation(QDesktopServices::MusicLocation)); 00317 00318 if (fd != "") 00319 { 00320 m_pConfig->set(ConfigKey("[Playlist]","Directory"), fd); 00321 m_pConfig->Save(); 00322 hasChanged_MusicDir = true; 00323 } 00324 } 00325 /* 00326 * Do not write meta data back to ID3 when meta data has changed 00327 * Because multiple TrackDao objects can exists for a particular track 00328 * writing meta data may ruine your MP3 file if done simultaneously. 00329 * see Bug #728197 00330 * For safety reasons, we deactivate this feature. 00331 */ 00332 m_pConfig->set(ConfigKey("[Library]","WriteAudioTags"), ConfigValue(0)); 00333 00334 00335 // library dies in seemingly unrelated qtsql error about not having a 00336 // sqlite driver if this path doesn't exist. Normally config->Save() 00337 // above would make it but if it doesn't get run for whatever reason 00338 // we get hosed -- bkgood 00339 if (!QDir(QDir::homePath().append("/").append(SETTINGS_PATH)).exists()) { 00340 QDir().mkpath(QDir::homePath().append("/").append(SETTINGS_PATH)); 00341 } 00342 00343 00344 00345 m_pLibrary = new Library(this, m_pConfig, 00346 bFirstRun || bUpgraded, 00347 m_pRecordingManager); 00348 qRegisterMetaType<TrackPointer>("TrackPointer"); 00349 00350 // Create the player manager. 00351 m_pPlayerManager = new PlayerManager(m_pConfig, m_pEngine, m_pLibrary); 00352 m_pPlayerManager->addDeck(); 00353 m_pPlayerManager->addDeck(); 00354 m_pPlayerManager->addSampler(); 00355 m_pPlayerManager->addSampler(); 00356 m_pPlayerManager->addSampler(); 00357 m_pPlayerManager->addSampler(); 00358 00359 // register the engine's outputs 00360 m_pSoundManager->registerOutput(AudioOutput(AudioOutput::MASTER), 00361 m_pEngine); 00362 m_pSoundManager->registerOutput(AudioOutput(AudioOutput::HEADPHONES), 00363 m_pEngine); 00364 for (unsigned int deck = 0; deck < m_pPlayerManager->numDecks(); ++deck) { 00365 // TODO(bkgood) make this look less dumb by putting channelBase after 00366 // index in the AudioOutput() params 00367 m_pSoundManager->registerOutput( 00368 AudioOutput(AudioOutput::DECK, 0, deck), m_pEngine); 00369 } 00370 00371 #ifdef __VINYLCONTROL__ 00372 m_pVCManager = new VinylControlManager(this, m_pConfig); 00373 for (unsigned int deck = 0; deck < m_pPlayerManager->numDecks(); ++deck) { 00374 m_pSoundManager->registerInput( 00375 AudioInput(AudioInput::VINYLCONTROL, 0, deck), 00376 m_pVCManager); 00377 } 00378 #else 00379 m_pVCManager = NULL; 00380 #endif 00381 00382 //Scan the library directory. 00383 m_pLibraryScanner = new LibraryScanner(m_pLibrary->getTrackCollection()); 00384 00385 //Refresh the library models when the library (re)scan is finished. 00386 connect(m_pLibraryScanner, SIGNAL(scanFinished()), 00387 m_pLibrary, SLOT(slotRefreshLibraryModels())); 00388 00389 //Scan the library for new files and directories 00390 bool rescan = (bool)m_pConfig->getValueString(ConfigKey("[Library]","RescanOnStartup")).toInt(); 00391 // rescan the library if we get a new plugin 00392 QSet<QString> prev_plugins = QSet<QString>::fromList(m_pConfig->getValueString( 00393 ConfigKey("[Library]", "SupportedFileExtensions")).split(",", QString::SkipEmptyParts)); 00394 QSet<QString> curr_plugins = QSet<QString>::fromList( 00395 SoundSourceProxy::supportedFileExtensions()); 00396 rescan = rescan || (prev_plugins != curr_plugins); 00397 00398 if(rescan || hasChanged_MusicDir){ 00399 m_pLibraryScanner->scan( 00400 m_pConfig->getValueString(ConfigKey("[Playlist]", "Directory"))); 00401 qDebug() << "Rescan finished"; 00402 } 00403 m_pConfig->set(ConfigKey("[Library]", "SupportedFileExtensions"), 00404 QStringList(SoundSourceProxy::supportedFileExtensions()).join(",")); 00405 00406 // Call inits to invoke all other construction parts 00407 00408 // Verify path for xml track file. 00409 QFile trackfile( 00410 m_pConfig->getValueString(ConfigKey("[Playlist]", "Listfile"))); 00411 if (m_pConfig->getValueString(ConfigKey("[Playlist]", "Listfile")) 00412 .length() < 1 || !trackfile.exists()) 00413 { 00414 m_pConfig->set(ConfigKey("[Playlist]", "Listfile"), 00415 QDir::homePath().append("/").append(SETTINGS_PATH) 00416 .append(TRACK_FILE)); 00417 m_pConfig->Save(); 00418 } 00419 00420 // Intialize default BPM system values 00421 if (m_pConfig->getValueString(ConfigKey("[BPM]", "BPMRangeStart")) 00422 .length() < 1) 00423 { 00424 m_pConfig->set(ConfigKey("[BPM]", "BPMRangeStart"),ConfigValue(65)); 00425 } 00426 00427 if (m_pConfig->getValueString(ConfigKey("[BPM]", "BPMRangeEnd")) 00428 .length() < 1) 00429 { 00430 m_pConfig->set(ConfigKey("[BPM]", "BPMRangeEnd"),ConfigValue(135)); 00431 } 00432 00433 if (m_pConfig->getValueString(ConfigKey("[BPM]", "AnalyzeEntireSong")) 00434 .length() < 1) 00435 { 00436 m_pConfig->set(ConfigKey("[BPM]", "AnalyzeEntireSong"),ConfigValue(1)); 00437 } 00438 00439 //ControlObject::getControl(ConfigKey("[Channel1]","TrackEndMode"))->queueFromThread(m_pConfig->getValueString(ConfigKey("[Controls]","TrackEndModeCh1")).toDouble()); 00440 //ControlObject::getControl(ConfigKey("[Channel2]","TrackEndMode"))->queueFromThread(m_pConfig->getValueString(ConfigKey("[Controls]","TrackEndModeCh2")).toDouble()); 00441 00442 qRegisterMetaType<MidiMessage>("MidiMessage"); 00443 qRegisterMetaType<MidiStatusByte>("MidiStatusByte"); 00444 00445 // Initialise midi 00446 m_pMidiDeviceManager = new MidiDeviceManager(m_pConfig); 00447 m_pMidiDeviceManager->setupDevices(); 00448 00449 m_pSkinLoader = new SkinLoader(m_pConfig); 00450 00451 // Initialize preference dialog 00452 m_pPrefDlg = new DlgPreferences(this, m_pSkinLoader, m_pSoundManager, m_pPlayerManager, 00453 m_pMidiDeviceManager, m_pVCManager, m_pConfig); 00454 m_pPrefDlg->setWindowIcon(QIcon(":/images/ic_mixxx_window.png")); 00455 m_pPrefDlg->setHidden(true); 00456 00457 // Try open player device If that fails, the preference panel is opened. 00458 int setupDevices = m_pSoundManager->setupDevices(); 00459 unsigned int numDevices = m_pSoundManager->getConfig().getOutputs().count(); 00460 // test for at least one out device, if none, display another dlg that 00461 // says "mixxx will barely work with no outs" 00462 while (setupDevices != OK || numDevices == 0) 00463 { 00464 00465 #ifdef __C_METRICS__ 00466 cm_writemsg_ascii(MIXXXCMETRICS_FAILED_TO_OPEN_SNDDEVICE_AT_STARTUP, 00467 "Mixxx failed to open audio device(s) on startup."); 00468 #endif 00469 00470 // Exit when we press the Exit button in the noSoundDlg dialog 00471 // only call it if setupDevices != OK 00472 if (setupDevices != OK) { 00473 if (noSoundDlg() != 0) { 00474 exit(0); 00475 } 00476 } else if (numDevices == 0) { 00477 bool continueClicked = false; 00478 int noOutput = noOutputDlg(&continueClicked); 00479 if (continueClicked) break; 00480 if (noOutput != 0) { 00481 exit(0); 00482 } 00483 } 00484 setupDevices = m_pSoundManager->setupDevices(); 00485 numDevices = m_pSoundManager->getConfig().getOutputs().count(); 00486 } 00487 00488 //setFocusPolicy(QWidget::StrongFocus); 00489 //grabKeyboard(); 00490 00491 // Load tracks in args.qlMusicFiles (command line arguments) into player 00492 // 1 and 2: 00493 for (int i = 0; i < (int)m_pPlayerManager->numDecks() 00494 && i < args.qlMusicFiles.count(); ++i) { 00495 m_pPlayerManager->slotLoadToDeck(args.qlMusicFiles.at(i), i+1); 00496 } 00497 00498 //Automatically load specially marked promotional tracks on first run 00499 if (bFirstRun || bUpgraded) { 00500 QList<TrackPointer> tracksToAutoLoad = 00501 m_pLibrary->getTracksToAutoLoad(); 00502 for (int i = 0; i < (int)m_pPlayerManager->numDecks() 00503 && i < tracksToAutoLoad.count(); i++) { 00504 m_pPlayerManager->slotLoadToDeck(tracksToAutoLoad.at(i)->getLocation(), i+1); 00505 } 00506 } 00507 00508 #ifdef __SCRIPT__ 00509 scriptEng = new ScriptEngine(this, m_pTrack); 00510 #endif 00511 00512 initActions(); 00513 initMenuBar(); 00514 00515 // Use frame as container for view, needed for fullscreen display 00516 m_pView = new QFrame; 00517 00518 m_pWidgetParent = NULL; 00519 // Loads the skin as a child of m_pView 00520 // assignment itentional in next line 00521 if (!(m_pWidgetParent = m_pSkinLoader->loadDefaultSkin(m_pView, 00522 m_pKeyboard, 00523 m_pPlayerManager, 00524 m_pLibrary, 00525 m_pVCManager))) { 00526 qDebug() << "Could not load default skin."; 00527 } 00528 00529 // this has to be after the OpenGL widgets are created or depending on a 00530 // million different variables the first waveform may be horribly 00531 // corrupted. See bug 521509 -- bkgood 00532 setCentralWidget(m_pView); 00533 00534 // keep gui centered (esp for fullscreen) 00535 // the layout will be deleted whenever m_pView gets deleted 00536 QHBoxLayout *pLayout = new QHBoxLayout(m_pView); 00537 pLayout->addWidget(m_pWidgetParent); 00538 pLayout->setContentsMargins(0, 0, 0, 0); // don't want margins 00539 00540 // Check direct rendering and warn user if they don't have it 00541 checkDirectRendering(); 00542 00543 //Install an event filter to catch certain QT events, such as tooltips. 00544 //This allows us to turn off tooltips. 00545 m_pApp->installEventFilter(this); // The eventfilter is located in this 00546 // Mixxx class as a callback. 00547 00548 // If we were told to start in fullscreen mode on the command-line, 00549 // then turn on fullscreen mode. 00550 if (args.bStartInFullscreen) 00551 slotOptionsFullScreen(true); 00552 #ifdef __C_METRICS__ 00553 cm_writemsg_ascii(MIXXXCMETRICS_MIXXX_CONSTRUCTOR_COMPLETE, 00554 "Mixxx constructor complete."); 00555 #endif 00556 00557 // Refresh the GUI (workaround for Qt 4.6 display bug) 00558 /* // TODO(bkgood) delete this block if the moving of setCentralWidget 00559 * // totally fixes this first-wavefore-fubar issue for 00560 * // everyone 00561 QString QtVersion = qVersion(); 00562 if (QtVersion>="4.6.0") { 00563 qDebug() << "Qt v4.6.0 or higher detected. Using rebootMixxxView() " 00564 "workaround.\n (See bug https://bugs.launchpad.net/mixxx/" 00565 "+bug/521509)"; 00566 rebootMixxxView(); 00567 } */ 00568 } 00569 00570 MixxxApp::~MixxxApp() 00571 { 00572 QTime qTime; 00573 qTime.start(); 00574 00575 qDebug() << "Destroying MixxxApp"; 00576 00577 // Moved this up to insulate macros you've worked hard on from being lost in 00578 // a segfault that happens sometimes somewhere below here 00579 #ifdef __SCRIPT__ 00580 scriptEng->saveMacros(); 00581 delete scriptEng; 00582 #endif 00583 00584 qDebug() << "save config, " << qTime.elapsed(); 00585 m_pConfig->Save(); 00586 00587 // Save state of End of track controls in config database 00588 //m_pConfig->set(ConfigKey("[Controls]","TrackEndModeCh1"), ConfigValue((int)ControlObject::getControl(ConfigKey("[Channel1]","TrackEndMode"))->get())); 00589 //m_pConfig->set(ConfigKey("[Controls]","TrackEndModeCh2"), ConfigValue((int)ControlObject::getControl(ConfigKey("[Channel2]","TrackEndMode"))->get())); 00590 00591 // SoundManager depend on Engine and Config 00592 qDebug() << "delete soundmanager, " << qTime.elapsed(); 00593 delete m_pSoundManager; 00594 00595 #ifdef __VINYLCONTROL__ 00596 // VinylControlManager depends on a CO the engine owns 00597 // (vinylcontrol_enabled in VinylControlControl) 00598 qDebug() << "delete vinylcontrolmanager, " << qTime.elapsed(); 00599 delete m_pVCManager; 00600 #endif 00601 00602 // View depends on MixxxKeyboard, PlayerManager, Library 00603 qDebug() << "delete view, " << qTime.elapsed(); 00604 delete m_pView; 00605 00606 // SkinLoader depends on Config 00607 qDebug() << "delete SkinLoader"; 00608 delete m_pSkinLoader; 00609 00610 // MIDIDeviceManager depends on Config 00611 qDebug() << "delete MidiDeviceManager"; 00612 delete m_pMidiDeviceManager; 00613 00614 // PlayerManager depends on Engine, Library, and Config 00615 qDebug() << "delete playerManager" << qTime.elapsed(); 00616 delete m_pPlayerManager; 00617 00618 // EngineMaster depends on Config 00619 qDebug() << "delete m_pEngine, " << qTime.elapsed(); 00620 delete m_pEngine; 00621 00622 // LibraryScanner depends on Library 00623 qDebug() << "delete library scanner" << qTime.elapsed(); 00624 delete m_pLibraryScanner; 00625 00626 // Delete the library after the view so there are no dangling pointers to 00627 // Depends on RecordingManager 00628 // the data models. 00629 qDebug() << "delete library" << qTime.elapsed(); 00630 delete m_pLibrary; 00631 00632 // RecordingManager depends on config 00633 qDebug() << "delete RecordingManager" << qTime.elapsed(); 00634 delete m_pRecordingManager; 00635 00636 // HACK: Save config again. We saved it once before doing some dangerous 00637 // stuff. We only really want to save it here, but the first one was just 00638 // a precaution. The earlier one can be removed when stuff is more stable 00639 // at exit. 00640 00641 //Disable shoutcast so when Mixxx starts again it will not connect 00642 m_pConfig->set(ConfigKey("[Shoutcast]", "enabled"),0); 00643 m_pConfig->Save(); 00644 delete m_pPrefDlg; 00645 00646 #ifdef __C_METRICS__ 00647 // cmetrics will cause this whole method to segfault on Linux/i386 if it is 00648 // called after config is deleted. Obviously, it depends on config somehow. 00649 qDebug() << "cmetrics to report:" << "Mixxx deconstructor complete."; 00650 cm_writemsg_ascii(MIXXXCMETRICS_MIXXX_DESTRUCTOR_COMPLETE, 00651 "Mixxx deconstructor complete."); 00652 cm_close(10); 00653 #endif 00654 00655 qDebug() << "delete config, " << qTime.elapsed(); 00656 delete m_pConfig; 00657 00658 // Check for leaked ControlObjects and give warnings. 00659 QList<ControlObject*> leakedControls; 00660 QList<ConfigKey> leakedConfigKeys; 00661 00662 ControlObject::getControls(&leakedControls); 00663 00664 if (leakedControls.size() > 0) { 00665 qDebug() << "WARNING: The following" << leakedControls.size() << "controls were leaked:"; 00666 foreach (ControlObject* pControl, leakedControls) { 00667 ConfigKey key = pControl->getKey(); 00668 qDebug() << key.group << key.item; 00669 leakedConfigKeys.append(key); 00670 } 00671 00672 foreach (ConfigKey key, leakedConfigKeys) { 00673 // delete just to satisfy valgrind: 00674 // check if the pointer is still valid, the control object may have bin already 00675 // deleted by its parent in this loop 00676 delete ControlObject::getControl(key); 00677 } 00678 } 00679 qDebug() << "~MixxxApp: All leaking controls deleted."; 00680 00681 delete m_pKeyboard; 00682 } 00683 00684 int MixxxApp::noSoundDlg(void) 00685 { 00686 QMessageBox msgBox; 00687 msgBox.setIcon(QMessageBox::Warning); 00688 msgBox.setWindowTitle(tr("Sound Device Busy")); 00689 msgBox.setText( 00690 "<html>" + 00691 tr("Mixxx was unable to access all the configured sound devices. " 00692 "Another application is using a sound device Mixxx is configured to " 00693 "use or a device is not plugged in.") + 00694 "<ul>" 00695 "<li>" + 00696 tr("<b>Retry</b> after closing the other application " 00697 "or reconnecting a sound device") + 00698 "</li>" 00699 "<li>" + 00700 tr("<b>Reconfigure</b> Mixxx's sound device settings.") + 00701 "</li>" 00702 "<li>" + 00703 tr("Get <b>Help</b> from the Mixxx Wiki.") + 00704 "</li>" 00705 "<li>" + 00706 tr("<b>Exit</b> Mixxx.") + 00707 "</li>" 00708 "</ul></html>" 00709 ); 00710 00711 QPushButton *retryButton = msgBox.addButton(tr("Retry"), 00712 QMessageBox::ActionRole); 00713 QPushButton *reconfigureButton = msgBox.addButton(tr("Reconfigure"), 00714 QMessageBox::ActionRole); 00715 QPushButton *wikiButton = msgBox.addButton(tr("Help"), 00716 QMessageBox::ActionRole); 00717 QPushButton *exitButton = msgBox.addButton(tr("Exit"), 00718 QMessageBox::ActionRole); 00719 00720 while (true) 00721 { 00722 msgBox.exec(); 00723 00724 if (msgBox.clickedButton() == retryButton) { 00725 m_pSoundManager->queryDevices(); 00726 return 0; 00727 } else if (msgBox.clickedButton() == wikiButton) { 00728 QDesktopServices::openUrl(QUrl( 00729 "http://mixxx.org/wiki/doku.php/troubleshooting" 00730 "#no_or_too_few_sound_cards_appear_in_the_preferences_dialog") 00731 ); 00732 wikiButton->setEnabled(false); 00733 } else if (msgBox.clickedButton() == reconfigureButton) { 00734 msgBox.hide(); 00735 m_pSoundManager->queryDevices(); 00736 00737 // This way of opening the dialog allows us to use it synchronously 00738 m_pPrefDlg->setWindowModality(Qt::ApplicationModal); 00739 m_pPrefDlg->exec(); 00740 if (m_pPrefDlg->result() == QDialog::Accepted) { 00741 m_pSoundManager->queryDevices(); 00742 return 0; 00743 } 00744 00745 msgBox.show(); 00746 00747 } else if (msgBox.clickedButton() == exitButton) { 00748 return 1; 00749 } 00750 } 00751 } 00752 00753 int MixxxApp::noOutputDlg(bool *continueClicked) 00754 { 00755 QMessageBox msgBox; 00756 msgBox.setIcon(QMessageBox::Warning); 00757 msgBox.setWindowTitle("No Output Devices"); 00758 msgBox.setText( "<html>Mixxx was configured without any output sound devices. " 00759 "Audio processing will be disabled without a configured output device." 00760 "<ul>" 00761 "<li>" 00762 "<b>Continue</b> without any outputs." 00763 "</li>" 00764 "<li>" 00765 "<b>Reconfigure</b> Mixxx's sound device settings." 00766 "</li>" 00767 "<li>" 00768 "<b>Exit</b> Mixxx." 00769 "</li>" 00770 "</ul></html>" 00771 ); 00772 00773 QPushButton *continueButton = msgBox.addButton(tr("Continue"), QMessageBox::ActionRole); 00774 QPushButton *reconfigureButton = msgBox.addButton(tr("Reconfigure"), QMessageBox::ActionRole); 00775 QPushButton *exitButton = msgBox.addButton(tr("Exit"), QMessageBox::ActionRole); 00776 00777 while (true) 00778 { 00779 msgBox.exec(); 00780 00781 if (msgBox.clickedButton() == continueButton) { 00782 *continueClicked = true; 00783 return 0; 00784 } else if (msgBox.clickedButton() == reconfigureButton) { 00785 msgBox.hide(); 00786 m_pSoundManager->queryDevices(); 00787 00788 // This way of opening the dialog allows us to use it synchronously 00789 m_pPrefDlg->setWindowModality(Qt::ApplicationModal); 00790 m_pPrefDlg->exec(); 00791 if ( m_pPrefDlg->result() == QDialog::Accepted) { 00792 m_pSoundManager->queryDevices(); 00793 return 0; 00794 } 00795 00796 msgBox.show(); 00797 00798 } else if (msgBox.clickedButton() == exitButton) { 00799 return 1; 00800 } 00801 } 00802 } 00803 00805 void MixxxApp::initActions() 00806 { 00807 m_pFileLoadSongPlayer1 = new QAction(tr("Load Song (Player &1)..."), this); 00808 m_pFileLoadSongPlayer1->setShortcut(tr("Ctrl+O")); 00809 m_pFileLoadSongPlayer1->setShortcutContext(Qt::ApplicationShortcut); 00810 00811 m_pFileLoadSongPlayer2 = new QAction(tr("Load Song (Player &2)..."), this); 00812 m_pFileLoadSongPlayer2->setShortcut(tr("Ctrl+Shift+O")); 00813 m_pFileLoadSongPlayer2->setShortcutContext(Qt::ApplicationShortcut); 00814 00815 m_pFileQuit = new QAction(tr("&Exit"), this); 00816 m_pFileQuit->setShortcut(tr("Ctrl+Q")); 00817 m_pFileQuit->setShortcutContext(Qt::ApplicationShortcut); 00818 00819 m_pLibraryRescan = new QAction(tr("&Rescan Library"), this); 00820 00821 m_pPlaylistsNew = new QAction(tr("Add &new playlist"), this); 00822 m_pPlaylistsNew->setShortcut(tr("Ctrl+N")); 00823 m_pPlaylistsNew->setShortcutContext(Qt::ApplicationShortcut); 00824 00825 m_pCratesNew = new QAction(tr("Add new &crate"), this); 00826 m_pCratesNew->setShortcut(tr("Ctrl+C")); 00827 m_pCratesNew->setShortcutContext(Qt::ApplicationShortcut); 00828 00829 m_pPlaylistsImport = new QAction(tr("&Import playlist"), this); 00830 m_pPlaylistsImport->setShortcut(tr("Ctrl+I")); 00831 m_pPlaylistsImport->setShortcutContext(Qt::ApplicationShortcut); 00832 00833 m_pOptionsBeatMark = new QAction(tr("&Audio Beat Marks"), this); 00834 00835 m_pOptionsFullScreen = new QAction(tr("&Full Screen"), this); 00836 00837 #ifdef __APPLE__ 00838 m_pOptionsFullScreen->setShortcut(tr("Ctrl+Shift+F")); 00839 #else 00840 m_pOptionsFullScreen->setShortcut(tr("F11")); 00841 #endif 00842 00843 m_pOptionsFullScreen->setShortcutContext(Qt::ApplicationShortcut); 00844 // QShortcut * shortcut = new QShortcut(QKeySequence(tr("Esc")), this); 00845 // connect(shortcut, SIGNAL(triggered()), this, SLOT(slotQuitFullScreen())); 00846 00847 m_pOptionsPreferences = new QAction(tr("&Preferences"), this); 00848 m_pOptionsPreferences->setShortcut(tr("Ctrl+P")); 00849 m_pOptionsPreferences->setShortcutContext(Qt::ApplicationShortcut); 00850 00851 m_pHelpAboutApp = new QAction(tr("&About"), this); 00852 m_pHelpSupport = new QAction(tr("&Community Support"), this); 00853 m_pHelpManual = new QAction(tr("&User Manual"), this); 00854 m_pHelpFeedback = new QAction(tr("Send Us &Feedback"), this); 00855 m_pHelpTranslation = new QAction(tr("&Translate this application"), this); 00856 00857 #ifdef __VINYLCONTROL__ 00858 m_pOptionsVinylControl = new QAction(tr("Enable Vinyl Control &1"), this); 00859 m_pOptionsVinylControl->setShortcut(tr("Ctrl+Y")); 00860 m_pOptionsVinylControl->setShortcutContext(Qt::ApplicationShortcut); 00861 00862 m_pOptionsVinylControl2 = new QAction(tr("Enable Vinyl Control &2"), this); 00863 m_pOptionsVinylControl2->setShortcut(tr("Ctrl+U")); 00864 m_pOptionsVinylControl2->setShortcutContext(Qt::ApplicationShortcut); 00865 #endif 00866 00867 #ifdef __SHOUTCAST__ 00868 m_pOptionsShoutcast = new QAction(tr("Enable live &broadcasting"), this); 00869 m_pOptionsShoutcast->setShortcut(tr("Ctrl+L")); 00870 m_pOptionsShoutcast->setShortcutContext(Qt::ApplicationShortcut); 00871 #endif 00872 00873 m_pOptionsRecord = new QAction(tr("&Record Mix"), this); 00874 m_pOptionsRecord->setShortcut(tr("Ctrl+R")); 00875 m_pOptionsRecord->setShortcutContext(Qt::ApplicationShortcut); 00876 00877 #ifdef __SCRIPT__ 00878 macroStudio = new QAction(tr("Show Studio"), this); 00879 #endif 00880 00881 m_pFileLoadSongPlayer1->setStatusTip(tr("Opens a song in player 1")); 00882 m_pFileLoadSongPlayer1->setWhatsThis( 00883 tr("Open\n\nOpens a song in player 1")); 00884 connect(m_pFileLoadSongPlayer1, SIGNAL(triggered()), 00885 this, SLOT(slotFileLoadSongPlayer1())); 00886 00887 m_pFileLoadSongPlayer2->setStatusTip(tr("Opens a song in player 2")); 00888 m_pFileLoadSongPlayer2->setWhatsThis( 00889 tr("Open\n\nOpens a song in player 2")); 00890 connect(m_pFileLoadSongPlayer2, SIGNAL(triggered()), 00891 this, SLOT(slotFileLoadSongPlayer2())); 00892 00893 m_pFileQuit->setStatusTip(tr("Quits the application")); 00894 m_pFileQuit->setWhatsThis(tr("Exit\n\nQuits the application")); 00895 connect(m_pFileQuit, SIGNAL(triggered()), this, SLOT(slotFileQuit())); 00896 00897 m_pLibraryRescan->setStatusTip(tr("Rescans the song library")); 00898 m_pLibraryRescan->setWhatsThis( 00899 tr("Rescan library\n\nRescans the song library")); 00900 m_pLibraryRescan->setCheckable(false); 00901 connect(m_pLibraryRescan, SIGNAL(triggered()), 00902 this, SLOT(slotScanLibrary())); 00903 connect(m_pLibraryScanner, SIGNAL(scanFinished()), 00904 this, SLOT(slotEnableRescanLibraryAction())); 00905 00906 m_pPlaylistsNew->setStatusTip(tr("Create a new playlist")); 00907 m_pPlaylistsNew->setWhatsThis(tr("New playlist\n\nCreate a new playlist")); 00908 connect(m_pPlaylistsNew, SIGNAL(triggered()), 00909 m_pLibrary, SLOT(slotCreatePlaylist())); 00910 00911 m_pCratesNew->setStatusTip(tr("Create a new crate")); 00912 m_pCratesNew->setWhatsThis(tr("New crate\n\nCreate a new crate.")); 00913 connect(m_pCratesNew, SIGNAL(triggered()), 00914 m_pLibrary, SLOT(slotCreateCrate())); 00915 00916 m_pPlaylistsImport->setStatusTip(tr("Import playlist")); 00917 m_pPlaylistsImport->setWhatsThis(tr("Import playlist")); 00918 //connect(playlistsImport, SIGNAL(triggered()), 00919 // m_pTrack, SLOT(slotImportPlaylist())); 00920 //FIXME: Disabled due to library rework 00921 00922 m_pOptionsBeatMark->setCheckable(false); 00923 m_pOptionsBeatMark->setChecked(false); 00924 m_pOptionsBeatMark->setStatusTip(tr("Audio Beat Marks")); 00925 m_pOptionsBeatMark->setWhatsThis( 00926 tr("Audio Beat Marks\nMark beats by audio clicks")); 00927 connect(m_pOptionsBeatMark, SIGNAL(toggled(bool)), 00928 this, SLOT(slotOptionsBeatMark(bool))); 00929 00930 #ifdef __VINYLCONTROL__ 00931 // Either check or uncheck the vinyl control menu item depending on what 00932 // it was saved as. 00933 m_pOptionsVinylControl->setCheckable(true); 00934 m_pOptionsVinylControl->setChecked(false); 00935 m_pOptionsVinylControl->setStatusTip(tr("Activate Vinyl Control")); 00936 m_pOptionsVinylControl->setWhatsThis( 00937 tr("Use timecoded vinyls on external turntables to control Mixxx")); 00938 connect(m_pOptionsVinylControl, SIGNAL(toggled(bool)), this, 00939 SLOT(slotCheckboxVinylControl(bool))); 00940 00941 ControlObjectThreadMain* enabled1 = new ControlObjectThreadMain( 00942 ControlObject::getControl(ConfigKey("[Channel1]", "vinylcontrol_enabled")),this); 00943 connect(enabled1, SIGNAL(valueChanged(double)), this, 00944 SLOT(slotControlVinylControl(double))); 00945 00946 m_pOptionsVinylControl2->setCheckable(true); 00947 m_pOptionsVinylControl2->setChecked(false); 00948 m_pOptionsVinylControl2->setStatusTip(tr("Activate Vinyl Control")); 00949 m_pOptionsVinylControl2->setWhatsThis( 00950 tr("Use timecoded vinyls on external turntables to control Mixxx")); 00951 connect(m_pOptionsVinylControl2, SIGNAL(toggled(bool)), this, 00952 SLOT(slotCheckboxVinylControl2(bool))); 00953 00954 ControlObjectThreadMain* enabled2 = new ControlObjectThreadMain( 00955 ControlObject::getControl(ConfigKey("[Channel2]", "vinylcontrol_enabled")),this); 00956 connect(enabled2, SIGNAL(valueChanged(double)), this, 00957 SLOT(slotControlVinylControl2(double))); 00958 #endif 00959 00960 #ifdef __SHOUTCAST__ 00961 m_pOptionsShoutcast->setCheckable(true); 00962 bool broadcastEnabled = 00963 (m_pConfig->getValueString(ConfigKey("[Shoutcast]", "enabled")) 00964 .toInt() == 1); 00965 00966 m_pOptionsShoutcast->setChecked(broadcastEnabled); 00967 00968 m_pOptionsShoutcast->setStatusTip(tr("Activate live broadcasting")); 00969 m_pOptionsShoutcast->setWhatsThis( 00970 tr("Stream your mixes to a shoutcast or icecast server")); 00971 00972 connect(m_pOptionsShoutcast, SIGNAL(toggled(bool)), 00973 this, SLOT(slotOptionsShoutcast(bool))); 00974 #endif 00975 00976 m_pOptionsRecord->setCheckable(true); 00977 m_pOptionsRecord->setStatusTip(tr("Start Recording your Mix")); 00978 m_pOptionsRecord->setWhatsThis(tr("Record your mix to a file")); 00979 connect(m_pOptionsRecord, SIGNAL(toggled(bool)), 00980 this, SLOT(slotOptionsRecord(bool))); 00981 00982 m_pOptionsFullScreen->setCheckable(true); 00983 m_pOptionsFullScreen->setChecked(false); 00984 m_pOptionsFullScreen->setStatusTip(tr("Full Screen")); 00985 m_pOptionsFullScreen->setWhatsThis( 00986 tr("Display Mixxx using the full screen")); 00987 connect(m_pOptionsFullScreen, SIGNAL(toggled(bool)), 00988 this, SLOT(slotOptionsFullScreen(bool))); 00989 00990 m_pOptionsPreferences->setStatusTip(tr("Preferences")); 00991 m_pOptionsPreferences->setWhatsThis( 00992 tr("Preferences\nPlayback and MIDI preferences")); 00993 connect(m_pOptionsPreferences, SIGNAL(triggered()), 00994 this, SLOT(slotOptionsPreferences())); 00995 00996 m_pHelpSupport->setStatusTip(tr("Support...")); 00997 m_pHelpSupport->setWhatsThis(tr("Support\n\nGet help with Mixxx")); 00998 connect(m_pHelpSupport, SIGNAL(triggered()), this, SLOT(slotHelpSupport())); 00999 01000 m_pHelpManual->setStatusTip(tr("Read the Mixxx user manual.")); 01001 m_pHelpManual->setWhatsThis(tr("Support\n\nRead the Mixxx user manual.")); 01002 connect(m_pHelpManual, SIGNAL(triggered()), this, SLOT(slotHelpManual())); 01003 01004 m_pHelpFeedback->setStatusTip(tr("Send feedback to the Mixxx team.")); 01005 m_pHelpFeedback->setWhatsThis(tr("Support\n\nSend feedback to the Mixxx team.")); 01006 connect(m_pHelpFeedback, SIGNAL(triggered()), this, SLOT(slotHelpFeedback())); 01007 01008 m_pHelpTranslation->setStatusTip(tr("Help translate this application into your language.")); 01009 m_pHelpTranslation->setWhatsThis(tr("Support\n\nHelp translate this application into your language.")); 01010 connect(m_pHelpTranslation, SIGNAL(triggered()), this, SLOT(slotHelpTranslation())); 01011 01012 m_pHelpAboutApp->setStatusTip(tr("About the application")); 01013 m_pHelpAboutApp->setWhatsThis(tr("About\n\nAbout the application")); 01014 connect(m_pHelpAboutApp, SIGNAL(triggered()), this, SLOT(slotHelpAbout())); 01015 01016 #ifdef __SCRIPT__ 01017 macroStudio->setStatusTip(tr("Shows the macro studio window")); 01018 macroStudio->setWhatsThis( 01019 tr("Show Studio\n\nMakes the macro studio visible")); 01020 connect(macroStudio, SIGNAL(triggered()), 01021 scriptEng->getStudio(), SLOT(showStudio())); 01022 #endif 01023 } 01024 01025 void MixxxApp::initMenuBar() 01026 { 01027 // MENUBAR 01028 m_pFileMenu = new QMenu(tr("&File"), menuBar()); 01029 m_pOptionsMenu = new QMenu(tr("&Options"), menuBar()); 01030 m_pLibraryMenu = new QMenu(tr("&Library"),menuBar()); 01031 m_pViewMenu = new QMenu(tr("&View"), menuBar()); 01032 m_pHelpMenu = new QMenu(tr("&Help"), menuBar()); 01033 #ifdef __SCRIPT__ 01034 macroMenu=new QMenu(tr("&Macro"), menuBar()); 01035 #endif 01036 connect(m_pOptionsMenu, SIGNAL(aboutToShow()), 01037 this, SLOT(slotOptionsMenuShow())); 01038 // menuBar entry fileMenu 01039 m_pFileMenu->addAction(m_pFileLoadSongPlayer1); 01040 m_pFileMenu->addAction(m_pFileLoadSongPlayer2); 01041 m_pFileMenu->addSeparator(); 01042 m_pFileMenu->addAction(m_pFileQuit); 01043 01044 // menuBar entry optionsMenu 01045 //optionsMenu->setCheckable(true); 01046 // optionsBeatMark->addTo(optionsMenu); 01047 #ifdef __VINYLCONTROL__ 01048 m_pVinylControlMenu = new QMenu(tr("&Vinyl Control"), menuBar()); 01049 m_pVinylControlMenu->addAction(m_pOptionsVinylControl); 01050 m_pVinylControlMenu->addAction(m_pOptionsVinylControl2); 01051 m_pOptionsMenu->addMenu(m_pVinylControlMenu); 01052 #endif 01053 m_pOptionsMenu->addAction(m_pOptionsRecord); 01054 #ifdef __SHOUTCAST__ 01055 m_pOptionsMenu->addAction(m_pOptionsShoutcast); 01056 #endif 01057 m_pOptionsMenu->addAction(m_pOptionsFullScreen); 01058 m_pOptionsMenu->addSeparator(); 01059 m_pOptionsMenu->addAction(m_pOptionsPreferences); 01060 01061 // libraryMenu->setCheckable(true); 01062 m_pLibraryMenu->addAction(m_pLibraryRescan); 01063 m_pLibraryMenu->addSeparator(); 01064 m_pLibraryMenu->addAction(m_pPlaylistsNew); 01065 m_pLibraryMenu->addAction(m_pCratesNew); 01066 //libraryMenu->addAction(playlistsImport); 01067 01068 // menuBar entry viewMenu 01069 //viewMenu->setCheckable(true); 01070 01071 // menuBar entry helpMenu 01072 m_pHelpMenu->addAction(m_pHelpSupport); 01073 m_pHelpMenu->addAction(m_pHelpManual); 01074 m_pHelpMenu->addAction(m_pHelpFeedback); 01075 m_pHelpMenu->addAction(m_pHelpTranslation); 01076 m_pHelpMenu->addSeparator(); 01077 m_pHelpMenu->addAction(m_pHelpAboutApp); 01078 01079 01080 #ifdef __SCRIPT__ 01081 macroMenu->addAction(macroStudio); 01082 #endif 01083 01084 menuBar()->addMenu(m_pFileMenu); 01085 menuBar()->addMenu(m_pLibraryMenu); 01086 menuBar()->addMenu(m_pOptionsMenu); 01087 01088 // menuBar()->addMenu(viewMenu); 01089 #ifdef __SCRIPT__ 01090 menuBar()->addMenu(macroMenu); 01091 #endif 01092 menuBar()->addSeparator(); 01093 menuBar()->addMenu(m_pHelpMenu); 01094 01095 m_NativeMenuBarSupport = menuBar()->isNativeMenuBar(); 01096 } 01097 01098 void MixxxApp::slotlibraryMenuAboutToShow(){ 01099 } 01100 01101 bool MixxxApp::queryExit() 01102 { 01103 int exit=QMessageBox::information(this, tr("Quit..."), 01104 tr("Do your really want to quit?"), 01105 QMessageBox::Ok, QMessageBox::Cancel); 01106 01107 if (exit==1) 01108 { 01109 } 01110 else 01111 { 01112 }; 01113 01114 return (exit==1); 01115 } 01116 01117 void MixxxApp::slotFileLoadSongPlayer1() 01118 { 01119 ControlObject* play = 01120 ControlObject::getControl(ConfigKey("[Channel1]", "play")); 01121 01122 if (play->get() == 1.) 01123 { 01124 int ret = QMessageBox::warning(this, tr("Mixxx"), 01125 tr("Player 1 is currently playing a song.\n" 01126 "Are you sure you want to load a new song?"), 01127 QMessageBox::Yes | QMessageBox::No, 01128 QMessageBox::No); 01129 01130 if (ret != QMessageBox::Yes) 01131 return; 01132 } 01133 01134 QString s = 01135 QFileDialog::getOpenFileName( 01136 this, 01137 tr("Load Song into Player 1"), 01138 m_pConfig->getValueString(ConfigKey("[Playlist]", "Directory")), 01139 QString("Audio (%1)") 01140 .arg(SoundSourceProxy::supportedFileExtensionsString())); 01141 01142 if (s != QString::null) { 01143 m_pPlayerManager->slotLoadToDeck(s, 1); 01144 } 01145 } 01146 01147 void MixxxApp::slotFileLoadSongPlayer2() 01148 { 01149 ControlObject* play = 01150 ControlObject::getControl(ConfigKey("[Channel2]", "play")); 01151 01152 if (play->get() == 1.) 01153 { 01154 int ret = QMessageBox::warning(this, tr("Mixxx"), 01155 tr("Player 2 is currently playing a song.\n" 01156 "Are you sure you want to load a new song?"), 01157 QMessageBox::Yes | QMessageBox::No, 01158 QMessageBox::No); 01159 01160 if (ret != QMessageBox::Yes) 01161 return; 01162 } 01163 01164 QString s = 01165 QFileDialog::getOpenFileName( 01166 this, 01167 tr("Load Song into Player 2"), 01168 m_pConfig->getValueString(ConfigKey("[Playlist]", "Directory")), 01169 QString("Audio (%1)") 01170 .arg(SoundSourceProxy::supportedFileExtensionsString())); 01171 01172 if (s != QString::null) { 01173 m_pPlayerManager->slotLoadToDeck(s, 2); 01174 } 01175 } 01176 01177 void MixxxApp::slotFileQuit() 01178 { 01179 if (!confirmExit()) { 01180 return; 01181 } 01182 hide(); 01183 qApp->quit(); 01184 } 01185 01186 void MixxxApp::slotOptionsBeatMark(bool) 01187 { 01188 // BEAT MARK STUFF 01189 } 01190 01191 void MixxxApp::slotOptionsFullScreen(bool toggle) 01192 { 01193 if (m_pOptionsFullScreen) 01194 m_pOptionsFullScreen->setChecked(toggle); 01195 01196 if (isFullScreen() == toggle) { 01197 return; 01198 } 01199 01200 if (toggle) { 01201 #if defined(__LINUX__) || defined(__APPLE__) 01202 // this and the later move(m_winpos) doesn't seem necessary 01203 // here on kwin, if it's necessary with some other x11 wm, re-enable 01204 // it, I guess -bkgood 01205 //m_winpos = pos(); 01206 // fix some x11 silliness -- for some reason the move(m_winpos) 01207 // is moving the currentWindow to (0, 0), not the frame (as it's 01208 // supposed to, I might add) 01209 // if this messes stuff up on your distro yell at me -bkgood 01210 //m_winpos.setX(m_winpos.x() + (geometry().x() - x())); 01211 //m_winpos.setY(m_winpos.y() + (geometry().y() - y())); 01212 #endif 01213 menuBar()->setNativeMenuBar(false); 01214 showFullScreen(); 01215 } else { 01216 showNormal(); 01217 menuBar()->setNativeMenuBar(m_NativeMenuBarSupport); 01218 #ifdef __LINUX__ 01219 //move(m_winpos); 01220 #endif 01221 } 01222 } 01223 01224 void MixxxApp::slotOptionsPreferences() 01225 { 01226 m_pPrefDlg->setHidden(false); 01227 m_pPrefDlg->activateWindow(); 01228 } 01229 01230 void MixxxApp::slotControlVinylControl(double toggle) 01231 { 01232 #ifdef __VINYLCONTROL__ 01233 if (m_pVCManager->vinylInputEnabled(1)) { 01234 m_pOptionsVinylControl->setChecked((bool)toggle); 01235 } else { 01236 m_pOptionsVinylControl->setChecked(false); 01237 if (toggle) { 01238 QMessageBox::warning(this, tr("Mixxx"), 01239 tr("No input device(s) select.\nPlease select your soundcard(s) " 01240 "in the sound hardware preferences."), 01241 QMessageBox::Ok, 01242 QMessageBox::Ok); 01243 m_pPrefDlg->show(); 01244 m_pPrefDlg->showSoundHardwarePage(); 01245 ControlObject::getControl(ConfigKey("[Channel1]", "vinylcontrol_status"))->set(VINYL_STATUS_DISABLED); 01246 ControlObject::getControl(ConfigKey("[Channel1]", "vinylcontrol_enabled"))->set(0); 01247 } 01248 } 01249 #endif 01250 } 01251 01252 void MixxxApp::slotCheckboxVinylControl(bool toggle) 01253 { 01254 #ifdef __VINYLCONTROL__ 01255 ControlObject::getControl(ConfigKey("[Channel1]", "vinylcontrol_enabled"))->set((double)toggle); 01256 #endif 01257 } 01258 01259 void MixxxApp::slotControlVinylControl2(double toggle) 01260 { 01261 #ifdef __VINYLCONTROL__ 01262 if (m_pVCManager->vinylInputEnabled(2)) { 01263 m_pOptionsVinylControl2->setChecked((bool)toggle); 01264 } else { 01265 m_pOptionsVinylControl2->setChecked(false); 01266 if (toggle) { 01267 QMessageBox::warning(this, tr("Mixxx"), 01268 tr("No input device(s) select.\nPlease select your soundcard(s) " 01269 "in the sound hardware preferences."), 01270 QMessageBox::Ok, 01271 QMessageBox::Ok); 01272 m_pPrefDlg->show(); 01273 m_pPrefDlg->showSoundHardwarePage(); 01274 ControlObject::getControl(ConfigKey("[Channel2]", "vinylcontrol_status"))->set(VINYL_STATUS_DISABLED); 01275 ControlObject::getControl(ConfigKey("[Channel2]", "vinylcontrol_enabled"))->set(0); 01276 } 01277 } 01278 #endif 01279 } 01280 01281 void MixxxApp::slotCheckboxVinylControl2(bool toggle) 01282 { 01283 #ifdef __VINYLCONTROL__ 01284 ControlObject::getControl(ConfigKey("[Channel2]", "vinylcontrol_enabled"))->set((double)toggle); 01285 #endif 01286 } 01287 01288 //Also can't ifdef this (MOC again) 01289 void MixxxApp::slotOptionsRecord(bool toggle) 01290 { 01291 //Only start recording if checkbox was set to true and recording is inactive 01292 if(toggle && !m_pRecordingManager->isRecordingActive()) //start recording 01293 m_pRecordingManager->startRecording(); 01294 //Only stop recording if checkbox was set to false and recording is active 01295 else if(!toggle && m_pRecordingManager->isRecordingActive()) 01296 m_pRecordingManager->stopRecording(); 01297 } 01298 01299 void MixxxApp::slotHelpAbout() { 01300 QString buildBranch, buildRevision; 01301 #ifdef BUILD_BRANCH 01302 buildBranch = BUILD_BRANCH; 01303 #endif 01304 #ifdef BUILD_REV 01305 buildRevision = BUILD_REV; 01306 #endif 01307 DlgAbout *about = new DlgAbout(this); 01308 01309 QStringList version; 01310 version.append(VERSION); 01311 if (!buildBranch.isEmpty() || !buildRevision.isEmpty()) { 01312 QStringList buildInfo; 01313 buildInfo.append("build"); 01314 if (!buildBranch.isEmpty()) { 01315 buildInfo.append(buildBranch); 01316 } 01317 if (!buildRevision.isEmpty()) { 01318 buildInfo.append(QString("r%1").arg(buildRevision)); 01319 } 01320 version.append(QString("(%1)").arg(buildInfo.join(" "))); 01321 } 01322 about->version_label->setText(version.join(" ")); 01323 01324 QString s_devTeam=QString(tr("Mixxx %1 Development Team")).arg(VERSION); 01325 QString s_contributions=tr("With contributions from:"); 01326 QString s_specialThanks=tr("And special thanks to:"); 01327 QString s_pastDevs=tr("Past Developers"); 01328 QString s_pastContribs=tr("Past Contributors"); 01329 01330 QString credits = 01331 QString("<p align=\"center\"><b>%1</b></p>" 01332 "<p align=\"center\">" 01333 "Adam Davison<br>" 01334 "Albert Santoni<br>" 01335 "RJ Ryan<br>" 01336 "Garth Dahlstrom<br>" 01337 "Sean Pappalardo<br>" 01338 "Phillip Whelan<br>" 01339 "Tobias Rafreider<br>" 01340 "S. Brandt<br>" 01341 "Bill Good<br>" 01342 "Owen Williams<br>" 01343 "Vittorio Colao<br>" 01344 01345 "</p>" 01346 "<p align=\"center\"><b>%2</b></p>" 01347 "<p align=\"center\">" 01348 "Mark Hills<br>" 01349 "Andre Roth<br>" 01350 "Robin Sheat<br>" 01351 "Mark Glines<br>" 01352 "Mathieu Rene<br>" 01353 "Miko Kiiski<br>" 01354 "Brian Jackson<br>" 01355 "Andreas Pflug<br>" 01356 "Bas van Schaik<br>" 01357 "Ján Jockusch<br>" 01358 "Oliver Stöneberg<br>" 01359 "Jan Jockusch<br>" 01360 "C. Stewart<br>" 01361 "Bill Egert<br>" 01362 "Zach Shutters<br>" 01363 "Owen Bullock<br>" 01364 "Graeme Mathieson<br>" 01365 "Sebastian Actist<br>" 01366 "Jussi Sainio<br>" 01367 "David Gnedt<br>" 01368 "Antonio Passamani<br>" 01369 "Guy Martin<br>" 01370 "Anders Gunnarsson<br>" 01371 "Alex Barker<br>" 01372 "Mikko Jania<br>" 01373 "Juan Pedro Bolívar Puente<br>" 01374 "Linus Amvall<br>" 01375 "Irwin Céspedes B<br>" 01376 "Micz Flor<br>" 01377 "Daniel James<br>" 01378 "Mika Haulo<br>" 01379 "Matthew Mikolay<br>" 01380 "Tom Mast<br>" 01381 "Miko Kiiski<br>" 01382 "Vinícius Dias dos Santos<br>" 01383 "Joe Colosimo<br>" 01384 "Shashank Kumar<br>" 01385 "Till Hofmann<br>" 01386 "Daniel Schürmann<br>" 01387 "Peter Vágner<br>" 01388 "Thanasis Liappis<br>" 01389 "Jens Nachtigall<br>" 01390 "Scott Ullrich<br>" 01391 "Jonas Ådahl<br>" 01392 "Maxime Bochon<br>" 01393 "Akash Shetye<br>" 01394 01395 "</p>" 01396 "<p align=\"center\"><b>%3</b></p>" 01397 "<p align=\"center\">" 01398 "Vestax<br>" 01399 "Stanton<br>" 01400 "Hercules<br>" 01401 "EKS<br>" 01402 "Echo Digital Audio<br>" 01403 "JP Disco<br>" 01404 "Adam Bellinson<br>" 01405 "Alexandre Bancel<br>" 01406 "Melanie Thielker<br>" 01407 "Julien Rosener<br>" 01408 "Pau Arumí<br>" 01409 "David Garcia<br>" 01410 "Seb Ruiz<br>" 01411 "Joseph Mattiello<br>" 01412 "</p>" 01413 01414 "<p align=\"center\"><b>%4</b></p>" 01415 "<p align=\"center\">" 01416 "Tue Haste Andersen<br>" 01417 "Ken Haste Andersen<br>" 01418 "Cedric Gestes<br>" 01419 "John Sully<br>" 01420 "Torben Hohn<br>" 01421 "Peter Chang<br>" 01422 "Micah Lee<br>" 01423 "Ben Wheeler<br>" 01424 "Wesley Stessens<br>" 01425 "Nathan Prado<br>" 01426 "Zach Elko<br>" 01427 "Tom Care<br>" 01428 "Pawel Bartkiewicz<br>" 01429 "Nick Guenther<br>" 01430 "Bruno Buccolo<br>" 01431 "Ryan Baker<br>" 01432 "</p>" 01433 01434 "<p align=\"center\"><b>%5</b></p>" 01435 "<p align=\"center\">" 01436 "Ludek Horácek<br>" 01437 "Svein Magne Bang<br>" 01438 "Kristoffer Jensen<br>" 01439 "Ingo Kossyk<br>" 01440 "Mads Holm<br>" 01441 "Lukas Zapletal<br>" 01442 "Jeremie Zimmermann<br>" 01443 "Gianluca Romanin<br>" 01444 "Tim Jackson<br>" 01445 "Stefan Langhammer<br>" 01446 "Frank Willascheck<br>" 01447 "Jeff Nelson<br>" 01448 "Kevin Schaper<br>" 01449 "Alex Markley<br>" 01450 "Oriol Puigbó<br>" 01451 "Ulrich Heske<br>" 01452 "James Hagerman<br>" 01453 "quil0m80<br>" 01454 "Martin Sakmár<br>" 01455 "Ilian Persson<br>" 01456 "Dave Jarvis<br>" 01457 "Thomas Baag<br>" 01458 "Karlis Kalnins<br>" 01459 "Amias Channer<br>" 01460 "Sacha Berger<br>" 01461 "James Evans<br>" 01462 "Martin Sakmar<br>" 01463 "Navaho Gunleg<br>" 01464 "Gavin Pryke<br>" 01465 "Michael Pujos<br>" 01466 "Claudio Bantaloukas<br>" 01467 "Pavol Rusnak<br>" 01468 "</p>").arg(s_devTeam,s_contributions,s_specialThanks,s_pastDevs,s_pastContribs); 01469 01470 about->textBrowser->setHtml(credits); 01471 about->show(); 01472 01473 } 01474 01475 void MixxxApp::slotHelpSupport() { 01476 QUrl qSupportURL; 01477 qSupportURL.setUrl(MIXXX_SUPPORT_URL); 01478 QDesktopServices::openUrl(qSupportURL); 01479 } 01480 01481 void MixxxApp::slotHelpFeedback() { 01482 QUrl qFeedbackUrl; 01483 qFeedbackUrl.setUrl(MIXXX_FEEDBACK_URL); 01484 QDesktopServices::openUrl(qFeedbackUrl); 01485 } 01486 01487 void MixxxApp::slotHelpTranslation() { 01488 QUrl qTranslationUrl; 01489 qTranslationUrl.setUrl(MIXXX_TRANSLATION_URL); 01490 QDesktopServices::openUrl(qTranslationUrl); 01491 } 01492 01493 void MixxxApp::slotHelpManual() { 01494 QDir configDir(m_pConfig->getConfigPath()); 01495 // Default to the mixxx.org hosted version of the manual. 01496 QUrl qManualUrl(MIXXX_MANUAL_URL); 01497 #if defined(__APPLE__) 01498 // We don't include the PDF manual in the bundle on OSX. Default to the 01499 // web-hosted version. 01500 #elif defined(__WINDOWS__) 01501 // On Windows, the manual PDF sits in the same folder as the 'skins' folder. 01502 if (configDir.exists(MIXXX_MANUAL_FILENAME)) { 01503 qManualUrl = QUrl::fromLocalFile( 01504 configDir.absoluteFilePath(MIXXX_MANUAL_FILENAME)); 01505 } 01506 #elif defined(__LINUX__) 01507 // On GNU/Linux, the manual is installed to e.g. /usr/share/mixxx/doc/ 01508 configDir.cd("doc"); 01509 if (configDir.exists(MIXXX_MANUAL_FILENAME)) { 01510 qManualUrl = QUrl::fromLocalFile( 01511 configDir.absoluteFilePath(MIXXX_MANUAL_FILENAME)); 01512 } 01513 #else 01514 // No idea, default to the mixxx.org hosted version. 01515 #endif 01516 QDesktopServices::openUrl(qManualUrl); 01517 } 01518 01519 void MixxxApp::rebootMixxxView() { 01520 01521 if (!m_pWidgetParent || !m_pView) 01522 return; 01523 01524 qDebug() << "Now in Rebootmixxview..."; 01525 01526 // Workaround for changing skins while fullscreen, just go out of fullscreen 01527 // mode. If you change skins while in fullscreen (on Linux, at least) the 01528 // window returns to 0,0 but and the backdrop disappears so it looks as if 01529 // it is not fullscreen, but acts as if it is. 01530 slotOptionsFullScreen(false); 01531 01532 // TODO(XXX) Make getSkinPath not public 01533 QString qSkinPath = m_pSkinLoader->getConfiguredSkinPath(); 01534 01535 QWidget* pNewView = new QFrame(); 01536 01537 // assignment in next line intentional 01538 if (!(m_pWidgetParent = m_pSkinLoader->loadDefaultSkin(pNewView, 01539 m_pKeyboard, 01540 m_pPlayerManager, 01541 m_pLibrary, 01542 m_pVCManager))) { 01543 qDebug() << "Could not reload the skin."; 01544 } 01545 01546 // don't move this before loadDefaultSkin above. bug 521509 --bkgood 01547 // this hides and deletes the old CentralWidget 01548 setCentralWidget(pNewView); 01549 01550 m_pView = pNewView; 01551 01552 // keep gui centered (esp for fullscreen) 01553 // the layout will be deleted whenever m_pView gets deleted 01554 QHBoxLayout *pLayout = new QHBoxLayout(m_pView); 01555 pLayout->addWidget(m_pWidgetParent); 01556 pLayout->setContentsMargins(0, 0, 0, 0); // don't want margins 01557 01558 // if we move from big skin to smaller skin, size the window down to fit 01559 // (qt scales up for us if we go the other way) -bkgood 01560 // this doesn't always seem to snap down tight on Windows... sigh -bkgood 01561 setFixedSize(m_pView->width(), m_pView->height()); 01562 setFixedSize(QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); 01563 01564 // Set native menu bar. Fixes issue on OSX where menu bar went away after a 01565 // skin change. 01566 #if __OSX__ 01567 menuBar()->setNativeMenuBar(m_NativeMenuBarSupport); 01568 #endif 01569 qDebug() << "rebootgui DONE"; 01570 } 01571 01576 bool MixxxApp::eventFilter(QObject *obj, QEvent *event) 01577 { 01578 static int tooltips = 01579 m_pConfig->getValueString(ConfigKey("[Controls]", "Tooltips")).toInt(); 01580 01581 if (event->type() == QEvent::ToolTip) { 01582 // QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); 01583 // unused, remove? TODO(bkgood) 01584 if (tooltips == 1) 01585 return false; 01586 else 01587 return true; 01588 } else { 01589 // standard event processing 01590 return QObject::eventFilter(obj, event); 01591 } 01592 } 01593 01594 void MixxxApp::closeEvent(QCloseEvent *event) { 01595 if (!confirmExit()) { 01596 event->ignore(); 01597 } 01598 } 01599 01600 void MixxxApp::slotScanLibrary() 01601 { 01602 m_pLibraryRescan->setEnabled(false); 01603 m_pLibraryScanner->scan( 01604 m_pConfig->getValueString(ConfigKey("[Playlist]", "Directory"))); 01605 } 01606 01607 void MixxxApp::slotEnableRescanLibraryAction() 01608 { 01609 m_pLibraryRescan->setEnabled(true); 01610 } 01611 01612 void MixxxApp::slotOptionsMenuShow(){ 01613 // Check recording if it is active. 01614 m_pOptionsRecord->setChecked(m_pRecordingManager->isRecordingActive()); 01615 01616 #ifdef __SHOUTCAST__ 01617 bool broadcastEnabled = 01618 (m_pConfig->getValueString(ConfigKey("[Shoutcast]", "enabled")).toInt() 01619 == 1); 01620 if (broadcastEnabled) 01621 m_pOptionsShoutcast->setChecked(true); 01622 else 01623 m_pOptionsShoutcast->setChecked(false); 01624 #endif 01625 } 01626 01627 void MixxxApp::slotOptionsShoutcast(bool value){ 01628 #ifdef __SHOUTCAST__ 01629 m_pOptionsShoutcast->setChecked(value); 01630 m_pConfig->set(ConfigKey("[Shoutcast]", "enabled"),ConfigValue(value)); 01631 #else 01632 Q_UNUSED(value); 01633 #endif 01634 } 01635 01636 void MixxxApp::checkDirectRendering() { 01637 // IF 01638 // * A waveform viewer exists 01639 // AND 01640 // * The waveform viewer is an OpenGL waveform viewer 01641 // AND 01642 // * The waveform viewer does not have direct rendering enabled. 01643 // THEN 01644 // * Warn user 01645 01646 if (WaveformViewerFactory::numViewers(WAVEFORM_GL) > 0 && 01647 !WaveformViewerFactory::isDirectRenderingEnabled() && 01648 m_pConfig->getValueString(ConfigKey("[Direct Rendering]", "Warned")) != QString("yes")) { 01649 QMessageBox::warning(0, "OpenGL Direct Rendering", 01650 "Direct rendering is not enabled on your machine.\n\nThis means that the waveform displays will be very\nslow and take a lot of CPU time. Either update your\nconfiguration to enable direct rendering, or disable\nthe waveform displays in the control panel by\nselecting \"Simple\" under waveform displays.\nNOTE: In case you run on NVidia hardware,\ndirect rendering may not be present, but you will\nnot experience a degradation in performance."); 01651 m_pConfig->set(ConfigKey("[Direct Rendering]", "Warned"), ConfigValue(QString("yes"))); 01652 } 01653 } 01654 01655 bool MixxxApp::confirmExit() { 01656 bool playing(false); 01657 bool playingSampler(false); 01658 unsigned int deckCount = m_pPlayerManager->numDecks(); 01659 unsigned int samplerCount = m_pPlayerManager->numSamplers(); 01660 for (unsigned int i = 0; i < deckCount; ++i) { 01661 ControlObject *pPlayCO( 01662 ControlObject::getControl( 01663 ConfigKey(QString("[Channel%1]").arg(i + 1), "play") 01664 ) 01665 ); 01666 if (pPlayCO && pPlayCO->get()) { 01667 playing = true; 01668 break; 01669 } 01670 } 01671 for (unsigned int i = 0; i < samplerCount; ++i) { 01672 ControlObject *pPlayCO( 01673 ControlObject::getControl( 01674 ConfigKey(QString("[Sampler%1]").arg(i + 1), "play") 01675 ) 01676 ); 01677 if (pPlayCO && pPlayCO->get()) { 01678 playingSampler = true; 01679 break; 01680 } 01681 } 01682 if (playing) { 01683 QMessageBox::StandardButton btn = QMessageBox::question(this, 01684 tr("Confirm Exit"), 01685 tr("A deck is currently playing. Exit Mixxx?"), 01686 QMessageBox::Yes | QMessageBox::No, QMessageBox::No); 01687 if (btn == QMessageBox::No) { 01688 return false; 01689 } 01690 } else if (playingSampler) { 01691 QMessageBox::StandardButton btn = QMessageBox::question(this, 01692 tr("Confirm Exit"), 01693 tr("A sampler is currently playing. Exit Mixxx?"), 01694 QMessageBox::Yes | QMessageBox::No, QMessageBox::No); 01695 if (btn == QMessageBox::No) { 01696 return false; 01697 } 01698 } 01699 return true; 01700 }