--- qsampler/trunk/src/qsamplerMainForm.cpp 2016/08/15 19:10:16 2978 +++ qsampler/trunk/src/qsamplerMainForm.cpp 2020/10/19 17:08:56 3832 @@ -1,8 +1,8 @@ // qsamplerMainForm.cpp // /**************************************************************************** - Copyright (C) 2004-2016, rncbc aka Rui Nuno Capela. All rights reserved. - Copyright (C) 2007,2008,2015 Christian Schoenebeck + Copyright (C) 2004-2020, rncbc aka Rui Nuno Capela. All rights reserved. + Copyright (C) 2007-2019 Christian Schoenebeck This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -35,6 +35,10 @@ #include "qsamplerOptionsForm.h" #include "qsamplerDeviceStatusForm.h" +#include "qsamplerPaletteForm.h" + +#include + #include #include @@ -42,7 +46,6 @@ #include #include -#include #include #include #include @@ -58,24 +61,27 @@ #include #include -#if QT_VERSION >= 0x050000 +#include + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #endif -#if QT_VERSION < 0x040500 +#if QT_VERSION < QT_VERSION_CHECK(4, 5, 0) namespace Qt { const WindowFlags WindowCloseButtonHint = WindowFlags(0x08000000); } #endif -#ifdef HAVE_SIGNAL_H -#include -#endif - #ifdef CONFIG_LIBGIG #include #endif +// Deprecated QTextStreamFunctions/Qt namespaces workaround. +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) +#define endl Qt::endl +#endif + // Needed for lroundf() #include @@ -91,8 +97,9 @@ // All winsock apps needs this. -#if defined(WIN32) +#if defined(__WIN32__) || defined(_WIN32) || defined(WIN32) static WSADATA _wsaData; +#undef HAVE_SIGNAL_H #endif @@ -103,27 +110,38 @@ #include +#include #include #include - #include // File descriptor for SIGUSR1 notifier. -static int g_fdUsr1[2]; +static int g_fdSigusr1[2] = { -1, -1 }; // Unix SIGUSR1 signal handler. static void qsampler_sigusr1_handler ( int /* signo */ ) { char c = 1; - (::write(g_fdUsr1[0], &c, sizeof(c)) > 0); + (::write(g_fdSigusr1[0], &c, sizeof(c)) > 0); +} + +// File descriptor for SIGTERM notifier. +static int g_fdSigterm[2] = { -1, -1 }; + +// Unix SIGTERM signal handler. +static void qsampler_sigterm_handler ( int /* signo */ ) +{ + char c = 1; + + (::write(g_fdSigterm[0], &c, sizeof(c)) > 0); } #endif // HAVE_SIGNAL_H //------------------------------------------------------------------------- -// qsampler -- namespace +// QSampler -- namespace namespace QSampler { @@ -143,8 +161,7 @@ //------------------------------------------------------------------------- -// LscpEvent -- specialty for LSCP callback comunication. - +// QSampler::LscpEvent -- specialty for LSCP callback comunication. class LscpEvent : public QEvent { @@ -159,8 +176,8 @@ } // Accessors. - lscp_event_t event() { return m_event; } - QString& data() { return m_data; } + lscp_event_t event() { return m_event; } + const QString& data() { return m_data; } private: @@ -172,10 +189,30 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Main window form implementation. +// QSampler::Workspace -- Main window workspace (MDI Area) decl. + +class Workspace : public QMdiArea +{ +public: + + Workspace(MainForm *pMainForm) : QMdiArea(pMainForm) {} + +protected: + + void resizeEvent(QResizeEvent *) + { + MainForm *pMainForm = static_cast (parentWidget()); + if (pMainForm) + pMainForm->channelsArrangeAuto(); + } +}; + + +//------------------------------------------------------------------------- +// QSampler::MainForm -- Main window form implementation. // Kind of singleton reference. -MainForm* MainForm::g_pMainForm = NULL; +MainForm *MainForm::g_pMainForm = nullptr; MainForm::MainForm ( QWidget *pParent ) : QMainWindow(pParent) @@ -186,20 +223,20 @@ g_pMainForm = this; // Initialize some pointer references. - m_pOptions = NULL; + m_pOptions = nullptr; // All child forms are to be created later, not earlier than setup. - m_pMessages = NULL; - m_pInstrumentListForm = NULL; - m_pDeviceForm = NULL; + m_pMessages = nullptr; + m_pInstrumentListForm = nullptr; + m_pDeviceForm = nullptr; // We'll start clean. m_iUntitled = 0; m_iDirtySetup = 0; m_iDirtyCount = 0; - m_pServer = NULL; - m_pClient = NULL; + m_pServer = nullptr; + m_pClient = nullptr; m_iStartDelay = 0; m_iTimerDelay = 0; @@ -214,25 +251,48 @@ // LADISH Level 1 suport. // Initialize file descriptors for SIGUSR1 socket notifier. - ::socketpair(AF_UNIX, SOCK_STREAM, 0, g_fdUsr1); - m_pUsr1Notifier - = new QSocketNotifier(g_fdUsr1[1], QSocketNotifier::Read, this); + ::socketpair(AF_UNIX, SOCK_STREAM, 0, g_fdSigusr1); + m_pSigusr1Notifier + = new QSocketNotifier(g_fdSigusr1[1], QSocketNotifier::Read, this); - QObject::connect(m_pUsr1Notifier, + QObject::connect(m_pSigusr1Notifier, SIGNAL(activated(int)), SLOT(handle_sigusr1())); // Install SIGUSR1 signal handler. - struct sigaction usr1; - usr1.sa_handler = qsampler_sigusr1_handler; - sigemptyset(&usr1.sa_mask); - usr1.sa_flags = 0; - usr1.sa_flags |= SA_RESTART; - ::sigaction(SIGUSR1, &usr1, NULL); + struct sigaction sigusr1; + sigusr1.sa_handler = qsampler_sigusr1_handler; + sigemptyset(&sigusr1.sa_mask); + sigusr1.sa_flags = 0; + sigusr1.sa_flags |= SA_RESTART; + ::sigaction(SIGUSR1, &sigusr1, nullptr); + + // Initialize file descriptors for SIGTERM socket notifier. + ::socketpair(AF_UNIX, SOCK_STREAM, 0, g_fdSigterm); + m_pSigtermNotifier + = new QSocketNotifier(g_fdSigterm[1], QSocketNotifier::Read, this); + + QObject::connect(m_pSigtermNotifier, + SIGNAL(activated(int)), + SLOT(handle_sigterm())); + + // Install SIGTERM signal handler. + struct sigaction sigterm; + sigterm.sa_handler = qsampler_sigterm_handler; + sigemptyset(&sigterm.sa_mask); + sigterm.sa_flags = 0; + sigterm.sa_flags |= SA_RESTART; + ::sigaction(SIGTERM, &sigterm, nullptr); + ::sigaction(SIGQUIT, &sigterm, nullptr); + + // Ignore SIGHUP/SIGINT signals. + ::signal(SIGHUP, SIG_IGN); + ::signal(SIGINT, SIG_IGN); #else // HAVE_SIGNAL_H - m_pUsr1Notifier = NULL; + m_pSigusr1Notifier = nullptr; + m_pSigtermNotifier = nullptr; #endif // !HAVE_SIGNAL_H @@ -270,7 +330,7 @@ #endif // Make it an MDI workspace. - m_pWorkspace = new QMdiArea(this); + m_pWorkspace = new Workspace(this); m_pWorkspace->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); m_pWorkspace->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); // Set the activation connection. @@ -305,7 +365,7 @@ m_statusItem[QSAMPLER_STATUS_SESSION] = pLabel; statusBar()->addWidget(pLabel); -#if defined(WIN32) +#if defined(__WIN32__) || defined(_WIN32) || defined(WIN32) WSAStartup(MAKEWORD(1, 1), &_wsaData); #endif @@ -406,13 +466,15 @@ // Do final processing anyway. processServerExit(); -#if defined(WIN32) +#if defined(__WIN32__) || defined(_WIN32) || defined(WIN32) WSACleanup(); #endif #if defined(HAVE_SIGNAL_H) && defined(HAVE_SYS_SOCKET_H) - if (m_pUsr1Notifier) - delete m_pUsr1Notifier; + if (m_pSigusr1Notifier) + delete m_pSigusr1Notifier; + if (m_pSigtermNotifier) + delete m_pSigtermNotifier; #endif // Finally drop any widgets around... @@ -441,7 +503,7 @@ #endif // Pseudo-singleton reference shut-down. - g_pMainForm = NULL; + g_pMainForm = nullptr; } @@ -578,7 +640,7 @@ void MainForm::dragEnterEvent ( QDragEnterEvent* pDragEnterEvent ) { // Accept external drags only... - if (pDragEnterEvent->source() == NULL + if (pDragEnterEvent->source() == nullptr && pDragEnterEvent->mimeData()->hasUrls()) { pDragEnterEvent->accept(); } else { @@ -602,7 +664,7 @@ if (QFileInfo(sPath).exists()) { // Try to create a new channel from instrument file... Channel *pChannel = new Channel(); - if (pChannel == NULL) + if (pChannel == nullptr) return; // Start setting the instrument filename... pChannel->setInstrument(sPath, 0); @@ -695,23 +757,28 @@ } -// Window resize event handler. -void MainForm::resizeEvent ( QResizeEvent * ) +// LADISH Level 1 -- SIGUSR1 signal handler. +void MainForm::handle_sigusr1 (void) { - if (m_pOptions->bAutoArrange) - channelsArrange(); +#if defined(HAVE_SIGNAL_H) && defined(HAVE_SYS_SOCKET_H) + + char c; + + if (::read(g_fdSigusr1[1], &c, sizeof(c)) > 0) + saveSession(false); + +#endif } -// LADISH Level 1 -- SIGUSR1 signal handler. -void MainForm::handle_sigusr1 (void) +void MainForm::handle_sigterm (void) { #if defined(HAVE_SIGNAL_H) && defined(HAVE_SYS_SOCKET_H) char c; - if (::read(g_fdUsr1[1], &c, sizeof(c)) > 0) - saveSession(false); + if (::read(g_fdSigterm[1], &c, sizeof(c)) > 0) + close(); #endif } @@ -742,7 +809,7 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Brainless public property accessors. +// QSampler::MainForm -- Brainless public property accessors. // The global options settings property. Options *MainForm::options (void) const @@ -766,7 +833,7 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Session file stuff. +// QSampler::MainForm -- Session file stuff. // Format the displayable session filename. QString MainForm::sessionName ( const QString& sFilename ) @@ -795,7 +862,7 @@ m_iUntitled++; // Stabilize form. - m_sFilename = QString::null; + m_sFilename = QString(); m_iDirtyCount = 0; appendMessages(tr("New session: \"%1\".").arg(sessionName(m_sFilename))); stabilizeForm(); @@ -807,12 +874,12 @@ // Open an existing sampler session. bool MainForm::openSession (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return false; // Ask for the filename to open... QString sFilename = QFileDialog::getOpenFileName(this, - QSAMPLER_TITLE ": " + tr("Open Session"), // Caption. + tr("Open Session"), // Caption. m_pOptions->sSessionDir, // Start here. tr("LSCP Session files") + " (*.lscp)" // Filter (LSCP files) ); @@ -833,7 +900,7 @@ // Save current sampler session with another name. bool MainForm::saveSession ( bool bPrompt ) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return false; QString sFilename = m_sFilename; @@ -845,7 +912,7 @@ sFilename = m_pOptions->sSessionDir; // Prompt the guy... sFilename = QFileDialog::getSaveFileName(this, - QSAMPLER_TITLE ": " + tr("Save Session"), // Caption. + tr("Save Session"), // Caption. sFilename, // Start here. tr("LSCP Session files") + " (*.lscp)" // Filter (LSCP files) ); @@ -855,10 +922,11 @@ // Enforce .lscp extension... if (QFileInfo(sFilename).suffix().isEmpty()) sFilename += ".lscp"; + #if 0 // Check if already exists... if (sFilename != m_sFilename && QFileInfo(sFilename).exists()) { if (QMessageBox::warning(this, - QSAMPLER_TITLE ": " + tr("Warning"), + tr("Warning"), tr("The file already exists:\n\n" "\"%1\"\n\n" "Do you want to replace it?") @@ -867,6 +935,7 @@ == QMessageBox::No) return false; } + #endif } // Save it right away. @@ -882,7 +951,7 @@ // Are we dirty enough to prompt it? if (m_iDirtyCount > 0) { switch (QMessageBox::warning(this, - QSAMPLER_TITLE ": " + tr("Warning"), + tr("Warning"), tr("The current session has been changed:\n\n" "\"%1\"\n\n" "Do you want to save the changes?") @@ -907,20 +976,16 @@ m_pWorkspace->setUpdatesEnabled(false); const QList& wlist = m_pWorkspace->subWindowList(); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) { Channel *pChannel = pChannelStrip->channel(); if (bForce && pChannel) pChannel->removeChannel(); delete pChannelStrip; } - if (pMdiSubWindow) - delete pMdiSubWindow; + delete pMdiSubWindow; } m_pWorkspace->setUpdatesEnabled(true); // We're now clean, for sure. @@ -934,7 +999,7 @@ // Load a session from specific file path. bool MainForm::loadSessionFile ( const QString& sFilename ) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return false; // Open and read from real file. @@ -1010,7 +1075,7 @@ // Save current session to specific file path. bool MainForm::saveSessionFile ( const QString& sFilename ) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return false; // Check whether server is apparently OK... @@ -1035,10 +1100,8 @@ int iErrors = 0; QTextStream ts(&file); ts << "# " << QSAMPLER_TITLE " - " << tr(QSAMPLER_SUBTITLE) << endl; - ts << "# " << tr("Version") - << ": " QSAMPLER_VERSION << endl; - ts << "# " << tr("Build") - << ": " __DATE__ " " __TIME__ << endl; + ts << "# " << tr("Version") << ": " CONFIG_BUILD_VERSION << endl; +// ts << "# " << tr("Build") << ": " CONFIG_BUILD_DATE << endl; ts << "#" << endl; ts << "# " << tr("File") << ": " << QFileInfo(sFilename).fileName() << endl; @@ -1051,16 +1114,19 @@ // It is assumed that this new kind of device+session file // will be loaded from a complete initialized server... int *piDeviceIDs; - int iDevice; + int i, iDevice; ts << "RESET" << endl; // Audio device mapping. - QMap audioDeviceMap; + QMap audioDeviceMap; iDevice = 0; piDeviceIDs = Device::getDevices(m_pClient, Device::Audio); - for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) { - ts << endl; - Device device(Device::Audio, piDeviceIDs[iDevice]); + for (i = 0; piDeviceIDs && piDeviceIDs[i] >= 0; ++i) { + Device device(Device::Audio, piDeviceIDs[i]); + // Avoid plug-in driver devices... + if (device.driverName().toUpper() == "PLUGIN") + continue; // Audio device specification... + ts << endl; ts << "# " << device.deviceTypeName() << " " << device.driverName() << " " << tr("Device") << " " << iDevice << endl; ts << "CREATE AUDIO_OUTPUT_DEVICE " << device.driverName(); @@ -1091,18 +1157,21 @@ iPort++; } // Audio device index/id mapping. - audioDeviceMap[device.deviceID()] = iDevice; + audioDeviceMap.insert(device.deviceID(), iDevice++); // Try to keep it snappy :) QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); } // MIDI device mapping. - QMap midiDeviceMap; + QMap midiDeviceMap; iDevice = 0; piDeviceIDs = Device::getDevices(m_pClient, Device::Midi); - for (iDevice = 0; piDeviceIDs && piDeviceIDs[iDevice] >= 0; iDevice++) { - ts << endl; - Device device(Device::Midi, piDeviceIDs[iDevice]); + for (i = 0; piDeviceIDs && piDeviceIDs[i] >= 0; ++i) { + Device device(Device::Midi, piDeviceIDs[i]); + // Avoid plug-in driver devices... + if (device.driverName().toUpper() == "PLUGIN") + continue; // MIDI device specification... + ts << endl; ts << "# " << device.deviceTypeName() << " " << device.driverName() << " " << tr("Device") << " " << iDevice << endl; ts << "CREATE MIDI_INPUT_DEVICE " << device.driverName(); @@ -1133,7 +1202,7 @@ iPort++; } // MIDI device index/id mapping. - midiDeviceMap[device.deviceID()] = iDevice; + midiDeviceMap.insert(device.deviceID(), iDevice++); // Try to keep it snappy :) QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); } @@ -1196,33 +1265,38 @@ } ts << endl; // Check for errors... - if (pInstrs == NULL && ::lscp_client_get_errno(m_pClient)) { + if (pInstrs == nullptr && ::lscp_client_get_errno(m_pClient)) { appendMessagesClient("lscp_list_midi_instruments"); iErrors++; } // MIDI strument index/id mapping. - midiInstrumentMap[iMidiMap] = iMap; + midiInstrumentMap.insert(iMidiMap, iMap); } // Check for errors... - if (piMaps == NULL && ::lscp_client_get_errno(m_pClient)) { + if (piMaps == nullptr && ::lscp_client_get_errno(m_pClient)) { appendMessagesClient("lscp_list_midi_instrument_maps"); iErrors++; } #endif // CONFIG_MIDI_INSTRUMENT - // Sampler channel mapping. + // Sampler channel mapping... + int iChannelID = 0; const QList& wlist = m_pWorkspace->subWindowList(); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) { Channel *pChannel = pChannelStrip->channel(); if (pChannel) { - const int iChannelID = pChannel->channelID(); + // Avoid "artifial" plug-in devices... + const int iAudioDevice = pChannel->audioDevice(); + if (!audioDeviceMap.contains(iAudioDevice)) + continue; + const int iMidiDevice = pChannel->midiDevice(); + if (!midiDeviceMap.contains(iMidiDevice)) + continue; + // Go for regular, canonical devices... ts << "# " << tr("Channel") << " " << iChannelID << endl; ts << "ADD CHANNEL" << endl; if (audioDeviceMap.isEmpty()) { @@ -1230,14 +1304,14 @@ << " " << pChannel->audioDriver() << endl; } else { ts << "SET CHANNEL AUDIO_OUTPUT_DEVICE " << iChannelID - << " " << audioDeviceMap[pChannel->audioDevice()] << endl; + << " " << audioDeviceMap.value(iAudioDevice) << endl; } if (midiDeviceMap.isEmpty()) { ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannelID << " " << pChannel->midiDriver() << endl; } else { ts << "SET CHANNEL MIDI_INPUT_DEVICE " << iChannelID - << " " << midiDeviceMap[pChannel->midiDevice()] << endl; + << " " << midiDeviceMap.value(iMidiDevice) << endl; } ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannelID << " " << pChannel->midiPort() << endl; @@ -1268,9 +1342,10 @@ if (pChannel->channelSolo()) ts << "SET CHANNEL SOLO " << iChannelID << " 1" << endl; #ifdef CONFIG_MIDI_INSTRUMENT - if (pChannel->midiMap() >= 0) { + const int iMidiMap = pChannel->midiMap(); + if (midiInstrumentMap.contains(iMidiMap)) { ts << "SET CHANNEL MIDI_INSTRUMENT_MAP " << iChannelID - << " " << midiInstrumentMap[pChannel->midiMap()] << endl; + << " " << midiInstrumentMap.value(iMidiMap) << endl; } #endif #ifdef CONFIG_FXSEND @@ -1309,6 +1384,8 @@ } #endif ts << endl; + // Go for next channel... + ++iChannelID; } } // Try to keep it snappy :) @@ -1360,7 +1437,7 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- File Action slots. +// QSampler::MainForm -- File Action slots. // Create a new sampler session. void MainForm::fileNew (void) @@ -1414,12 +1491,12 @@ // Reset the sampler instance. void MainForm::fileReset (void) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; // Ask user whether he/she want's an internal sampler reset... if (m_pOptions && m_pOptions->bConfirmReset) { - const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning"); + const QString& sTitle = tr("Warning"); const QString& sText = tr( "Resetting the sampler instance will close\n" "all device and channel configurations.\n\n" @@ -1470,7 +1547,7 @@ // Restart the client/server instance. void MainForm::fileRestart (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; bool bRestart = true; @@ -1478,7 +1555,7 @@ // Ask user whether he/she want's a complete restart... // (if we're currently up and running) if (m_pOptions && m_pOptions->bConfirmRestart) { - const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning"); + const QString& sTitle = tr("Warning"); const QString& sText = tr( "New settings will be effective after\n" "restarting the client/server connection.\n\n" @@ -1526,7 +1603,7 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Edit Action slots. +// QSampler::MainForm -- Edit Action slots. // Add a new sampler channel. void MainForm::editAddChannel (void) @@ -1538,12 +1615,12 @@ void MainForm::addChannelStrip (void) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; // Just create the channel instance... Channel *pChannel = new Channel(); - if (pChannel == NULL) + if (pChannel == nullptr) return; // Before we show it up, may be we'll @@ -1561,8 +1638,7 @@ } // Do we auto-arrange? - if (m_pOptions && m_pOptions->bAutoArrange) - channelsArrange(); + channelsArrangeAuto(); // Make that an overall update. m_iDirtyCount++; @@ -1580,20 +1656,20 @@ void MainForm::removeChannelStrip (void) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; ChannelStrip *pChannelStrip = activeChannelStrip(); - if (pChannelStrip == NULL) + if (pChannelStrip == nullptr) return; Channel *pChannel = pChannelStrip->channel(); - if (pChannel == NULL) + if (pChannel == nullptr) return; // Prompt user if he/she's sure about this... if (m_pOptions && m_pOptions->bConfirmRemove) { - const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning"); + const QString& sTitle = tr("Warning"); const QString& sText = tr( "About to remove channel:\n\n" "%1\n\n" @@ -1636,11 +1712,11 @@ // Setup current sampler channel. void MainForm::editSetupChannel (void) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; ChannelStrip *pChannelStrip = activeChannelStrip(); - if (pChannelStrip == NULL) + if (pChannelStrip == nullptr) return; // Just invoque the channel strip procedure. @@ -1651,11 +1727,11 @@ // Edit current sampler channel. void MainForm::editEditChannel (void) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; ChannelStrip *pChannelStrip = activeChannelStrip(); - if (pChannelStrip == NULL) + if (pChannelStrip == nullptr) return; // Just invoque the channel strip procedure. @@ -1666,11 +1742,11 @@ // Reset current sampler channel. void MainForm::editResetChannel (void) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; ChannelStrip *pChannelStrip = activeChannelStrip(); - if (pChannelStrip == NULL) + if (pChannelStrip == nullptr) return; // Just invoque the channel strip procedure. @@ -1681,7 +1757,7 @@ // Reset all sampler channels. void MainForm::editResetAllChannels (void) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; // Invoque the channel strip procedure, @@ -1689,12 +1765,9 @@ m_pWorkspace->setUpdatesEnabled(false); const QList& wlist = m_pWorkspace->subWindowList(); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) pChannelStrip->channelReset(); } @@ -1703,7 +1776,7 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- View Action slots. +// QSampler::MainForm -- View Action slots. // Show/hide the main program window menubar. void MainForm::viewMenubar ( bool bOn ) @@ -1753,7 +1826,7 @@ // Show/hide the MIDI instrument list-view form. void MainForm::viewInstruments (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; if (m_pInstrumentListForm) { @@ -1772,7 +1845,7 @@ // Show/hide the device configurator form. void MainForm::viewDevices (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; if (m_pDeviceForm) { @@ -1791,7 +1864,7 @@ // Show options dialog. void MainForm::viewOptions (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; OptionsForm* pOptionsForm = new OptionsForm(this); @@ -1803,40 +1876,62 @@ if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages) m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString(); // To track down deferred or immediate changes. - const QString sOldServerHost = m_pOptions->sServerHost; - const int iOldServerPort = m_pOptions->iServerPort; - const int iOldServerTimeout = m_pOptions->iServerTimeout; - const bool bOldServerStart = m_pOptions->bServerStart; - const QString sOldServerCmdLine = m_pOptions->sServerCmdLine; - const bool bOldMessagesLog = m_pOptions->bMessagesLog; - const QString sOldMessagesLogPath = m_pOptions->sMessagesLogPath; - const QString sOldDisplayFont = m_pOptions->sDisplayFont; - const bool bOldDisplayEffect = m_pOptions->bDisplayEffect; - const int iOldMaxVolume = m_pOptions->iMaxVolume; - const QString sOldMessagesFont = m_pOptions->sMessagesFont; - const bool bOldKeepOnTop = m_pOptions->bKeepOnTop; - const bool bOldStdoutCapture = m_pOptions->bStdoutCapture; - const int bOldMessagesLimit = m_pOptions->bMessagesLimit; + const QString sOldServerHost = m_pOptions->sServerHost; + const int iOldServerPort = m_pOptions->iServerPort; + const int iOldServerTimeout = m_pOptions->iServerTimeout; + const bool bOldServerStart = m_pOptions->bServerStart; + const QString sOldServerCmdLine = m_pOptions->sServerCmdLine; + const bool bOldMessagesLog = m_pOptions->bMessagesLog; + const QString sOldMessagesLogPath = m_pOptions->sMessagesLogPath; + const QString sOldDisplayFont = m_pOptions->sDisplayFont; + const bool bOldDisplayEffect = m_pOptions->bDisplayEffect; + const int iOldMaxVolume = m_pOptions->iMaxVolume; + const QString sOldMessagesFont = m_pOptions->sMessagesFont; + const bool bOldKeepOnTop = m_pOptions->bKeepOnTop; + const bool bOldStdoutCapture = m_pOptions->bStdoutCapture; + const int bOldMessagesLimit = m_pOptions->bMessagesLimit; const int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines; - const bool bOldCompletePath = m_pOptions->bCompletePath; - const bool bOldInstrumentNames = m_pOptions->bInstrumentNames; - const int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles; - const int iOldBaseFontSize = m_pOptions->iBaseFontSize; + const bool bOldCompletePath = m_pOptions->bCompletePath; + const bool bOldInstrumentNames = m_pOptions->bInstrumentNames; + const int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles; + const int iOldBaseFontSize = m_pOptions->iBaseFontSize; + const QString sOldCustomStyleTheme = m_pOptions->sCustomStyleTheme; + const QString sOldCustomColorTheme = m_pOptions->sCustomColorTheme; // Load the current setup settings. pOptionsForm->setup(m_pOptions); // Show the setup dialog... if (pOptionsForm->exec()) { // Warn if something will be only effective on next run. + int iNeedRestart = 0; if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) || - (!bOldStdoutCapture && m_pOptions->bStdoutCapture) || - ( bOldKeepOnTop && !m_pOptions->bKeepOnTop) || + (!bOldStdoutCapture && m_pOptions->bStdoutCapture)) { + updateMessagesCapture(); + ++iNeedRestart; + } + if (( bOldKeepOnTop && !m_pOptions->bKeepOnTop) || (!bOldKeepOnTop && m_pOptions->bKeepOnTop) || (iOldBaseFontSize != m_pOptions->iBaseFontSize)) { - QMessageBox::information(this, - QSAMPLER_TITLE ": " + tr("Information"), - tr("Some settings may be only effective\n" - "next time you start this program.")); - updateMessagesCapture(); + ++iNeedRestart; + } + // Check whether restart is needed or whether + // custom options maybe set up immediately... + if (m_pOptions->sCustomStyleTheme != sOldCustomStyleTheme) { + if (m_pOptions->sCustomStyleTheme.isEmpty()) { + ++iNeedRestart; + } else { + QApplication::setStyle( + QStyleFactory::create(m_pOptions->sCustomStyleTheme)); + } + } + if (m_pOptions->sCustomColorTheme != sOldCustomColorTheme) { + if (m_pOptions->sCustomColorTheme.isEmpty()) { + ++iNeedRestart; + } else { + QPalette pal; + if (PaletteForm::namedPalette( + &m_pOptions->settings(), m_pOptions->sCustomColorTheme, pal)) + QApplication::setPalette(pal); + } } // Check wheather something immediate has changed. if (( bOldMessagesLog && !m_pOptions->bMessagesLog) || @@ -1864,6 +1959,13 @@ (!bOldMessagesLimit && m_pOptions->bMessagesLimit) || (iOldMessagesLimitLines != m_pOptions->iMessagesLimitLines)) updateMessagesLimit(); + // Show restart needed message... + if (iNeedRestart > 0) { + QMessageBox::information(this, + tr("Information"), + tr("Some settings may be only effective\n" + "next time you start this program.")); + } // And now the main thing, whether we'll do client/server recycling? if ((sOldServerHost != m_pOptions->sServerHost) || (iOldServerPort != m_pOptions->iServerPort) || @@ -1884,7 +1986,7 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Channels action slots. +// QSampler::MainForm -- Channels action slots. // Arrange channel strips. void MainForm::channelsArrange (void) @@ -1897,18 +1999,16 @@ m_pWorkspace->setUpdatesEnabled(false); int y = 0; - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) { - pMdiSubWindow->adjustSize(); - int iWidth = m_pWorkspace->width(); - if (iWidth < pMdiSubWindow->width()) - iWidth = pMdiSubWindow->width(); - const int iHeight = pMdiSubWindow->frameGeometry().height(); - pMdiSubWindow->setGeometry(0, y, iWidth, iHeight); - y += iHeight; - } + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + pMdiSubWindow->adjustSize(); + const QRect& frameRect + = pMdiSubWindow->frameGeometry(); + int w = m_pWorkspace->width(); + if (w < frameRect.width()) + w = frameRect.width(); + const int h = frameRect.height(); + pMdiSubWindow->setGeometry(0, y, w, h); + y += h; } m_pWorkspace->setUpdatesEnabled(true); @@ -1919,20 +2019,26 @@ // Auto-arrange channel strips. void MainForm::channelsAutoArrange ( bool bOn ) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; // Toggle the auto-arrange flag. m_pOptions->bAutoArrange = bOn; // If on, update whole workspace... - if (m_pOptions->bAutoArrange) + channelsArrangeAuto(); +} + + +void MainForm::channelsArrangeAuto (void) +{ + if (m_pOptions && m_pOptions->bAutoArrange) channelsArrange(); } //------------------------------------------------------------------------- -// qsamplerMainForm -- Help Action slots. +// QSampler::MainForm -- Help Action slots. // Show information about the Qt toolkit. void MainForm::helpAboutQt (void) @@ -1944,72 +2050,55 @@ // Show information about application program. void MainForm::helpAbout (void) { - // Stuff the about box text... - QString sText = "

\n"; - sText += "" QSAMPLER_TITLE " - " + tr(QSAMPLER_SUBTITLE) + "
\n"; - sText += "
\n"; - sText += tr("Version") + ": " QSAMPLER_VERSION "
\n"; - sText += "" + tr("Build") + ": " __DATE__ " " __TIME__ "
\n"; + QStringList list; #ifdef CONFIG_DEBUG - sText += ""; - sText += tr("Debugging option enabled."); - sText += "
"; + list << tr("Debugging option enabled."); #endif #ifndef CONFIG_LIBGIG - sText += ""; - sText += tr("GIG (libgig) file support disabled."); - sText += "
"; + list << tr("GIG (libgig) file support disabled."); #endif #ifndef CONFIG_INSTRUMENT_NAME - sText += ""; - sText += tr("LSCP (liblscp) instrument_name support disabled."); - sText += "
"; + list << tr("LSCP (liblscp) instrument_name support disabled."); #endif #ifndef CONFIG_MUTE_SOLO - sText += ""; - sText += tr("Sampler channel Mute/Solo support disabled."); - sText += "
"; + list << tr("Sampler channel Mute/Solo support disabled."); #endif #ifndef CONFIG_AUDIO_ROUTING - sText += ""; - sText += tr("LSCP (liblscp) audio_routing support disabled."); - sText += "
"; + list << tr("LSCP (liblscp) audio_routing support disabled."); #endif #ifndef CONFIG_FXSEND - sText += ""; - sText += tr("Sampler channel Effect Sends support disabled."); - sText += "
"; + list << tr("Sampler channel Effect Sends support disabled."); #endif #ifndef CONFIG_VOLUME - sText += ""; - sText += tr("Global volume support disabled."); - sText += "
"; + list << tr("Global volume support disabled."); #endif #ifndef CONFIG_MIDI_INSTRUMENT - sText += ""; - sText += tr("MIDI instrument mapping support disabled."); - sText += "
"; + list << tr("MIDI instrument mapping support disabled."); #endif #ifndef CONFIG_EDIT_INSTRUMENT - sText += ""; - sText += tr("Instrument editing support disabled."); - sText += "
"; + list << tr("Instrument editing support disabled."); #endif #ifndef CONFIG_EVENT_CHANNEL_MIDI - sText += ""; - sText += tr("Channel MIDI event support disabled."); - sText += "
"; + list << tr("Channel MIDI event support disabled."); #endif #ifndef CONFIG_EVENT_DEVICE_MIDI - sText += ""; - sText += tr("Device MIDI event support disabled."); - sText += "
"; + list << tr("Device MIDI event support disabled."); #endif #ifndef CONFIG_MAX_VOICES - sText += ""; - sText += tr("Runtime max. voices / disk streams support disabled."); - sText += "
"; + list << tr("Runtime max. voices / disk streams support disabled."); #endif + + // Stuff the about box text... + QString sText = "

