Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/mixxx.cpp

Go to the documentation of this file.
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&aacute;n Jockusch<br>"
01358 "Oliver St&ouml;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&iacute;var Puente<br>"
01374 "Linus Amvall<br>"
01375 "Irwin C&eacute;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&iacute;cius Dias dos Santos<br>"
01383 "Joe Colosimo<br>"
01384 "Shashank Kumar<br>"
01385 "Till Hofmann<br>"
01386 "Daniel Sch&uuml;rmann<br>"
01387 "Peter V&aacute;gner<br>"
01388 "Thanasis Liappis<br>"
01389 "Jens Nachtigall<br>"
01390 "Scott Ullrich<br>"
01391 "Jonas &Aring;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&iacute;<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&#225;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&oacute;<br>"
01451 "Ulrich Heske<br>"
01452 "James Hagerman<br>"
01453 "quil0m80<br>"
01454 "Martin Sakm&#225;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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines