--- qsampler/trunk/src/qsamplerMainForm.cpp 2008/02/16 19:41:05 1698 +++ qsampler/trunk/src/qsamplerMainForm.cpp 2013/07/08 10:06:57 2459 @@ -1,7 +1,7 @@ // qsamplerMainForm.cpp // /**************************************************************************** - Copyright (C) 2004-2007, rncbc aka Rui Nuno Capela. All rights reserved. + Copyright (C) 2004-2013, rncbc aka Rui Nuno Capela. All rights reserved. Copyright (C) 2007, 2008 Christian Schoenebeck This program is free software; you can redistribute it and/or @@ -35,8 +35,10 @@ #include "qsamplerOptionsForm.h" #include "qsamplerDeviceStatusForm.h" +#include +#include + #include -#include #include #include @@ -56,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 @@ -85,6 +96,36 @@ #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. @@ -97,18 +138,21 @@ #define QSAMPLER_STATUS_SESSION 3 // Current session modification state. +// Specialties for thread-callback comunication. +#define QSAMPLER_LSCP_EVENT QEvent::Type(QEvent::User + 1) + + //------------------------------------------------------------------------- -// CustomEvent -- specialty for callback comunication. +// LscpEvent -- specialty for LSCP callback comunication. -#define QSAMPLER_CUSTOM_EVENT QEvent::Type(QEvent::User + 0) -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); @@ -161,10 +205,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... @@ -203,12 +272,13 @@ #endif // Make it an MDI workspace. - m_pWorkspace = new QWorkspace(this); - m_pWorkspace->setScrollBarsEnabled(true); + m_pWorkspace = new QMdiArea(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); @@ -337,6 +407,11 @@ 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; @@ -375,22 +450,28 @@ // 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(); @@ -421,7 +502,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); @@ -465,7 +546,7 @@ // 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(); @@ -482,9 +563,10 @@ void MainForm::closeEvent ( QCloseEvent *pCloseEvent ) { - if (queryClose()) + if (queryClose()) { + DeviceStatusForm::deleteAllInstances(); pCloseEvent->accept(); - else + } else pCloseEvent->ignore(); } @@ -513,7 +595,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) @@ -547,66 +630,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(); + int iChannelID = pLscpEvent->data().toInt(); ChannelStrip *pChannelStrip = channelStrip(iChannelID); if (pChannelStrip) channelStripChanged(pChannelStrip); break; } case LSCP_EVENT_MIDI_INPUT_DEVICE_COUNT: + if (m_pDeviceForm) m_pDeviceForm->refreshDevices(); DeviceStatusForm::onDevicesChanged(); updateViewMidiDeviceStatusMenu(); - break; //TODO: refresh device dialog as well -#if CONFIG_LSCP_CHANNEL_MIDI + 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: { - const 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 -#if CONFIG_LSCP_DEVICE_MIDI + #endif + #if CONFIG_EVENT_DEVICE_MIDI case LSCP_EVENT_DEVICE_MIDI: { - const int iDeviceID = pEvent->data().section(' ', 0, 0).toInt(); - const int iPortID = pEvent->data().section(' ', 1, 1).toInt(); - DeviceStatusForm* pDeviceStatusForm = - DeviceStatusForm::getInstance(iDeviceID); + 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 + #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"); } } } -void MainForm::updateViewMidiDeviceStatusMenu() { + +// 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(); - for ( - std::map::const_iterator iter = statusForms.begin(); - iter != statusForms.end(); ++iter - ) { - DeviceStatusForm* pForm = iter->second; + 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( - pForm->visibleAction() - ); + pStatusForm->visibleAction()); } } + // Context menu event handler. void MainForm::contextMenuEvent( QContextMenuEvent *pEvent ) { @@ -738,7 +852,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; } } @@ -761,11 +876,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; @@ -777,15 +894,20 @@ 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); + QList wlist = m_pWorkspace->subWindowList(); + for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) { + ChannelStrip *pChannelStrip = NULL; + QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel); + 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. @@ -1076,10 +1198,12 @@ #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)); + QList wlist = m_pWorkspace->subWindowList(); + for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) { + ChannelStrip *pChannelStrip = NULL; + QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel); + if (pMdiSubWindow) + pChannelStrip = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) { Channel *pChannel = pChannelStrip->channel(); if (pChannel) { @@ -1127,13 +1251,13 @@ ts << "SET CHANNEL MUTE " << iChannel << " 1" << endl; if (pChannel->channelSolo()) ts << "SET CHANNEL SOLO " << iChannel << " 1" << endl; -#ifdef CONFIG_MIDI_INSTRUMENT + #ifdef CONFIG_MIDI_INSTRUMENT if (pChannel->midiMap() >= 0) { ts << "SET CHANNEL MIDI_INSTRUMENT_MAP " << iChannel << " " << midiInstrumentMap[pChannel->midiMap()] << endl; } -#endif -#ifdef CONFIG_FXSEND + #endif + #ifdef CONFIG_FXSEND int iChannelID = pChannel->channelID(); int *piFxSends = ::lscp_list_fxsends(m_pClient, iChannelID); for (int iFxSend = 0; @@ -1157,18 +1281,18 @@ << " " << iAudioSrc << " " << piRouting[iAudioSrc] << endl; } -#ifdef CONFIG_FXSEND_LEVEL + #ifdef CONFIG_FXSEND_LEVEL ts << "SET FX_SEND LEVEL " << iChannel << " " << 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; } } @@ -1286,7 +1410,8 @@ "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) + QMessageBox::Ok | QMessageBox::Cancel) + == QMessageBox::Cancel) return; // Trye closing the current session, first... @@ -1327,7 +1452,7 @@ "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); + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok); } // Are we still for it? @@ -1392,7 +1517,7 @@ if (m_pClient == NULL) return; - ChannelStrip* pChannelStrip = activeChannelStrip(); + ChannelStrip *pChannelStrip = activeChannelStrip(); if (pChannelStrip == NULL) return; @@ -1408,7 +1533,8 @@ "%1\n\n" "Are you sure?") .arg(pChannelStrip->windowTitle()), - tr("OK"), tr("Cancel")) > 0) + QMessageBox::Ok | QMessageBox::Cancel) + == QMessageBox::Cancel) return; } @@ -1416,16 +1542,11 @@ if (!pChannel->removeChannel()) return; - // Just delete the channel strip. - delete pChannelStrip; - - // Do we auto-arrange? - if (m_pOptions && m_pOptions->bAutoArrange) - channelsArrange(); - // We'll be dirty, for sure... m_iDirtyCount++; - stabilizeForm(); + + // Just delete the channel strip. + destroyChannelStrip(pChannelStrip); } @@ -1435,7 +1556,7 @@ if (m_pClient == NULL) return; - ChannelStrip* pChannelStrip = activeChannelStrip(); + ChannelStrip *pChannelStrip = activeChannelStrip(); if (pChannelStrip == NULL) return; @@ -1450,7 +1571,7 @@ if (m_pClient == NULL) return; - ChannelStrip* pChannelStrip = activeChannelStrip(); + ChannelStrip *pChannelStrip = activeChannelStrip(); if (pChannelStrip == NULL) return; @@ -1465,7 +1586,7 @@ if (m_pClient == NULL) return; - ChannelStrip* pChannelStrip = activeChannelStrip(); + ChannelStrip *pChannelStrip = activeChannelStrip(); if (pChannelStrip == NULL) return; @@ -1483,9 +1604,12 @@ // 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); + QList wlist = m_pWorkspace->subWindowList(); + for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) { + ChannelStrip *pChannelStrip = NULL; + QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel); + if (pMdiSubWindow) + pChannelStrip = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) pChannelStrip->channelReset(); } @@ -1588,7 +1712,7 @@ 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) @@ -1599,6 +1723,8 @@ int iOldServerTimeout = m_pOptions->iServerTimeout; bool bOldServerStart = m_pOptions->bServerStart; QString sOldServerCmdLine = m_pOptions->sServerCmdLine; + bool bOldMessagesLog = m_pOptions->bMessagesLog; + QString sOldMessagesLogPath = m_pOptions->sMessagesLogPath; QString sOldDisplayFont = m_pOptions->sDisplayFont; bool bOldDisplayEffect = m_pOptions->bDisplayEffect; int iOldMaxVolume = m_pOptions->iMaxVolume; @@ -1610,6 +1736,7 @@ bool bOldCompletePath = m_pOptions->bCompletePath; bool bOldInstrumentNames = m_pOptions->bInstrumentNames; int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles; + int iOldBaseFontSize = m_pOptions->iBaseFontSize; // Load the current setup settings. pOptionsForm->setup(m_pOptions); // Show the setup dialog... @@ -1618,14 +1745,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)) @@ -1672,28 +1805,33 @@ void MainForm::channelsArrange (void) { // Full width vertical tiling - QWidgetList wlist = m_pWorkspace->windowList(); + 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; + for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) { + ChannelStrip *pChannelStrip = NULL; + QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel); + if (pMdiSubWindow) + pChannelStrip = static_cast (pMdiSubWindow->widget()); + if (pChannelStrip) { + /* 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; + } } m_pWorkspace->setUpdatesEnabled(true); @@ -1780,6 +1918,21 @@ sText += tr("Instrument editing support disabled."); sText += "
"; #endif +#ifndef CONFIG_EVENT_CHANNEL_MIDI + sText += ""; + sText += tr("Channel MIDI event support disabled."); + sText += "
"; +#endif +#ifndef CONFIG_EVENT_DEVICE_MIDI + sText += ""; + sText += tr("Device MIDI event support disabled."); + sText += "
"; +#endif +#ifndef CONFIG_MAX_VOICES + sText += ""; + sText += tr("Runtime max. voices / disk streams support disabled."); + sText += "
"; +#endif sText += "
\n"; sText += tr("Using") + ": "; sText += ::lscp_client_package(); @@ -1820,9 +1973,10 @@ setWindowTitle(tr(QSAMPLER_TITLE " - [%1]").arg(sSessionName)); // Update the main menu state... - ChannelStrip* pChannelStrip = activeChannelStrip(); - bool bHasClient = (m_pOptions != NULL && m_pClient != NULL); + ChannelStrip *pChannelStrip = activeChannelStrip(); + bool bHasClient = (m_pOptions != NULL && m_pClient != NULL); bool bHasChannel = (bHasClient && pChannelStrip != NULL); + bool bHasChannels = (bHasClient && m_pWorkspace->subWindowList().count() > 0); m_ui.fileNewAction->setEnabled(bHasClient); m_ui.fileOpenAction->setEnabled(bHasClient); m_ui.fileSaveAction->setEnabled(bHasClient && m_iDirtyCount > 0); @@ -1838,7 +1992,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 @@ -1850,7 +2004,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... @@ -1916,7 +2072,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)) { @@ -1954,6 +2110,22 @@ } #endif + updateAllChannelStrips(false); + + // 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(); +} + + +void MainForm::updateAllChannelStrips ( bool bRemoveDeadStrips ) +{ // Retrieve the current channel list. int *piChannelIDs = ::lscp_list_channels(m_pClient); if (piChannelIDs == NULL) { @@ -1965,23 +2137,42 @@ } 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? + if (m_pOptions && m_pOptions->bAutoArrange) + channelsArrange(); + // remove dead channel strips + if (bRemoveDeadStrips) { + QList wlist = m_pWorkspace->subWindowList(); + for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) { + ChannelStrip *pChannelStrip = NULL; + QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel); + if (pMdiSubWindow) + pChannelStrip = static_cast (pMdiSubWindow->widget()); + if (pChannelStrip) { + bool bExists = false; + for (int j = 0; piChannelIDs[j] >= 0; ++j) { + if (!pChannelStrip->channel()) + break; + if (piChannelIDs[j] == pChannelStrip->channel()->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(); } @@ -2031,12 +2222,12 @@ void MainForm::updateInstrumentNames (void) { // Full channel list update... - QWidgetList wlist = m_pWorkspace->windowList(); + QList wlist = m_pWorkspace->subWindowList(); if (wlist.isEmpty()) return; m_pWorkspace->setUpdatesEnabled(false); - for (int iChannel = 0; iChannel < (int) wlist.count(); iChannel++) { + for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) { ChannelStrip *pChannelStrip = (ChannelStrip *) wlist.at(iChannel); if (pChannelStrip) pChannelStrip->updateInstrumentName(true); @@ -2060,13 +2251,16 @@ return; // Full channel list update... - QWidgetList wlist = m_pWorkspace->windowList(); + 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); + for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) { + ChannelStrip *pChannelStrip = NULL; + QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel); + if (pMdiSubWindow) + pChannelStrip = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) pChannelStrip->setDisplayFont(font); } @@ -2078,13 +2272,16 @@ void MainForm::updateDisplayEffect (void) { // Full channel list update... - QWidgetList wlist = m_pWorkspace->windowList(); + 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); + for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) { + ChannelStrip *pChannelStrip = NULL; + QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel); + if (pMdiSubWindow) + pChannelStrip = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) pChannelStrip->setDisplayEffect(m_pOptions->bDisplayEffect); } @@ -2106,13 +2303,16 @@ #endif // Full channel list update... - QWidgetList wlist = m_pWorkspace->windowList(); + 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); + for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) { + ChannelStrip *pChannelStrip = NULL; + QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel); + if (pMdiSubWindow) + pChannelStrip = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) pChannelStrip->setMaxVolume(m_pOptions->iMaxVolume); } @@ -2157,7 +2357,7 @@ QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QMessageBox::critical(this, - QSAMPLER_TITLE ": " + tr("Error"), s, tr("Cancel")); + QSAMPLER_TITLE ": " + tr("Error"), s, QMessageBox::Cancel); } @@ -2220,7 +2420,7 @@ // qsamplerMainForm -- 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; @@ -2243,14 +2443,15 @@ } // 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(); @@ -2263,34 +2464,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? + if (m_pOptions && m_pOptions->bAutoArrange) + channelsArrange(); + + stabilizeForm(); +} + + // 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 iChannel ) { - QWidgetList wlist = m_pWorkspace->windowList(); + if (!m_pWorkspace) return NULL; + + QList wlist = m_pWorkspace->subWindowList(); if (wlist.isEmpty()) return NULL; - return static_cast (wlist.at(iChannel)); + if (iChannel < 0 || iChannel >= wlist.size()) + return NULL; + + QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel); + 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(); + 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)); + for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) { + ChannelStrip *pChannelStrip = NULL; + QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel); + if (pMdiSubWindow) + pChannelStrip = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) { Channel *pChannel = pChannelStrip->channel(); if (pChannel && pChannel->channelID() == iChannelID) @@ -2310,12 +2545,14 @@ m_ui.channelsMenu->addAction(m_ui.channelsArrangeAction); m_ui.channelsMenu->addAction(m_ui.channelsAutoArrangeAction); - QWidgetList wlist = m_pWorkspace->windowList(); + 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)); + for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) { + ChannelStrip *pChannelStrip = NULL; + QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel); + if (pMdiSubWindow) + pChannelStrip = static_cast (pMdiSubWindow->widget()); if (pChannelStrip) { QAction *pAction = m_ui.channelsMenu->addAction( pChannelStrip->windowTitle(), @@ -2337,7 +2574,7 @@ if (pAction == NULL) return; - ChannelStrip* pChannelStrip = channelStripAt(pAction->data().toInt()); + ChannelStrip *pChannelStrip = channelStripAt(pAction->data().toInt()); if (pChannelStrip) { pChannelStrip->showNormal(); pChannelStrip->setFocus(); @@ -2398,11 +2635,12 @@ 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); + QList wlist = m_pWorkspace->subWindowList(); + for (int iChannel = 0; iChannel < (int) wlist.count(); ++iChannel) { + ChannelStrip *pChannelStrip = NULL; + QMdiSubWindow *pMdiSubWindow = wlist.at(iChannel); + if (pMdiSubWindow) + pChannelStrip = static_cast (pMdiSubWindow->widget()); if (pChannelStrip && pChannelStrip->isVisible()) pChannelStrip->updateChannelUsage(); } @@ -2430,17 +2668,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; } @@ -2458,9 +2692,7 @@ // 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())); @@ -2514,9 +2746,8 @@ "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) + "Do you want LinuxSampler to stop?"), + QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) { bForceServerStop = false; } @@ -2526,13 +2757,13 @@ if (m_pServer && bForceServerStop) { appendMessages(tr("Server is stopping...")); if (m_pServer->state() == QProcess::Running) { -#if defined(WIN32) + #if defined(WIN32) // Try harder... m_pServer->kill(); -#else + #else // Try softly... m_pServer->terminate(); -#endif + #endif } } // Do final processing anyway. else processServerExit(); @@ -2602,7 +2833,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; } @@ -2647,6 +2878,8 @@ .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)"); @@ -2654,14 +2887,20 @@ 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_LSCP_CHANNEL_MIDI +#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_LSCP_DEVICE_MIDI +#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)"); @@ -2692,6 +2931,9 @@ } } + // send the current / loaded fine tuning settings to the sampler + m_pOptions->sendFineTuningSettings(); + // Make a new session return newSession(); } @@ -2719,14 +2961,18 @@ closeSession(false); // Close us as a client... -#if CONFIG_LSCP_DEVICE_MIDI +#if CONFIG_EVENT_DEVICE_MIDI ::lscp_client_unsubscribe(m_pClient, LSCP_EVENT_DEVICE_MIDI); #endif -#if CONFIG_LSCP_CHANNEL_MIDI +#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; @@ -2746,10 +2992,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);