--- qsampler/trunk/src/qsamplerMainForm.cpp 2008/02/15 17:08:33 1693 +++ qsampler/trunk/src/qsamplerMainForm.cpp 2017/10/18 08:57:21 3358 @@ -1,8 +1,8 @@ // qsamplerMainForm.cpp // /**************************************************************************** - Copyright (C) 2004-2007, rncbc aka Rui Nuno Capela. All rights reserved. - Copyright (C) 2007, Christian Schoenebeck + Copyright (C) 2004-2017, rncbc aka Rui Nuno Capela. All rights reserved. + Copyright (C) 2007,2008,2015 Christian Schoenebeck This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -33,9 +33,12 @@ #include "qsamplerInstrumentListForm.h" #include "qsamplerDeviceForm.h" #include "qsamplerOptionsForm.h" +#include "qsamplerDeviceStatusForm.h" + +#include +#include #include -#include #include #include @@ -55,6 +58,15 @@ #include #include +#if QT_VERSION >= 0x050000 +#include +#endif + +#if QT_VERSION < 0x040500 +namespace Qt { +const WindowFlags WindowCloseButtonHint = WindowFlags(0x08000000); +} +#endif #ifdef HAVE_SIGNAL_H #include @@ -79,11 +91,41 @@ // All winsock apps needs this. -#if defined(WIN32) +#if defined(__WIN32__) || defined(_WIN32) || defined(WIN32) static WSADATA _wsaData; #endif +//------------------------------------------------------------------------- +// LADISH Level 1 support stuff. + +#if defined(HAVE_SIGNAL_H) && defined(HAVE_SYS_SOCKET_H) + +#include + +#include +#include + +#include + +// File descriptor for SIGUSR1 notifier. +static int g_fdUsr1[2]; + +// Unix SIGUSR1 signal handler. +static void qsampler_sigusr1_handler ( int /* signo */ ) +{ + char c = 1; + + (::write(g_fdUsr1[0], &c, sizeof(c)) > 0); +} + +#endif // HAVE_SIGNAL_H + + +//------------------------------------------------------------------------- +// QSampler -- namespace + + namespace QSampler { // Timer constant stuff. @@ -96,26 +138,28 @@ #define QSAMPLER_STATUS_SESSION 3 // Current session modification state. -//------------------------------------------------------------------------- -// CustomEvent -- specialty for callback comunication. +// Specialties for thread-callback comunication. +#define QSAMPLER_LSCP_EVENT QEvent::Type(QEvent::User + 1) + -#define QSAMPLER_CUSTOM_EVENT QEvent::Type(QEvent::User + 0) +//------------------------------------------------------------------------- +// QSampler::LscpEvent -- specialty for LSCP callback comunication. -class CustomEvent : public QEvent +class LscpEvent : public QEvent { public: // Constructor. - CustomEvent(lscp_event_t event, const char *pchData, int cchData) - : QEvent(QSAMPLER_CUSTOM_EVENT) + LscpEvent(lscp_event_t event, const char *pchData, int cchData) + : QEvent(QSAMPLER_LSCP_EVENT) { m_event = event; m_data = QString::fromUtf8(pchData, cchData); } // 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: @@ -127,10 +171,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 = NULL; MainForm::MainForm ( QWidget *pParent ) : QMainWindow(pParent) @@ -150,6 +214,7 @@ // We'll start clean. m_iUntitled = 0; + m_iDirtySetup = 0; m_iDirtyCount = 0; m_pServer = NULL; @@ -160,10 +225,35 @@ m_iTimerSlot = 0; -#ifdef HAVE_SIGNAL_H +#if defined(HAVE_SIGNAL_H) && defined(HAVE_SYS_SOCKET_H) + // Set to ignore any fatal "Broken pipe" signals. ::signal(SIGPIPE, SIG_IGN); -#endif + + // 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); + + QObject::connect(m_pUsr1Notifier, + 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); + +#else // HAVE_SIGNAL_H + + m_pUsr1Notifier = NULL; + +#endif // !HAVE_SIGNAL_H #ifdef CONFIG_VOLUME // Make some extras into the toolbar... @@ -184,13 +274,10 @@ QObject::connect(m_pVolumeSlider, SIGNAL(valueChanged(int)), SLOT(volumeChanged(int))); - //m_ui.channelsToolbar->setHorizontallyStretchable(true); - //m_ui.channelsToolbar->setStretchableWidget(m_pVolumeSlider); m_ui.channelsToolbar->addWidget(m_pVolumeSlider); // Volume spin-box m_ui.channelsToolbar->addSeparator(); m_pVolumeSpinBox = new QSpinBox(m_ui.channelsToolbar); - m_pVolumeSpinBox->setMaximumHeight(24); m_pVolumeSpinBox->setSuffix(" %"); m_pVolumeSpinBox->setMinimum(0); m_pVolumeSpinBox->setMaximum(100); @@ -202,12 +289,13 @@ #endif // Make it an MDI workspace. - m_pWorkspace = new QWorkspace(this); - m_pWorkspace->setScrollBarsEnabled(true); + m_pWorkspace = new Workspace(this); + m_pWorkspace->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_pWorkspace->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); // Set the activation connection. QObject::connect(m_pWorkspace, - SIGNAL(windowActivated(QWidget *)), - SLOT(activateStrip(QWidget *))); + SIGNAL(subWindowActivated(QMdiSubWindow *)), + SLOT(activateStrip(QMdiSubWindow *))); // Make it shine :-) setCentralWidget(m_pWorkspace); @@ -236,7 +324,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 @@ -324,6 +412,11 @@ QObject::connect(m_ui.channelsMenu, SIGNAL(aboutToShow()), SLOT(channelsMenuAboutToShow())); +#ifdef CONFIG_VOLUME + QObject::connect(m_ui.channelsToolbar, + SIGNAL(orientationChanged(Qt::Orientation)), + SLOT(channelsToolbarOrientation(Qt::Orientation))); +#endif } // Destructor. @@ -332,10 +425,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; +#endif + // Finally drop any widgets around... if (m_pDeviceForm) delete m_pDeviceForm; @@ -374,26 +472,33 @@ // What style do we create these forms? Qt::WindowFlags wflags = Qt::Window -#if QT_VERSION >= 0x040200 | Qt::CustomizeWindowHint -#endif | Qt::WindowTitleHint | Qt::WindowSystemMenuHint - | Qt::WindowMinMaxButtonsHint; + | Qt::WindowMinMaxButtonsHint + | Qt::WindowCloseButtonHint; if (m_pOptions->bKeepOnTop) wflags |= Qt::Tool; + // Some child forms are to be created right now. m_pMessages = new Messages(this); m_pDeviceForm = new DeviceForm(this, wflags); #ifdef CONFIG_MIDI_INSTRUMENT m_pInstrumentListForm = new InstrumentListForm(this, wflags); #else - viewInstrumentsAction->setEnabled(false); + m_ui.viewInstrumentsAction->setEnabled(false); #endif + + // Setup messages logging appropriately... + m_pMessages->setLogging( + m_pOptions->bMessagesLog, + m_pOptions->sMessagesLogPath); + // Set message defaults... updateMessagesFont(); updateMessagesLimit(); updateMessagesCapture(); + // Set the visibility signal. QObject::connect(m_pMessages, SIGNAL(visibilityChanged(bool)), @@ -420,7 +525,7 @@ } // Try to restore old window positioning and initial visibility. - m_pOptions->loadWidgetGeometry(this); + m_pOptions->loadWidgetGeometry(this, true); m_pOptions->loadWidgetGeometry(m_pInstrumentListForm); m_pOptions->loadWidgetGeometry(m_pDeviceForm); @@ -459,12 +564,11 @@ || m_ui.channelsToolbar->isVisible()); m_pOptions->bStatusbar = statusBar()->isVisible(); // Save the dock windows state. - const QString sDockables = saveState().toBase64().data(); m_pOptions->settings().setValue("/Layout/DockWindows", saveState()); // And the children, and the main windows state,. m_pOptions->saveWidgetGeometry(m_pDeviceForm); m_pOptions->saveWidgetGeometry(m_pInstrumentListForm); - m_pOptions->saveWidgetGeometry(this); + m_pOptions->saveWidgetGeometry(this, true); // Close popup widgets. if (m_pInstrumentListForm) m_pInstrumentListForm->close(); @@ -481,9 +585,10 @@ void MainForm::closeEvent ( QCloseEvent *pCloseEvent ) { - if (queryClose()) + if (queryClose()) { + DeviceStatusForm::deleteAllInstances(); pCloseEvent->accept(); - else + } else pCloseEvent->ignore(); } @@ -501,7 +606,7 @@ } -void MainForm::dropEvent ( QDropEvent* pDropEvent ) +void MainForm::dropEvent ( QDropEvent *pDropEvent ) { // Accept externally originated drops only... if (pDropEvent->source()) @@ -512,7 +617,8 @@ QListIterator iter(pMimeData->urls()); while (iter.hasNext()) { const QString& sPath = iter.next().toLocalFile(); - if (Channel::isInstrumentFile(sPath)) { + // if (Channel::isDlsInstrumentFile(sPath)) { + if (QFileInfo(sPath).exists()) { // Try to create a new channel from instrument file... Channel *pChannel = new Channel(); if (pChannel == NULL) @@ -546,36 +652,97 @@ // Custome event handler. -void MainForm::customEvent(QEvent* pCustomEvent) +void MainForm::customEvent ( QEvent* pEvent ) { // For the time being, just pump it to messages. - if (pCustomEvent->type() == QSAMPLER_CUSTOM_EVENT) { - CustomEvent *pEvent = static_cast (pCustomEvent); - switch (pEvent->event()) { + if (pEvent->type() == QSAMPLER_LSCP_EVENT) { + LscpEvent *pLscpEvent = static_cast (pEvent); + switch (pLscpEvent->event()) { + case LSCP_EVENT_CHANNEL_COUNT: + updateAllChannelStrips(true); + break; case LSCP_EVENT_CHANNEL_INFO: { - int iChannelID = pEvent->data().toInt(); + const int iChannelID = pLscpEvent->data().toInt(); ChannelStrip *pChannelStrip = channelStrip(iChannelID); if (pChannelStrip) channelStripChanged(pChannelStrip); break; } -#if CONFIG_LSCP_CHANNEL_MIDI + case LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT: + if (m_pDeviceForm) m_pDeviceForm->refreshDevices(); + DeviceStatusForm::onDevicesChanged(); + updateViewMidiDeviceStatusMenu(); + break; + case LSCP_EVENT_MIDI_INPUT_DEVICE_INFO: { + if (m_pDeviceForm) m_pDeviceForm->refreshDevices(); + const int iDeviceID = pLscpEvent->data().section(' ', 0, 0).toInt(); + DeviceStatusForm::onDeviceChanged(iDeviceID); + break; + } + case LSCP_EVENT_AUDIO_OUTPUT_DEVICE_COUNT: + if (m_pDeviceForm) m_pDeviceForm->refreshDevices(); + break; + case LSCP_EVENT_AUDIO_OUTPUT_DEVICE_INFO: + if (m_pDeviceForm) m_pDeviceForm->refreshDevices(); + break; + #if CONFIG_EVENT_CHANNEL_MIDI case LSCP_EVENT_CHANNEL_MIDI: { - int iChannelID = pEvent->data().section(' ', 0, 0).toInt(); + const int iChannelID = pLscpEvent->data().section(' ', 0, 0).toInt(); ChannelStrip *pChannelStrip = channelStrip(iChannelID); if (pChannelStrip) - pChannelStrip->midiArrived(); + pChannelStrip->midiActivityLedOn(); break; } -#endif + #endif + #if CONFIG_EVENT_DEVICE_MIDI + case LSCP_EVENT_DEVICE_MIDI: { + const int iDeviceID = pLscpEvent->data().section(' ', 0, 0).toInt(); + const int iPortID = pLscpEvent->data().section(' ', 1, 1).toInt(); + DeviceStatusForm *pDeviceStatusForm + = DeviceStatusForm::getInstance(iDeviceID); + if (pDeviceStatusForm) + pDeviceStatusForm->midiArrived(iPortID); + break; + } + #endif default: - appendMessagesColor(tr("Notify event: %1 data: %2") - .arg(::lscp_event_to_text(pEvent->event())) - .arg(pEvent->data()), "#996699"); + appendMessagesColor(tr("LSCP Event: %1 data: %2") + .arg(::lscp_event_to_text(pLscpEvent->event())) + .arg(pLscpEvent->data()), "#996699"); } } } + +// LADISH Level 1 -- SIGUSR1 signal handler. +void MainForm::handle_sigusr1 (void) +{ +#if defined(HAVE_SIGNAL_H) && defined(HAVE_SYS_SOCKET_H) + + char c; + + if (::read(g_fdUsr1[1], &c, sizeof(c)) > 0) + saveSession(false); + +#endif +} + + +void MainForm::updateViewMidiDeviceStatusMenu (void) +{ + m_ui.viewMidiDeviceStatusMenu->clear(); + const std::map statusForms + = DeviceStatusForm::getInstances(); + std::map::const_iterator iter + = statusForms.begin(); + for ( ; iter != statusForms.end(); ++iter) { + DeviceStatusForm *pStatusForm = iter->second; + m_ui.viewMidiDeviceStatusMenu->addAction( + pStatusForm->visibleAction()); + } +} + + // Context menu event handler. void MainForm::contextMenuEvent( QContextMenuEvent *pEvent ) { @@ -586,7 +753,7 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Brainless public property accessors. +// QSampler::MainForm -- Brainless public property accessors. // The global options settings property. Options *MainForm::options (void) const @@ -610,12 +777,12 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Session file stuff. +// QSampler::MainForm -- Session file stuff. // Format the displayable session filename. QString MainForm::sessionName ( const QString& sFilename ) { - bool bCompletePath = (m_pOptions && m_pOptions->bCompletePath); + const bool bCompletePath = (m_pOptions && m_pOptions->bCompletePath); QString sSessionName = sFilename; if (sSessionName.isEmpty()) sSessionName = tr("Untitled") + QString::number(m_iUntitled); @@ -707,7 +874,8 @@ "\"%1\"\n\n" "Do you want to replace it?") .arg(sFilename), - tr("Replace"), tr("Cancel")) > 0) + QMessageBox::Yes | QMessageBox::No) + == QMessageBox::No) return false; } } @@ -730,11 +898,13 @@ "\"%1\"\n\n" "Do you want to save the changes?") .arg(sessionName(m_sFilename)), - tr("Save"), tr("Discard"), tr("Cancel"))) { - case 0: // Save... + QMessageBox::Save | + QMessageBox::Discard | + QMessageBox::Cancel)) { + case QMessageBox::Save: bClose = saveSession(false); // Fall thru.... - case 1: // Discard + case QMessageBox::Discard: break; default: // Cancel. bClose = false; @@ -746,15 +916,22 @@ if (bClose) { // Remove all channel strips from sight... m_pWorkspace->setUpdatesEnabled(false); - QWidgetList wlist = m_pWorkspace->windowList(); - for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) { - ChannelStrip *pChannelStrip = (ChannelStrip*) wlist.at(iChannel); + 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()); if (pChannelStrip) { Channel *pChannel = pChannelStrip->channel(); if (bForce && pChannel) pChannel->removeChannel(); delete pChannelStrip; } + if (pMdiSubWindow) + delete pMdiSubWindow; } m_pWorkspace->setUpdatesEnabled(true); // We're now clean, for sure. @@ -866,13 +1043,11 @@ QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Write the file. - int iErrors = 0; + 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; @@ -978,7 +1153,7 @@ QMap midiInstrumentMap; int *piMaps = ::lscp_list_midi_instrument_maps(m_pClient); for (int iMap = 0; piMaps && piMaps[iMap] >= 0; iMap++) { - int iMidiMap = piMaps[iMap]; + const int iMidiMap = piMaps[iMap]; const char *pszMapName = ::lscp_get_midi_instrument_map_name(m_pClient, iMidiMap); ts << "# " << tr("MIDI instrument map") << " " << iMap; @@ -1045,65 +1220,69 @@ #endif // CONFIG_MIDI_INSTRUMENT // Sampler channel mapping. - QWidgetList wlist = m_pWorkspace->windowList(); - for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) { - ChannelStrip* pChannelStrip - = static_cast (wlist.at(iChannel)); + 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()); if (pChannelStrip) { Channel *pChannel = pChannelStrip->channel(); if (pChannel) { - ts << "# " << tr("Channel") << " " << iChannel << endl; + const int iChannelID = pChannel->channelID(); + ts << "# " << tr("Channel") << " " << iChannelID << endl; ts << "ADD CHANNEL" << endl; if (audioDeviceMap.isEmpty()) { - ts << "SET CHANNEL AUDIO_OUTPUT_TYPE " << iChannel + ts << "SET CHANNEL AUDIO_OUTPUT_TYPE " << iChannelID << " " << pChannel->audioDriver() << endl; } else { - ts << "SET CHANNEL AUDIO_OUTPUT_DEVICE " << iChannel + ts << "SET CHANNEL AUDIO_OUTPUT_DEVICE " << iChannelID << " " << audioDeviceMap[pChannel->audioDevice()] << endl; } if (midiDeviceMap.isEmpty()) { - ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannel + ts << "SET CHANNEL MIDI_INPUT_TYPE " << iChannelID << " " << pChannel->midiDriver() << endl; } else { - ts << "SET CHANNEL MIDI_INPUT_DEVICE " << iChannel + ts << "SET CHANNEL MIDI_INPUT_DEVICE " << iChannelID << " " << midiDeviceMap[pChannel->midiDevice()] << endl; } - ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannel + ts << "SET CHANNEL MIDI_INPUT_PORT " << iChannelID << " " << pChannel->midiPort() << endl; - ts << "SET CHANNEL MIDI_INPUT_CHANNEL " << iChannel << " "; + ts << "SET CHANNEL MIDI_INPUT_CHANNEL " << iChannelID << " "; if (pChannel->midiChannel() == LSCP_MIDI_CHANNEL_ALL) ts << "ALL"; else ts << pChannel->midiChannel(); ts << endl; ts << "LOAD ENGINE " << pChannel->engineName() - << " " << iChannel << endl; + << " " << iChannelID << endl; if (pChannel->instrumentStatus() < 100) ts << "# "; ts << "LOAD INSTRUMENT NON_MODAL '" << pChannel->instrumentFile() << "' " - << pChannel->instrumentNr() << " " << iChannel << endl; + << pChannel->instrumentNr() << " " << iChannelID << endl; ChannelRoutingMap::ConstIterator audioRoute; for (audioRoute = pChannel->audioRouting().begin(); audioRoute != pChannel->audioRouting().end(); ++audioRoute) { - ts << "SET CHANNEL AUDIO_OUTPUT_CHANNEL " << iChannel + ts << "SET CHANNEL AUDIO_OUTPUT_CHANNEL " << iChannelID << " " << audioRoute.key() << " " << audioRoute.value() << endl; } - ts << "SET CHANNEL VOLUME " << iChannel + ts << "SET CHANNEL VOLUME " << iChannelID << " " << pChannel->volume() << endl; if (pChannel->channelMute()) - ts << "SET CHANNEL MUTE " << iChannel << " 1" << endl; + ts << "SET CHANNEL MUTE " << iChannelID << " 1" << endl; if (pChannel->channelSolo()) - ts << "SET CHANNEL SOLO " << iChannel << " 1" << endl; -#ifdef CONFIG_MIDI_INSTRUMENT + ts << "SET CHANNEL SOLO " << iChannelID << " 1" << endl; + #ifdef CONFIG_MIDI_INSTRUMENT if (pChannel->midiMap() >= 0) { - ts << "SET CHANNEL MIDI_INSTRUMENT_MAP " << iChannel + ts << "SET CHANNEL MIDI_INSTRUMENT_MAP " << iChannelID << " " << midiInstrumentMap[pChannel->midiMap()] << endl; } -#endif -#ifdef CONFIG_FXSEND - int iChannelID = pChannel->channelID(); + #endif + #ifdef CONFIG_FXSEND int *piFxSends = ::lscp_list_fxsends(m_pClient, iChannelID); for (int iFxSend = 0; piFxSends && piFxSends[iFxSend] >= 0; @@ -1111,7 +1290,7 @@ lscp_fxsend_info_t *pFxSendInfo = ::lscp_get_fxsend_info( m_pClient, iChannelID, piFxSends[iFxSend]); if (pFxSendInfo) { - ts << "CREATE FX_SEND " << iChannel + ts << "CREATE FX_SEND " << iChannelID << " " << pFxSendInfo->midi_controller; if (pFxSendInfo->name) ts << " '" << pFxSendInfo->name << "'"; @@ -1121,23 +1300,23 @@ piRouting && piRouting[iAudioSrc] >= 0; iAudioSrc++) { ts << "SET FX_SEND AUDIO_OUTPUT_CHANNEL " - << iChannel + << iChannelID << " " << iFxSend << " " << iAudioSrc << " " << piRouting[iAudioSrc] << endl; } -#ifdef CONFIG_FXSEND_LEVEL - ts << "SET FX_SEND LEVEL " << iChannel + #ifdef CONFIG_FXSEND_LEVEL + ts << "SET FX_SEND LEVEL " << iChannelID << " " << iFxSend << " " << pFxSendInfo->level << endl; -#endif + #endif } // Check for errors... else if (::lscp_client_get_errno(m_pClient)) { appendMessagesClient("lscp_get_fxsend_info"); iErrors++; } } -#endif + #endif ts << endl; } } @@ -1190,7 +1369,7 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- File Action slots. +// QSampler::MainForm -- File Action slots. // Create a new sampler session. void MainForm::fileNew (void) @@ -1214,7 +1393,7 @@ // Retrive filename index from action data... QAction *pAction = qobject_cast (sender()); if (pAction && m_pOptions) { - int iIndex = pAction->data().toInt(); + const int iIndex = pAction->data().toInt(); if (iIndex >= 0 && iIndex < m_pOptions->recentFiles.count()) { QString sFilename = m_pOptions->recentFiles[iIndex]; // Check if we can safely close the current session... @@ -1248,15 +1427,34 @@ return; // Ask user whether he/she want's an internal sampler reset... - if (QMessageBox::warning(this, - QSAMPLER_TITLE ": " + tr("Warning"), - tr("Resetting the sampler instance will close\n" - "all device and channel configurations.\n\n" - "Please note that this operation may cause\n" - "temporary MIDI and Audio disruption.\n\n" - "Do you want to reset the sampler engine now?"), - tr("Reset"), tr("Cancel")) > 0) - return; + if (m_pOptions && m_pOptions->bConfirmReset) { + const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning"); + const QString& sText = tr( + "Resetting the sampler instance will close\n" + "all device and channel configurations.\n\n" + "Please note that this operation may cause\n" + "temporary MIDI and Audio disruption.\n\n" + "Do you want to reset the sampler engine now?"); + #if 0 + if (QMessageBox::warning(this, sTitle, sText, + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) + return; + #else + QMessageBox mbox(this); + mbox.setIcon(QMessageBox::Warning); + mbox.setWindowTitle(sTitle); + mbox.setText(sText); + mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + QCheckBox cbox(tr("Don't ask this again")); + cbox.setChecked(false); + cbox.blockSignals(true); + mbox.addButton(&cbox, QMessageBox::ActionRole); + if (mbox.exec() == QMessageBox::Cancel) + return; + if (cbox.isChecked()) + m_pOptions->bConfirmReset = false; + #endif + } // Trye closing the current session, first... if (!closeSession(true)) @@ -1288,15 +1486,34 @@ // Ask user whether he/she want's a complete restart... // (if we're currently up and running) - if (bRestart && m_pClient) { - bRestart = (QMessageBox::warning(this, - QSAMPLER_TITLE ": " + tr("Warning"), - tr("New settings will be effective after\n" + if (m_pOptions && m_pOptions->bConfirmRestart) { + const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning"); + const QString& sText = tr( + "New settings will be effective after\n" "restarting the client/server connection.\n\n" "Please note that this operation may cause\n" "temporary MIDI and Audio disruption.\n\n" - "Do you want to restart the connection now?"), - tr("Restart"), tr("Cancel")) == 0); + "Do you want to restart the connection now?"); + #if 0 + if (QMessageBox::warning(this, sTitle, sText, + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) + bRestart = false; + #else + QMessageBox mbox(this); + mbox.setIcon(QMessageBox::Warning); + mbox.setWindowTitle(sTitle); + mbox.setText(sText); + mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + QCheckBox cbox(tr("Don't ask this again")); + cbox.setChecked(false); + cbox.blockSignals(true); + mbox.addButton(&cbox, QMessageBox::ActionRole); + if (mbox.exec() == QMessageBox::Cancel) + bRestart = false; + else + if (cbox.isChecked()) + m_pOptions->bConfirmRestart = false; + #endif } // Are we still for it? @@ -1318,11 +1535,18 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Edit Action slots. +// QSampler::MainForm -- Edit Action slots. // Add a new sampler channel. void MainForm::editAddChannel (void) { + ++m_iDirtySetup; + addChannelStrip(); + --m_iDirtySetup; +} + +void MainForm::addChannelStrip (void) +{ if (m_pClient == NULL) return; @@ -1346,8 +1570,7 @@ } // Do we auto-arrange? - if (m_pOptions && m_pOptions->bAutoArrange) - channelsArrange(); + channelsArrangeAuto(); // Make that an overall update. m_iDirtyCount++; @@ -1358,10 +1581,17 @@ // Remove current sampler channel. void MainForm::editRemoveChannel (void) { + ++m_iDirtySetup; + removeChannelStrip(); + --m_iDirtySetup; +} + +void MainForm::removeChannelStrip (void) +{ if (m_pClient == NULL) return; - ChannelStrip* pChannelStrip = activeChannelStrip(); + ChannelStrip *pChannelStrip = activeChannelStrip(); if (pChannelStrip == NULL) return; @@ -1371,14 +1601,31 @@ // Prompt user if he/she's sure about this... if (m_pOptions && m_pOptions->bConfirmRemove) { - if (QMessageBox::warning(this, - QSAMPLER_TITLE ": " + tr("Warning"), - tr("About to remove channel:\n\n" + const QString& sTitle = QSAMPLER_TITLE ": " + tr("Warning"); + const QString& sText = tr( + "About to remove channel:\n\n" "%1\n\n" "Are you sure?") - .arg(pChannelStrip->windowTitle()), - tr("OK"), tr("Cancel")) > 0) + .arg(pChannelStrip->windowTitle()); + #if 0 + if (QMessageBox::warning(this, sTitle, sText, + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) + return; + #else + QMessageBox mbox(this); + mbox.setIcon(QMessageBox::Warning); + mbox.setWindowTitle(sTitle); + mbox.setText(sText); + mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + QCheckBox cbox(tr("Don't ask this again")); + cbox.setChecked(false); + cbox.blockSignals(true); + mbox.addButton(&cbox, QMessageBox::ActionRole); + if (mbox.exec() == QMessageBox::Cancel) return; + if (cbox.isChecked()) + m_pOptions->bConfirmRemove = false; + #endif } // Remove the existing sampler channel. @@ -1386,11 +1633,7 @@ return; // Just delete the channel strip. - delete pChannelStrip; - - // Do we auto-arrange? - if (m_pOptions && m_pOptions->bAutoArrange) - channelsArrange(); + destroyChannelStrip(pChannelStrip); // We'll be dirty, for sure... m_iDirtyCount++; @@ -1404,7 +1647,7 @@ if (m_pClient == NULL) return; - ChannelStrip* pChannelStrip = activeChannelStrip(); + ChannelStrip *pChannelStrip = activeChannelStrip(); if (pChannelStrip == NULL) return; @@ -1419,7 +1662,7 @@ if (m_pClient == NULL) return; - ChannelStrip* pChannelStrip = activeChannelStrip(); + ChannelStrip *pChannelStrip = activeChannelStrip(); if (pChannelStrip == NULL) return; @@ -1434,7 +1677,7 @@ if (m_pClient == NULL) return; - ChannelStrip* pChannelStrip = activeChannelStrip(); + ChannelStrip *pChannelStrip = activeChannelStrip(); if (pChannelStrip == NULL) return; @@ -1452,9 +1695,14 @@ // Invoque the channel strip procedure, // for all channels out there... m_pWorkspace->setUpdatesEnabled(false); - QWidgetList wlist = m_pWorkspace->windowList(); - for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) { - ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel); + 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()); if (pChannelStrip) pChannelStrip->channelReset(); } @@ -1463,7 +1711,7 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- View Action slots. +// QSampler::MainForm -- View Action slots. // Show/hide the main program window menubar. void MainForm::viewMenubar ( bool bOn ) @@ -1557,28 +1805,31 @@ OptionsForm* pOptionsForm = new OptionsForm(this); if (pOptionsForm) { // Check out some initial nullities(tm)... - ChannelStrip* pChannelStrip = activeChannelStrip(); + ChannelStrip *pChannelStrip = activeChannelStrip(); if (m_pOptions->sDisplayFont.isEmpty() && pChannelStrip) m_pOptions->sDisplayFont = pChannelStrip->displayFont().toString(); if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages) m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString(); // To track down deferred or immediate changes. - QString sOldServerHost = m_pOptions->sServerHost; - int iOldServerPort = m_pOptions->iServerPort; - int iOldServerTimeout = m_pOptions->iServerTimeout; - bool bOldServerStart = m_pOptions->bServerStart; - QString sOldServerCmdLine = m_pOptions->sServerCmdLine; - QString sOldDisplayFont = m_pOptions->sDisplayFont; - bool bOldDisplayEffect = m_pOptions->bDisplayEffect; - int iOldMaxVolume = m_pOptions->iMaxVolume; - QString sOldMessagesFont = m_pOptions->sMessagesFont; - bool bOldKeepOnTop = m_pOptions->bKeepOnTop; - bool bOldStdoutCapture = m_pOptions->bStdoutCapture; - int bOldMessagesLimit = m_pOptions->bMessagesLimit; - int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines; - bool bOldCompletePath = m_pOptions->bCompletePath; - bool bOldInstrumentNames = m_pOptions->bInstrumentNames; - int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles; + 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; // Load the current setup settings. pOptionsForm->setup(m_pOptions); // Show the setup dialog... @@ -1587,14 +1838,20 @@ if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) || (!bOldStdoutCapture && m_pOptions->bStdoutCapture) || ( bOldKeepOnTop && !m_pOptions->bKeepOnTop) || - (!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."), tr("OK")); + "next time you start this program.")); updateMessagesCapture(); } // Check wheather something immediate has changed. + if (( bOldMessagesLog && !m_pOptions->bMessagesLog) || + (!bOldMessagesLog && m_pOptions->bMessagesLog) || + (sOldMessagesLogPath != m_pOptions->sMessagesLogPath)) + m_pMessages->setLogging( + m_pOptions->bMessagesLog, m_pOptions->sMessagesLogPath); if (( bOldCompletePath && !m_pOptions->bCompletePath) || (!bOldCompletePath && m_pOptions->bCompletePath) || (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles)) @@ -1635,34 +1892,31 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Channels action slots. +// QSampler::MainForm -- Channels action slots. // Arrange channel strips. void MainForm::channelsArrange (void) { // Full width vertical tiling - QWidgetList wlist = m_pWorkspace->windowList(); + const QList& wlist + = m_pWorkspace->subWindowList(); if (wlist.isEmpty()) return; m_pWorkspace->setUpdatesEnabled(false); int y = 0; - for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) { - ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel); - /* if (pChannelStrip->testWState(WState_Maximized | WState_Minimized)) { - // Prevent flicker... - pChannelStrip->hide(); - pChannelStrip->showNormal(); - } */ - pChannelStrip->adjustSize(); - int iWidth = m_pWorkspace->width(); - if (iWidth < pChannelStrip->width()) - iWidth = pChannelStrip->width(); - // int iHeight = pChannelStrip->height() - // + pChannelStrip->parentWidget()->baseSize().height(); - int iHeight = pChannelStrip->parentWidget()->frameGeometry().height(); - pChannelStrip->parentWidget()->setGeometry(0, y, iWidth, iHeight); - y += iHeight; + 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; + } } m_pWorkspace->setUpdatesEnabled(true); @@ -1680,13 +1934,19 @@ 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) @@ -1698,57 +1958,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 + list << tr("Channel MIDI event support disabled."); +#endif +#ifndef CONFIG_EVENT_DEVICE_MIDI + list << tr("Device MIDI event support disabled."); #endif +#ifndef CONFIG_MAX_VOICES + 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(); @@ -1778,7 +2036,7 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Main window stabilization. +// QSampler::MainForm -- Main window stabilization. void MainForm::stabilizeForm (void) { @@ -1789,9 +2047,11 @@ setWindowTitle(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName)); // Update the main menu state... - ChannelStrip* pChannelStrip = activeChannelStrip(); - bool bHasClient = (m_pOptions != NULL && m_pClient != NULL); - bool bHasChannel = (bHasClient && pChannelStrip != NULL); + 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 bHasChannels = (bHasClient && wlist.count() > 0); m_ui.fileNewAction->setEnabled(bHasClient); m_ui.fileOpenAction->setEnabled(bHasClient); m_ui.fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0); @@ -1807,7 +2067,7 @@ m_ui.editEditChannelAction->setEnabled(false); #endif m_ui.editResetChannelAction->setEnabled(bHasChannel); - m_ui.editResetAllChannelsAction->setEnabled(bHasChannel); + m_ui.editResetAllChannelsAction->setEnabled(bHasChannels); m_ui.viewMessagesAction->setChecked(m_pMessages && m_pMessages->isVisible()); #ifdef CONFIG_MIDI_INSTRUMENT m_ui.viewInstrumentsAction->setChecked(m_pInstrumentListForm @@ -1819,7 +2079,9 @@ m_ui.viewDevicesAction->setChecked(m_pDeviceForm && m_pDeviceForm->isVisible()); m_ui.viewDevicesAction->setEnabled(bHasClient); - m_ui.channelsArrangeAction->setEnabled(bHasChannel); + m_ui.viewMidiDeviceStatusMenu->setEnabled( + DeviceStatusForm::getInstances().size() > 0); + m_ui.channelsArrangeAction->setEnabled(bHasChannels); #ifdef CONFIG_VOLUME // Toolbar widgets are also affected... @@ -1869,7 +2131,7 @@ m_pVolumeSpinBox->setValue(iVolume); // Do it as commanded... - float fVolume = 0.01f * float(iVolume); + const float fVolume = 0.01f * float(iVolume); if (::lscp_set_volume(m_pClient, fVolume) == LSCP_OK) appendMessages(QObject::tr("Volume: %1.").arg(fVolume)); else @@ -1885,7 +2147,7 @@ // Channel change receiver slot. -void MainForm::channelStripChanged(ChannelStrip* pChannelStrip) +void MainForm::channelStripChanged ( ChannelStrip *pChannelStrip ) { // Add this strip to the changed list... if (!m_changedStrips.contains(pChannelStrip)) { @@ -1904,7 +2166,7 @@ void MainForm::updateSession (void) { #ifdef CONFIG_VOLUME - int iVolume = ::lroundf(100.0f * ::lscp_get_volume(m_pClient)); + const int iVolume = ::lroundf(100.0f * ::lscp_get_volume(m_pClient)); m_iVolumeChanging++; m_pVolumeSlider->setValue(iVolume); m_pVolumeSpinBox->setValue(iVolume); @@ -1912,7 +2174,7 @@ #endif #ifdef CONFIG_MIDI_INSTRUMENT // FIXME: Make some room for default instrument maps... - int iMaps = ::lscp_get_midi_instrument_maps(m_pClient); + const int iMaps = ::lscp_get_midi_instrument_maps(m_pClient); if (iMaps < 0) appendMessagesClient("lscp_get_midi_instrument_maps"); else if (iMaps < 1) { @@ -1923,6 +2185,25 @@ } #endif + updateAllChannelStrips(false); + + // Do we auto-arrange? + channelsArrangeAuto(); + + // Remember to refresh devices and instruments... + if (m_pInstrumentListForm) + m_pInstrumentListForm->refreshInstruments(); + if (m_pDeviceForm) + m_pDeviceForm->refreshDevices(); +} + + +void MainForm::updateAllChannelStrips ( bool bRemoveDeadStrips ) +{ + // Skip if setting up a new channel strip... + if (m_iDirtySetup > 0) + return; + // Retrieve the current channel list. int *piChannelIDs = ::lscp_list_channels(m_pClient); if (piChannelIDs == NULL) { @@ -1934,23 +2215,44 @@ } else { // Try to (re)create each channel. m_pWorkspace->setUpdatesEnabled(false); - for (int iChannel = 0; piChannelIDs[iChannel] >= 0; iChannel++) { + for (int iChannel = 0; piChannelIDs[iChannel] >= 0; ++iChannel) { // Check if theres already a channel strip for this one... if (!channelStrip(piChannelIDs[iChannel])) createChannelStrip(new Channel(piChannelIDs[iChannel])); } + // Do we auto-arrange? + 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()); + if (pChannelStrip) { + bool bExists = false; + for (int iChannel = 0; piChannelIDs[iChannel] >= 0; ++iChannel) { + Channel *pChannel = pChannelStrip->channel(); + if (pChannel == NULL) + break; + if (piChannelIDs[iChannel] == pChannel->channelID()) { + // strip exists, don't touch it + bExists = true; + break; + } + } + if (!bExists) + destroyChannelStrip(pChannelStrip); + } + } + } m_pWorkspace->setUpdatesEnabled(true); } - // Do we auto-arrange? - if (m_pOptions && m_pOptions->bAutoArrange) - channelsArrange(); - - // Remember to refresh devices and instruments... - if (m_pInstrumentListForm) - m_pInstrumentListForm->refreshInstruments(); - if (m_pDeviceForm) - m_pDeviceForm->refreshDevices(); + stabilizeForm(); } @@ -1961,7 +2263,7 @@ return; // Remove from list if already there (avoid duplicates) - int iIndex = m_pOptions->recentFiles.indexOf(sFilename); + const int iIndex = m_pOptions->recentFiles.indexOf(sFilename); if (iIndex >= 0) m_pOptions->recentFiles.removeAt(iIndex); // Put it to front... @@ -2000,13 +2302,18 @@ void MainForm::updateInstrumentNames (void) { // Full channel list update... - QWidgetList wlist = m_pWorkspace->windowList(); + const QList& wlist + = m_pWorkspace->subWindowList(); if (wlist.isEmpty()) return; m_pWorkspace->setUpdatesEnabled(false); - for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) { - ChannelStrip *pChannelStrip = (ChannelStrip *) wlist.at(iChannel); + 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()); if (pChannelStrip) pChannelStrip->updateInstrumentName(true); } @@ -2023,19 +2330,25 @@ // Check if display font is legal. if (m_pOptions->sDisplayFont.isEmpty()) return; + // Realize it. QFont font; if (!font.fromString(m_pOptions->sDisplayFont)) return; // Full channel list update... - QWidgetList wlist = m_pWorkspace->windowList(); + const QList& wlist + = m_pWorkspace->subWindowList(); if (wlist.isEmpty()) return; m_pWorkspace->setUpdatesEnabled(false); - for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) { - ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel); + 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()); if (pChannelStrip) pChannelStrip->setDisplayFont(font); } @@ -2047,13 +2360,18 @@ void MainForm::updateDisplayEffect (void) { // Full channel list update... - QWidgetList wlist = m_pWorkspace->windowList(); + const QList& wlist + = m_pWorkspace->subWindowList(); if (wlist.isEmpty()) return; m_pWorkspace->setUpdatesEnabled(false); - for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) { - ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel); + 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()); if (pChannelStrip) pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect); } @@ -2075,13 +2393,18 @@ #endif // Full channel list update... - QWidgetList wlist = m_pWorkspace->windowList(); + const QList& wlist + = m_pWorkspace->subWindowList(); if (wlist.isEmpty()) return; m_pWorkspace->setUpdatesEnabled(false); - for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) { - ChannelStrip* pChannelStrip = (ChannelStrip*) wlist.at(iChannel); + 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()); if (pChannelStrip) pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume); } @@ -2090,7 +2413,7 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Messages window form handlers. +// QSampler::MainForm -- Messages window form handlers. // Messages output methods. void MainForm::appendMessages( const QString& s ) @@ -2115,18 +2438,34 @@ m_pMessages->appendMessagesText(s); } -void MainForm::appendMessagesError( const QString& s ) +void MainForm::appendMessagesError( const QString& sText ) { if (m_pMessages) m_pMessages->show(); - appendMessagesColor(s.simplified(), "#ff0000"); + appendMessagesColor(sText.simplified(), "#ff0000"); // Make it look responsive...:) QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); - QMessageBox::critical(this, - QSAMPLER_TITLE ": " + tr("Error"), s, tr("Cancel")); + if (m_pOptions && m_pOptions->bConfirmError) { + const QString& sTitle = QSAMPLER_TITLE ": " + 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.setStandardButtons(QMessageBox::Cancel); + QCheckBox cbox(tr("Don't show this again")); + cbox.setChecked(false); + cbox.blockSignals(true); + mbox.addButton(&cbox, QMessageBox::ActionRole); + if (mbox.exec() && cbox.isChecked()) + m_pOptions->bConfirmError = false; + #endif + } } @@ -2186,10 +2525,10 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- MDI channel strip management. +// QSampler::MainForm -- MDI channel strip management. // The channel strip creation executive. -ChannelStrip* MainForm::createChannelStrip ( Channel *pChannel ) +ChannelStrip *MainForm::createChannelStrip ( Channel *pChannel ) { if (m_pClient == NULL || pChannel == NULL) return NULL; @@ -2205,21 +2544,23 @@ pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect); // We'll need a display font. QFont font; - if (font.fromString(m_pOptions->sDisplayFont)) + if (!m_pOptions->sDisplayFont.isEmpty() && + font.fromString(m_pOptions->sDisplayFont)) pChannelStrip->setDisplayFont(font); // Maximum allowed volume setting. pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume); } // Add it to workspace... - m_pWorkspace->addWindow(pChannelStrip, Qt::FramelessWindowHint); + m_pWorkspace->addSubWindow(pChannelStrip, + Qt::SubWindow | Qt::FramelessWindowHint); // Actual channel strip setup... pChannelStrip->setup(pChannel); QObject::connect(pChannelStrip, - SIGNAL(channelChanged(ChannelStrip*)), - SLOT(channelStripChanged(ChannelStrip*))); + SIGNAL(channelChanged(ChannelStrip *)), + SLOT(channelStripChanged(ChannelStrip *))); // Now we show up us to the world. pChannelStrip->show(); @@ -2232,34 +2573,68 @@ } +void MainForm::destroyChannelStrip ( ChannelStrip *pChannelStrip ) +{ + QMdiSubWindow *pMdiSubWindow + = static_cast (pChannelStrip->parentWidget()); + if (pMdiSubWindow == NULL) + return; + + // Just delete the channel strip. + delete pChannelStrip; + delete pMdiSubWindow; + + // Do we auto-arrange? + channelsArrangeAuto(); +} + + // Retrieve the active channel strip. -ChannelStrip* MainForm::activeChannelStrip (void) +ChannelStrip *MainForm::activeChannelStrip (void) { - return static_cast (m_pWorkspace->activeWindow()); + QMdiSubWindow *pMdiSubWindow = m_pWorkspace->activeSubWindow(); + if (pMdiSubWindow) + return static_cast (pMdiSubWindow->widget()); + else + return NULL; } // Retrieve a channel strip by index. -ChannelStrip* MainForm::channelStripAt ( int iChannel ) +ChannelStrip *MainForm::channelStripAt ( int iStrip ) { - QWidgetList wlist = m_pWorkspace->windowList(); + if (!m_pWorkspace) return NULL; + + const QList& wlist + = m_pWorkspace->subWindowList(); if (wlist.isEmpty()) return NULL; - return static_cast (wlist.at(iChannel)); + if (iStrip < 0 || iStrip >= wlist.count()) + return NULL; + + QMdiSubWindow *pMdiSubWindow = wlist.at(iStrip); + if (pMdiSubWindow) + return static_cast (pMdiSubWindow->widget()); + else + return NULL; } // Retrieve a channel strip by sampler channel id. -ChannelStrip* MainForm::channelStrip ( int iChannelID ) +ChannelStrip *MainForm::channelStrip ( int iChannelID ) { - QWidgetList wlist = m_pWorkspace->windowList(); + const QList& wlist + = m_pWorkspace->subWindowList(); if (wlist.isEmpty()) return NULL; - for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) { - ChannelStrip* pChannelStrip - = static_cast (wlist.at(iChannel)); + 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()); if (pChannelStrip) { Channel *pChannel = pChannelStrip->channel(); if (pChannel && pChannel->channelID() == iChannelID) @@ -2279,19 +2654,23 @@ m_ui.channelsMenu->addAction(m_ui.channelsArrangeAction); m_ui.channelsMenu->addAction(m_ui.channelsAutoArrangeAction); - QWidgetList wlist = m_pWorkspace->windowList(); + const QList& wlist + = m_pWorkspace->subWindowList(); if (!wlist.isEmpty()) { m_ui.channelsMenu->addSeparator(); - for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) { - ChannelStrip* pChannelStrip - = static_cast (wlist.at(iChannel)); + 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()); if (pChannelStrip) { QAction *pAction = m_ui.channelsMenu->addAction( pChannelStrip->windowTitle(), this, SLOT(channelsMenuActivated())); pAction->setCheckable(true); pAction->setChecked(activeChannelStrip() == pChannelStrip); - pAction->setData(iChannel); + pAction->setData(iStrip); } } } @@ -2306,7 +2685,7 @@ if (pAction == NULL) return; - ChannelStrip* pChannelStrip = channelStripAt(pAction->data().toInt()); + ChannelStrip *pChannelStrip = channelStripAt(pAction->data().toInt()); if (pChannelStrip) { pChannelStrip->showNormal(); pChannelStrip->setFocus(); @@ -2315,7 +2694,7 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Timer stuff. +// QSampler::MainForm -- Timer stuff. // Set the pseudo-timer delay schedule. void MainForm::startSchedule ( int iStartDelay ) @@ -2356,7 +2735,7 @@ ChannelStrip *pChannelStrip = iter.next(); // If successfull, remove from pending list... if (pChannelStrip->updateChannelInfo()) { - int iChannelStrip = m_changedStrips.indexOf(pChannelStrip); + const int iChannelStrip = m_changedStrips.indexOf(pChannelStrip); if (iChannelStrip >= 0) m_changedStrips.removeAt(iChannelStrip); } @@ -2367,11 +2746,14 @@ if (m_iTimerSlot >= m_pOptions->iAutoRefreshTime) { m_iTimerSlot = 0; // Update the channel stream usage for each strip... - QWidgetList wlist = m_pWorkspace->windowList(); - for (int iChannel = 0; - iChannel < (int) wlist.count(); iChannel++) { - ChannelStrip* pChannelStrip - = (ChannelStrip*) wlist.at(iChannel); + 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()); if (pChannelStrip && pChannelStrip->isVisible()) pChannelStrip->updateChannelUsage(); } @@ -2385,7 +2767,7 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Server stuff. +// QSampler::MainForm -- Server stuff. // Start linuxsampler server... void MainForm::startServer (void) @@ -2399,17 +2781,13 @@ // Is the server process instance still here? if (m_pServer) { - switch (QMessageBox::warning(this, + if (QMessageBox::warning(this, QSAMPLER_TITLE ": " + tr("Warning"), tr("Could not start the LinuxSampler server.\n\n" "Maybe it is already started."), - tr("Stop"), tr("Kill"), tr("Cancel"))) { - case 0: + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { m_pServer->terminate(); - break; - case 1: m_pServer->kill(); - break; } return; } @@ -2423,20 +2801,16 @@ // OK. Let's build the startup process... m_pServer = new QProcess(); - bForceServerStop = true; + m_bForceServerStop = true; // Setup stdout/stderr capture... -// if (m_pOptions->bStdoutCapture) { -#if QT_VERSION >= 0x040200 - m_pServer->setProcessChannelMode(QProcess::ForwardedChannels); -#endif - QObject::connect(m_pServer, - SIGNAL(readyReadStandardOutput()), - SLOT(readServerStdout())); - QObject::connect(m_pServer, - SIGNAL(readyReadStandardError()), - SLOT(readServerStdout())); -// } + m_pServer->setProcessChannelMode(QProcess::ForwardedChannels); + QObject::connect(m_pServer, + SIGNAL(readyReadStandardOutput()), + SLOT(readServerStdout())); + QObject::connect(m_pServer, + SIGNAL(readyReadStandardError()), + SLOT(readServerStdout())); // The unforgiveable signal communication... QObject::connect(m_pServer, @@ -2471,7 +2845,7 @@ // Stop linuxsampler server... -void MainForm::stopServer (bool bInteractive) +void MainForm::stopServer ( bool bInteractive ) { // Stop client code. stopClient(); @@ -2483,25 +2857,24 @@ "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 or to keep running in\n" - "the background?"), - tr("Stop"), tr("Keep Running")) == 1) - { - bForceServerStop = false; + "Do you want LinuxSampler to stop?"), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::Yes) == QMessageBox::No) { + m_bForceServerStop = false; } } // 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 + #else // Try softly... m_pServer->terminate(); -#endif + #endif } } // Do final processing anyway. else processServerExit(); @@ -2532,7 +2905,7 @@ 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... @@ -2557,7 +2930,7 @@ //------------------------------------------------------------------------- -// qsamplerMainForm -- Client stuff. +// QSampler::MainForm -- Client stuff. // The LSCP client callback procedure. lscp_status_t qsampler_client_callback ( lscp_client_t */*pClient*/, @@ -2571,7 +2944,7 @@ // as this is run under some other thread context. // A custom event must be posted here... QApplication::postEvent(pMainForm, - new CustomEvent(event, pchData, cchData)); + new LscpEvent(event, pchData, cchData)); return LSCP_OK; } @@ -2609,6 +2982,7 @@ stabilizeForm(); return false; } + // Just set receive timeout value, blindly. ::lscp_client_set_timeout(m_pClient, m_pOptions->iServerTimeout); appendMessages( @@ -2616,15 +2990,34 @@ .arg(::lscp_client_get_timeout(m_pClient))); // Subscribe to channel info change notifications... + if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_COUNT) != LSCP_OK) + appendMessagesClient("lscp_client_subscribe(CHANNEL_COUNT)"); if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_INFO) != LSCP_OK) appendMessagesClient("lscp_client_subscribe(CHANNEL_INFO)"); -#if CONFIG_LSCP_CHANNEL_MIDI + DeviceStatusForm::onDevicesChanged(); // initialize + updateViewMidiDeviceStatusMenu(); + if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT) != LSCP_OK) + appendMessagesClient("lscp_client_subscribe(MIDI_INPUT_DEVICE_COUNT)"); + if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_INFO) != LSCP_OK) + appendMessagesClient("lscp_client_subscribe(MIDI_INPUT_DEVICE_INFO)"); + if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_COUNT) != LSCP_OK) + appendMessagesClient("lscp_client_subscribe(AUDIO_OUTPUT_DEVICE_COUNT)"); + if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_INFO) != LSCP_OK) + appendMessagesClient("lscp_client_subscribe(AUDIO_OUTPUT_DEVICE_INFO)"); + +#if CONFIG_EVENT_CHANNEL_MIDI // Subscribe to channel MIDI data notifications... if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_CHANNEL_MIDI) != LSCP_OK) appendMessagesClient("lscp_client_subscribe(CHANNEL_MIDI)"); #endif +#if CONFIG_EVENT_DEVICE_MIDI + // Subscribe to channel MIDI data notifications... + if (::lscp_client_subscribe(m_pClient, LSCP_EVENT_DEVICE_MIDI) != LSCP_OK) + appendMessagesClient("lscp_client_subscribe(DEVICE_MIDI)"); +#endif + // We may stop scheduling around. stopSchedule(); @@ -2650,6 +3043,9 @@ } } + // send the current / loaded fine tuning settings to the sampler + m_pOptions->sendFineTuningSettings(); + // Make a new session return newSession(); } @@ -2677,10 +3073,18 @@ closeSession(false); // Close us as a client... -#if CONFIG_LSCP_CHANNEL_MIDI +#if CONFIG_EVENT_DEVICE_MIDI + ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_DEVICE_MIDI); +#endif +#if CONFIG_EVENT_CHANNEL_MIDI ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_CHANNEL_MIDI); #endif + ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_INFO); + ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_AUDIO_OUTPUT_DEVICE_COUNT); + ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_INFO); + ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT); ::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; @@ -2700,10 +3104,11 @@ // Channel strip activation/selection. -void MainForm::activateStrip ( QWidget *pWidget ) +void MainForm::activateStrip ( QMdiSubWindow *pMdiSubWindow ) { - ChannelStrip *pChannelStrip - = static_cast (pWidget); + ChannelStrip *pChannelStrip = NULL; + if (pMdiSubWindow) + pChannelStrip = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) pChannelStrip->setSelected(true); @@ -2711,6 +3116,30 @@ } +// Channel toolbar orientation change. +void MainForm::channelsToolbarOrientation ( Qt::Orientation orientation ) +{ +#ifdef CONFIG_VOLUME + m_pVolumeSlider->setOrientation(orientation); + if (orientation == Qt::Horizontal) { + m_pVolumeSlider->setMinimumHeight(24); + m_pVolumeSlider->setMaximumHeight(32); + m_pVolumeSlider->setMinimumWidth(120); + m_pVolumeSlider->setMaximumWidth(640); + m_pVolumeSpinBox->setMaximumWidth(64); + m_pVolumeSpinBox->setButtonSymbols(QSpinBox::UpDownArrows); + } else { + m_pVolumeSlider->setMinimumHeight(120); + m_pVolumeSlider->setMaximumHeight(480); + m_pVolumeSlider->setMinimumWidth(24); + m_pVolumeSlider->setMaximumWidth(32); + m_pVolumeSpinBox->setMaximumWidth(32); + m_pVolumeSpinBox->setButtonSymbols(QSpinBox::NoButtons); + } +#endif +} + + } // namespace QSampler