\n"; + sText += "" QSAMPLER_TITLE " - " + tr(QSAMPLER_SUBTITLE) + "
\n"; + sText += "
\n"; + sText += tr("Version") + ": " CONFIG_BUILD_VERSION "
\n"; +// sText += "" + tr("Build") + ": " CONFIG_BUILD_DATE "
\n"; + if (!list.isEmpty()) { + sText += ""; + sText += list.join("
\n"); + sText += "
"; + } sText += "
\n"; sText += tr("Using") + ": "; sText += ::lscp_client_package(); @@ -2034,12 +2123,12 @@ sText += ""; sText += "

\n"; - QMessageBox::about(this, tr("About") + " " QSAMPLER_TITLE, sText); + QMessageBox::about(this, tr("About"), sText); } //------------------------------------------------------------------------- -// qsamplerMainForm -- Main window stabilization. +// QSampler::MainForm -- Main window stabilization. void MainForm::stabilizeForm (void) { @@ -2047,20 +2136,20 @@ QString sSessionName = sessionName(m_sFilename); if (m_iDirtyCount > 0) sSessionName += " *"; - setWindowTitle(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName)); + setWindowTitle(sSessionName); // Update the main menu state... ChannelStrip *pChannelStrip = activeChannelStrip(); const QList& wlist = m_pWorkspace->subWindowList(); - const bool bHasClient = (m_pOptions != NULL && m_pClient != NULL); - const bool bHasChannel = (bHasClient && pChannelStrip != NULL); + const bool bHasClient = (m_pOptions != nullptr && m_pClient != nullptr); + const bool bHasChannel = (bHasClient && pChannelStrip != nullptr); const bool bHasChannels = (bHasClient && wlist.count() > 0); m_ui.fileNewAction->setEnabled(bHasClient); m_ui.fileOpenAction->setEnabled(bHasClient); m_ui.fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0); m_ui.fileSaveAsAction->setEnabled(bHasClient); m_ui.fileResetAction->setEnabled(bHasClient); - m_ui.fileRestartAction->setEnabled(bHasClient || m_pServer == NULL); + m_ui.fileRestartAction->setEnabled(bHasClient || m_pServer == nullptr); m_ui.editAddChannelAction->setEnabled(bHasClient); m_ui.editRemoveChannelAction->setEnabled(bHasChannel); m_ui.editSetupChannelAction->setEnabled(bHasChannel); @@ -2191,8 +2280,7 @@ updateAllChannelStrips(false); // Do we auto-arrange? - if (m_pOptions && m_pOptions->bAutoArrange) - channelsArrange(); + channelsArrangeAuto(); // Remember to refresh devices and instruments... if (m_pInstrumentListForm) @@ -2210,7 +2298,7 @@ // Retrieve the current channel list. int *piChannelIDs = ::lscp_list_channels(m_pClient); - if (piChannelIDs == NULL) { + if (piChannelIDs == nullptr) { if (::lscp_client_get_errno(m_pClient)) { appendMessagesClient("lscp_list_channels"); appendMessagesError( @@ -2225,23 +2313,19 @@ createChannelStrip(new Channel(piChannelIDs[iChannel])); } // Do we auto-arrange? - if (m_pOptions && m_pOptions->bAutoArrange) - channelsArrange(); + channelsArrangeAuto(); // remove dead channel strips if (bRemoveDeadStrips) { const QList& wlist = m_pWorkspace->subWindowList(); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) { bool bExists = false; for (int iChannel = 0; piChannelIDs[iChannel] >= 0; ++iChannel) { Channel *pChannel = pChannelStrip->channel(); - if (pChannel == NULL) + if (pChannel == nullptr) break; if (piChannelIDs[iChannel] == pChannel->channelID()) { // strip exists, don't touch it @@ -2264,7 +2348,7 @@ // Update the recent files list and menu. void MainForm::updateRecentFiles ( const QString& sFilename ) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; // Remove from list if already there (avoid duplicates) @@ -2279,7 +2363,7 @@ // Update the recent files list and menu. void MainForm::updateRecentFilesMenu (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; // Time to keep the list under limits. @@ -2313,12 +2397,9 @@ return; m_pWorkspace->setUpdatesEnabled(false); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) pChannelStrip->updateInstrumentName(true); } @@ -2329,7 +2410,7 @@ // Force update of the channels display font. void MainForm::updateDisplayFont (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; // Check if display font is legal. @@ -2348,12 +2429,9 @@ return; m_pWorkspace->setUpdatesEnabled(false); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) pChannelStrip->setDisplayFont(font); } @@ -2371,12 +2449,9 @@ return; m_pWorkspace->setUpdatesEnabled(false); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect); } @@ -2387,7 +2462,7 @@ // Force update of the channels maximum volume setting. void MainForm::updateMaxVolume (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; #ifdef CONFIG_VOLUME @@ -2404,12 +2479,9 @@ return; m_pWorkspace->setUpdatesEnabled(false); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume); } @@ -2418,10 +2490,10 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Messages window form handlers. +// QSampler::MainForm -- Messages window form handlers. // Messages output methods. -void MainForm::appendMessages( const QString& s ) +void MainForm::appendMessages ( const QString& s ) { if (m_pMessages) m_pMessages->appendMessages(s); @@ -2429,39 +2501,39 @@ statusBar()->showMessage(s, 3000); } -void MainForm::appendMessagesColor( const QString& s, const QString& c ) +void MainForm::appendMessagesColor ( const QString& s, const QColor& rgb ) { if (m_pMessages) - m_pMessages->appendMessagesColor(s, c); + m_pMessages->appendMessagesColor(s, rgb); statusBar()->showMessage(s, 3000); } -void MainForm::appendMessagesText( const QString& s ) +void MainForm::appendMessagesText ( const QString& s ) { if (m_pMessages) m_pMessages->appendMessagesText(s); } -void MainForm::appendMessagesError( const QString& sText ) +void MainForm::appendMessagesError ( const QString& s ) { if (m_pMessages) m_pMessages->show(); - appendMessagesColor(sText.simplified(), "#ff0000"); + appendMessagesColor(s.simplified(), Qt::red); // Make it look responsive...:) QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); if (m_pOptions && m_pOptions->bConfirmError) { - const QString& sTitle = QSAMPLER_TITLE ": " + tr("Error"); + const QString& sTitle = tr("Error"); #if 0 QMessageBox::critical(this, sTitle, sText, QMessageBox::Cancel); #else QMessageBox mbox(this); mbox.setIcon(QMessageBox::Critical); mbox.setWindowTitle(sTitle); - mbox.setText(sText); + mbox.setText(s); mbox.setStandardButtons(QMessageBox::Cancel); QCheckBox cbox(tr("Don't show this again")); cbox.setChecked(false); @@ -2477,7 +2549,7 @@ // This is a special message format, just for client results. void MainForm::appendMessagesClient( const QString& s ) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; appendMessagesColor(s + QString(": %1 (errno=%2)") @@ -2492,7 +2564,7 @@ // Force update of the messages font. void MainForm::updateMessagesFont (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) { @@ -2506,7 +2578,7 @@ // Update messages window line limit. void MainForm::updateMessagesLimit (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; if (m_pMessages) { @@ -2521,7 +2593,7 @@ // Enablement of the messages capture feature. void MainForm::updateMessagesCapture (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; if (m_pMessages) @@ -2530,18 +2602,18 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- MDI channel strip management. +// QSampler::MainForm -- MDI channel strip management. // The channel strip creation executive. ChannelStrip *MainForm::createChannelStrip ( Channel *pChannel ) { - if (m_pClient == NULL || pChannel == NULL) - return NULL; + if (m_pClient == nullptr || pChannel == nullptr) + return nullptr; // Add a new channel itema... ChannelStrip *pChannelStrip = new ChannelStrip(); - if (pChannelStrip == NULL) - return NULL; + if (pChannelStrip == nullptr) + return nullptr; // Set some initial channel strip options... if (m_pOptions) { @@ -2557,8 +2629,10 @@ } // Add it to workspace... - m_pWorkspace->addSubWindow(pChannelStrip, - Qt::SubWindow | Qt::FramelessWindowHint); + QMdiSubWindow *pMdiSubWindow + = m_pWorkspace->addSubWindow(pChannelStrip, + Qt::SubWindow | Qt::FramelessWindowHint); + pMdiSubWindow->setAttribute(Qt::WA_DeleteOnClose); // Actual channel strip setup... pChannelStrip->setup(pChannel); @@ -2582,7 +2656,7 @@ { QMdiSubWindow *pMdiSubWindow = static_cast (pChannelStrip->parentWidget()); - if (pMdiSubWindow == NULL) + if (pMdiSubWindow == nullptr) return; // Just delete the channel strip. @@ -2590,8 +2664,7 @@ delete pMdiSubWindow; // Do we auto-arrange? - if (m_pOptions && m_pOptions->bAutoArrange) - channelsArrange(); + channelsArrangeAuto(); } @@ -2602,28 +2675,28 @@ if (pMdiSubWindow) return static_cast (pMdiSubWindow->widget()); else - return NULL; + return nullptr; } // Retrieve a channel strip by index. ChannelStrip *MainForm::channelStripAt ( int iStrip ) { - if (!m_pWorkspace) return NULL; + if (!m_pWorkspace) return nullptr; const QList& wlist = m_pWorkspace->subWindowList(); if (wlist.isEmpty()) - return NULL; + return nullptr; if (iStrip < 0 || iStrip >= wlist.count()) - return NULL; + return nullptr; QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); if (pMdiSubWindow) return static_cast (pMdiSubWindow->widget()); else - return NULL; + return nullptr; } @@ -2633,14 +2706,11 @@ const QList& wlist = m_pWorkspace->subWindowList(); if (wlist.isEmpty()) - return NULL; + return nullptr; - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) { Channel *pChannel = pChannelStrip->channel(); if (pChannel && pChannel->channelID() == iChannelID) @@ -2649,7 +2719,7 @@ } // Not found. - return NULL; + return nullptr; } @@ -2664,12 +2734,10 @@ = m_pWorkspace->subWindowList(); if (!wlist.isEmpty()) { m_ui.channelsMenu->addSeparator(); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + int iStrip = 0; + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) { QAction *pAction = m_ui.channelsMenu->addAction( pChannelStrip->windowTitle(), @@ -2678,6 +2746,7 @@ pAction->setChecked(activeChannelStrip() == pChannelStrip); pAction->setData(iStrip); } + ++iStrip; } } } @@ -2688,7 +2757,7 @@ { // Retrive channel index from action data... QAction *pAction = qobject_cast (sender()); - if (pAction == NULL) + if (pAction == nullptr) return; ChannelStrip *pChannelStrip = channelStripAt(pAction->data().toInt()); @@ -2700,7 +2769,7 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Timer stuff. +// QSampler::MainForm -- Timer stuff. // Set the pseudo-timer delay schedule. void MainForm::startSchedule ( int iStartDelay ) @@ -2719,7 +2788,7 @@ // Timer slot funtion. void MainForm::timerSlot (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; // Is it the first shot on server start after a few delay? @@ -2754,17 +2823,25 @@ // Update the channel stream usage for each strip... const QList& wlist = m_pWorkspace->subWindowList(); - const int iStripCount = wlist.count(); - for (int iStrip = 0; iStrip < iStripCount; ++iStrip) { - ChannelStrip *pChannelStrip = NULL; - QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); - if (pMdiSubWindow) - pChannelStrip = static_cast (pMdiSubWindow->widget()); + foreach (QMdiSubWindow *pMdiSubWindow, wlist) { + ChannelStrip *pChannelStrip + = static_cast (pMdiSubWindow->widget()); if (pChannelStrip && pChannelStrip->isVisible()) pChannelStrip->updateChannelUsage(); } } } + + #if CONFIG_LSCP_CLIENT_CONNECTION_LOST + // If we lost connection to server: Try to automatically reconnect if we + // did not start the server. + // + // TODO: If we started the server, then we might inform the user that + // the server probably crashed and asking user ONCE whether we should + // restart the server. + if (lscp_client_connection_lost(m_pClient) && !m_pServer) + startAutoReconnectClient(); + #endif // CONFIG_LSCP_CLIENT_CONNECTION_LOST } // Register the next timer slot. @@ -2773,12 +2850,12 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Server stuff. +// QSampler::MainForm -- Server stuff. // Start linuxsampler server... void MainForm::startServer (void) { - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return; // Aren't already a client, are we? @@ -2788,7 +2865,7 @@ // Is the server process instance still here? if (m_pServer) { if (QMessageBox::warning(this, - QSAMPLER_TITLE ": " + tr("Warning"), + tr("Warning"), tr("Could not start the LinuxSampler server.\n\n" "Maybe it is already started."), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { @@ -2807,7 +2884,7 @@ // OK. Let's build the startup process... m_pServer = new QProcess(); - bForceServerStop = true; + m_bForceServerStop = true; // Setup stdout/stderr capture... m_pServer->setProcessChannelMode(QProcess::ForwardedChannels); @@ -2841,7 +2918,12 @@ // Show startup results... appendMessages( - tr("Server was started with PID=%1.").arg((long) m_pServer->pid())); + tr("Server was started with PID=%1.") + #if QT_VERSION < QT_VERSION_CHECK(5, 3, 0) + .arg(quint64(m_pServer->pid()))); + #else + .arg(quint64(m_pServer->processId()))); + #endif // Reset (yet again) the timer counters, // but this time is deferred as the user opted. @@ -2851,46 +2933,51 @@ // Stop linuxsampler server... -void MainForm::stopServer (bool bInteractive) +void MainForm::stopServer ( bool bInteractive ) { // Stop client code. stopClient(); if (m_pServer && bInteractive) { if (QMessageBox::question(this, - QSAMPLER_TITLE ": " + tr("The backend's fate ..."), + tr("The backend's fate ..."), tr("You have the option to keep the sampler backend (LinuxSampler)\n" "running in the background. The sampler would continue to work\n" "according to your current sampler session and you could alter the\n" "sampler session at any time by relaunching QSampler.\n\n" "Do you want LinuxSampler to stop?"), QMessageBox::Yes | QMessageBox::No, - QMessageBox::Yes) == QMessageBox::No) - { - bForceServerStop = false; + QMessageBox::Yes) == QMessageBox::No) { + m_bForceServerStop = false; } } + bool bGraceWait = true; + // And try to stop server. - if (m_pServer && bForceServerStop) { + if (m_pServer && m_bForceServerStop) { appendMessages(tr("Server is stopping...")); if (m_pServer->state() == QProcess::Running) { - #if defined(WIN32) + #if defined(__WIN32__) || defined(_WIN32) || defined(WIN32) // Try harder... m_pServer->kill(); #else // Try softly... m_pServer->terminate(); + bool bFinished = m_pServer->waitForFinished(QSAMPLER_TIMER_MSECS * 1000); + if (bFinished) bGraceWait = false; #endif } } // Do final processing anyway. else processServerExit(); // Give it some time to terminate gracefully and stabilize... - QTime t; - t.start(); - while (t.elapsed() < QSAMPLER_TIMER_MSECS) - QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + if (bGraceWait) { + QElapsedTimer timer; + timer.start(); + while (timer.elapsed() < QSAMPLER_TIMER_MSECS) + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + } } @@ -2912,15 +2999,15 @@ if (m_pMessages) m_pMessages->flushStdoutBuffer(); - if (m_pServer && bForceServerStop) { + if (m_pServer && m_bForceServerStop) { if (m_pServer->state() != QProcess::NotRunning) { appendMessages(tr("Server is being forced...")); // Force final server shutdown... m_pServer->kill(); // Give it some time to terminate gracefully and stabilize... - QTime t; - t.start(); - while (t.elapsed() < QSAMPLER_TIMER_MSECS) + QElapsedTimer timer; + timer.start(); + while (timer.elapsed() < QSAMPLER_TIMER_MSECS) QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); } // Force final server shutdown... @@ -2928,7 +3015,7 @@ tr("Server was stopped with exit status %1.") .arg(m_pServer->exitStatus())); delete m_pServer; - m_pServer = NULL; + m_pServer = nullptr; } // Again, make status visible stable. @@ -2937,14 +3024,14 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Client stuff. +// QSampler::MainForm -- Client stuff. // The LSCP client callback procedure. lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/, lscp_event_t event, const char *pchData, int cchData, void *pvData ) { MainForm* pMainForm = (MainForm *) pvData; - if (pMainForm == NULL) + if (pMainForm == nullptr) return LSCP_FAILED; // ATTN: DO NOT EVER call any GUI code here, @@ -2958,10 +3045,10 @@ // Start our almighty client... -bool MainForm::startClient (void) +bool MainForm::startClient (bool bReconnectOnly) { // Have it a setup? - if (m_pOptions == NULL) + if (m_pOptions == nullptr) return false; // Aren't we already started, are we? @@ -2975,13 +3062,19 @@ m_pClient = ::lscp_client_create( m_pOptions->sServerHost.toUtf8().constData(), m_pOptions->iServerPort, qsampler_client_callback, this); - if (m_pClient == NULL) { + if (m_pClient == nullptr) { // Is this the first try? // maybe we need to start a local server... if ((m_pServer && m_pServer->state() == QProcess::Running) - || !m_pOptions->bServerStart) { - appendMessagesError( - tr("Could not connect to server as client.\n\nSorry.")); + || !m_pOptions->bServerStart || bReconnectOnly) + { + // if this method is called from autoReconnectClient() + // then don't bother user with an error message... + if (!bReconnectOnly) { + appendMessagesError( + tr("Could not connect to server as client.\n\nSorry.") + ); + } } else { startServer(); } @@ -2989,6 +3082,7 @@ stabilizeForm(); return false; } + // Just set receive timeout value, blindly. ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout); appendMessages( @@ -3044,7 +3138,7 @@ if (!m_pOptions->sSessionFile.isEmpty()) { // Just load the prabably startup session... if (loadSessionFile(m_pOptions->sSessionFile)) { - m_pOptions->sSessionFile = QString::null; + m_pOptions->sSessionFile = QString(); return true; } } @@ -3060,7 +3154,7 @@ // Stop client... void MainForm::stopClient (void) { - if (m_pClient == NULL) + if (m_pClient == nullptr) return; // Log prepare here. @@ -3092,7 +3186,7 @@ ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO); ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_COUNT); ::lscp_client_destroy(m_pClient); - m_pClient = NULL; + m_pClient = nullptr; // Hard-notify instrumnet and device configuration forms, // if visible, that we're running out... @@ -3109,10 +3203,26 @@ } +void MainForm::startAutoReconnectClient (void) +{ + stopClient(); + appendMessages(tr("Trying to reconnect...")); + QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(autoReconnectClient())); +} + + +void MainForm::autoReconnectClient (void) +{ + const bool bSuccess = startClient(true); + if (!bSuccess) + QTimer::singleShot(QSAMPLER_TIMER_MSECS, this, SLOT(autoReconnectClient())); +} + + // Channel strip activation/selection. void MainForm::activateStrip ( QMdiSubWindow *pMdiSubWindow ) { - ChannelStrip *pChannelStrip = NULL; + ChannelStrip *pChannelStrip = nullptr; if (pMdiSubWindow) pChannelStrip = static_cast (pMdiSubWindow->widget()); if (pChannelStrip)