diff --git a/gui/viewsliders.cpp b/gui/viewsliders.cpp index 24b86014..d3c135e0 100644 --- a/gui/viewsliders.cpp +++ b/gui/viewsliders.cpp @@ -1,410 +1,404 @@ /* * KMix -- KDE's full featured mini mixer * * * Copyright (C) 1996-2004 Christian Esken * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //#define TEST_MIXDEVICE_COMPOSITE #undef TEST_MIXDEVICE_COMPOSITE #ifdef TEST_MIXDEVICE_COMPOSITE #ifdef __GNUC__ #warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #warning !!! MIXDEVICE COMPOSITE TESTING IS ACTIVATED !!! #warning !!! THIS IS PRE-ALPHA CODE! !!! #warning !!! DO NOT SHIP KMIX IN THIS STATE !!! #warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #endif #endif #include "viewsliders.h" // KMix #include "core/ControlManager.h" #include "core/GlobalConfig.h" #include "core/mixdevicecomposite.h" #include "core/mixer.h" #include "gui/guiprofile.h" #include "gui/mdwenum.h" #include "gui/mdwslider.h" // KDE #include +#include // Qt #include -#include #include #include #include /** * Generic View implementation. This can hold now all kinds of controls (not just Sliders, as * the class name suggests). */ ViewSliders::ViewSliders(QWidget *parent, const QString &id, Mixer *mixer, ViewBase::ViewFlags vflags, const QString &guiProfileId, KActionCollection *actColl) : ViewBase(parent, id, Qt::FramelessWindowHint, vflags, guiProfileId, actColl) { addMixer(mixer); m_configureViewButton = nullptr; m_layoutMDW = nullptr; m_layoutSliders = nullptr; m_layoutSwitches = nullptr; m_emptyStreamHint = nullptr; createDeviceWidgets(); ControlManager::instance().addListener(mixer->id(), ControlManager::GUI|ControlManager::ControlList|ControlManager::Volume, this, QString("ViewSliders.%1").arg(mixer->id())); } ViewSliders::~ViewSliders() { ControlManager::instance().removeListener(this); delete m_layoutMDW; } void ViewSliders::controlsChange(ControlManager::ChangeType changeType) { switch (changeType) { case ControlManager::ControlList: createDeviceWidgets(); break; case ControlManager::GUI: updateGuiOptions(); break; case ControlManager::Volume: if (GlobalConfig::instance().data.debugVolume) qCDebug(KMIX_LOG) << "NOW I WILL REFRESH VOLUME LEVELS. I AM " << id(); refreshVolumeLevels(); break; default: ControlManager::warnUnexpectedChangeType(changeType, this); break; } } QWidget *ViewSliders::add(const shared_ptr md) { MixDeviceWidget *mdw; Qt::Orientation orientation = GlobalConfig::instance().data.getToplevelOrientation(); if (md->isEnum()) // control is a switch { mdw = new MDWEnum(md, // MixDevice (parameter) orientation, // Orientation this, // parent this, // View widget md->controlProfile()); // profile m_layoutSwitches->addWidget(mdw); } else // control is a slider { mdw = new MDWSlider(md, // MixDevice (parameter) true, // Show Mute LED true, // Show Record LED false, // Include Mixer Name false, // Small orientation, // Orientation this, // parent this, // View widget md->controlProfile()); // profile m_layoutSliders->addWidget(mdw); } return (mdw); } void ViewSliders::initLayout() { resetMdws(); - delete m_emptyStreamHint; - m_emptyStreamHint = nullptr; - // Our m_layoutSliders now should only contain spacer widgets from the addSpacing() calls in add() above. // We need to trash those too otherwise all sliders gradually migrate away from the edge :p if (m_layoutSliders!=nullptr) { QLayoutItem *li; while ((li = m_layoutSliders->takeAt(0))!=nullptr) delete li; m_layoutSliders = nullptr; } delete m_configureViewButton; m_configureViewButton = nullptr; if (m_layoutSwitches!=nullptr) { QLayoutItem *li; while ((li = m_layoutSwitches->takeAt(0))!=nullptr) delete li; m_layoutSwitches = nullptr; } delete m_layoutMDW; m_layoutMDW = new QGridLayout(this); m_layoutMDW->setContentsMargins(0, 0, 0, 0); m_layoutMDW->setSpacing(0); m_layoutMDW->setRowStretch(0, 1); m_layoutMDW->setColumnStretch(0, 1); if (GlobalConfig::instance().data.getToplevelOrientation()==Qt::Horizontal) { // horizontal sliders m_layoutMDW->setAlignment(Qt::AlignLeft|Qt::AlignTop); m_layoutSliders = new QVBoxLayout(); m_layoutSliders->setAlignment(Qt::AlignVCenter|Qt::AlignLeft); } else // vertical sliders { m_layoutMDW->setAlignment(Qt::AlignHCenter|Qt::AlignTop); m_layoutSliders = new QHBoxLayout(); m_layoutSliders->setAlignment(Qt::AlignHCenter|Qt::AlignTop); } m_layoutSliders->setContentsMargins(0, 0, 0, 0); m_layoutSliders->setSpacing(0); m_layoutSwitches = new QVBoxLayout(); QString emptyStreamText = i18n("Nothing is playing audio."); // Hint: This text comparison is not a clean solution, but one that will work for quite a while. const QString viewId = id(); if (viewId.contains(".Capture_Streams.")) emptyStreamText = i18n("Nothing is capturing audio."); else if (viewId.contains(".Capture_Devices.")) emptyStreamText = i18n("There are no capture devices."); else if (viewId.contains(".Playback_Devices.")) emptyStreamText = i18n("There are no playback devices."); - // TODO: does this need to be a member? - m_emptyStreamHint = new QLabel(emptyStreamText); - QFont f = m_emptyStreamHint->font(); - f.setBold(true); - m_emptyStreamHint->setFont(f); - m_emptyStreamHint->setAlignment(Qt::AlignCenter); - m_emptyStreamHint->setWordWrap(true); - m_emptyStreamHint->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); + delete m_emptyStreamHint; + m_emptyStreamHint = new KMessageWidget(emptyStreamText, this); + m_emptyStreamHint->setIcon(QIcon::fromTheme("dialog-information")); + m_emptyStreamHint->setMessageType(KMessageWidget::Information); + m_emptyStreamHint->setCloseButtonVisible(false); m_layoutSliders->addWidget(m_emptyStreamHint); if (GlobalConfig::instance().data.getToplevelOrientation()==Qt::Horizontal) { // horizontal sliders // Row 0: Sliders m_layoutMDW->addLayout(m_layoutSliders, 0, 0, 1, -1, Qt::AlignHCenter|Qt::AlignVCenter); // Row 1: Switches m_layoutMDW->addLayout(m_layoutSwitches, 1, 0, Qt::AlignLeft|Qt::AlignTop); } else // vertical sliders { // Column 0: Sliders m_layoutMDW->addLayout(m_layoutSliders, 0, 0, -1, 1, Qt::AlignHCenter|Qt::AlignVCenter); // Column 1: Switches m_layoutMDW->addLayout(m_layoutSwitches, 0, 1, Qt::AlignLeft|Qt::AlignTop); } #ifdef TEST_MIXDEVICE_COMPOSITE QList > mds; // For temporary test #endif // This method iterates over all the controls from the GUI profile. // Each control is checked, whether it is also contained in the mixset, and // applicable for this kind of View. If yes, the control is accepted and inserted. const GUIProfile *guiprof = guiProfile(); if (guiprof!=nullptr) { foreach (Mixer *mixer, _mixers) { const MixSet &mixset = mixer->getMixSet(); foreach (ProfControl *control, guiprof->getControls()) { // The TabName of the control matches this View name (!! attention: Better use some ID, due to i18n() ) QRegExp idRegexp(control->id); // The following for-loop could be simplified by using a std::find_if for (int i = 0; i md = mixset[i]; if (md->id().contains(idRegexp)) { // match found (by name) if (_mixSet.contains(md)) { // check for duplicate already continue; } // Now check whether subcontrols required const bool subcontrolPlaybackWanted = (control->useSubcontrolPlayback() && (md->playbackVolume().hasVolume() || md->hasMuteSwitch())); const bool subcontrolCaptureWanted = (control->useSubcontrolCapture() && (md->captureVolume() .hasVolume() || md->captureVolume() .hasSwitch())); const bool subcontrolEnumWanted = (control->useSubcontrolEnum() && md->isEnum()); const bool subcontrolWanted = subcontrolPlaybackWanted|subcontrolCaptureWanted|subcontrolEnumWanted; if (!subcontrolWanted) continue; md->setControlProfile(control); if (!control->name.isNull()) { // Apply the custom name from the profile // TODO: This is the wrong place. It only // applies to controls in THIS type of view. md->setReadableName(control->name); } if (!control->getSwitchtype().isNull()) { if (control->getSwitchtype()=="On") md->playbackVolume().setSwitchType(Volume::OnSwitch); else if (control->getSwitchtype()=="Off") md->playbackVolume().setSwitchType(Volume::OffSwitch); } _mixSet.append(md); #ifdef TEST_MIXDEVICE_COMPOSITE if ( md->id() == "Front:0" || md->id() == "Surround:0") { // For temporary test mds.append(md); } #endif // No 'break' here, because multiple devices could match // the regexp (e.g. "^.*$") } // name matches } // loop for finding a suitable MixDevice } // iteration over all controls from the Profile } // iteration over all Mixers } // if there is a profile // Show a hint why a tab is empty (dynamic controls!) // TODO: 'visibleControls()==0' could be used for the !isDynamic() case m_emptyStreamHint->setVisible(_mixSet.isEmpty() && isDynamic()); #ifdef TEST_MIXDEVICE_COMPOSITE // @todo: This is currently hardcoded, and instead must be read as usual from the Profile MixDeviceComposite *mdc = new MixDeviceComposite(_mixer, "Composite_Test", mds, "A Composite Control #1", MixDevice::KMIX_COMPOSITE); Volume::ChannelMask chn = Volume::MMAIN; Volume* vol = new Volume( chn, 0, 100, true, true); mdc->addPlaybackVolume(*vol); QString ctlId("Composite_Test"); QString ctlMatchAll("*"); ProfControl* pctl = new ProfControl(ctlId, ctlMatchAll); mdc->setControlProfile(pctl); _mixSet->append(mdc); #endif } void ViewSliders::constructionFinished() { configurationUpdate(); if (!isDynamic()) { // Layout row 1 column 1: Configure View button // TODO: does this need to be a member? m_configureViewButton = createConfigureViewButton(); m_layoutMDW->addWidget(m_configureViewButton, 1, 1, Qt::AlignRight|Qt::AlignBottom); } updateGuiOptions(); } void ViewSliders::configurationUpdate() { // Adjust height of top part by setting it to the maximum of all mdw's int labelExtent = 0; // Find out whether any MDWSlider has Switches. If one has, then we need "extents" for (int i = 0; i<_mdws.count(); ++i) { const MDWSlider *mdw = qobject_cast(_mdws[i]); if (mdw!=nullptr && mdw->isVisibleTo(this)) { labelExtent = qMax(labelExtent, mdw->labelExtentHint()); //qCDebug(KMIX_LOG) << "########## EXTENT for " << id() << " is " << labelExtent; } } //qCDebug(KMIX_LOG) << "topPartExtent is " << topPartExtent; for (int i = 0; i<_mdws.count(); ++i) { MixDeviceWidget *mdw = qobject_cast(_mdws[i]); if (mdw!=nullptr) { // guiLevel has been set earlier, by inspecting the controls ProfControl *matchingControl = findMdw(mdw->mixDevice()->id(), guiLevel); mdw->setVisible(matchingControl!=nullptr); MDWSlider *mdwSlider = qobject_cast(_mdws[i]); if (mdwSlider!=nullptr && labelExtent>0) { // additional options for sliders mdwSlider->setLabelExtent(labelExtent); } } // if have a MixDeviceWidget } // for all MDW's m_layoutMDW->activate(); } void ViewSliders::refreshVolumeLevels() { for (int i = 0; i < _mdws.count(); i++) { QWidget *mdwx = _mdws[i]; MixDeviceWidget* mdw = ::qobject_cast(mdwx); if (mdw != 0) { // sanity check #ifdef TEST_MIXDEVICE_COMPOSITE // --- start --- The following 4 code lines should be moved to a more // generic place, as it only works in this View. But it // should also work in the ViewDockareaPopup and everywhere else. MixDeviceComposite* mdc = ::qobject_cast(mdw->mixDevice()); if (mdc != 0) { mdc->update(); } // --- end --- #endif if (GlobalConfig::instance().data.debugVolume) { bool debugMe = (mdw->mixDevice()->id() == "PCM:0"); if (debugMe) qCDebug(KMIX_LOG) << "Old PCM:0 playback state" << mdw->mixDevice()->isMuted() << ", vol=" << mdw->mixDevice()->playbackVolume().getAvgVolumePercent(Volume::MALL); } mdw->update(); } else { qCCritical(KMIX_LOG) << "ViewSliders::refreshVolumeLevels(): mdw is not a MixDeviceWidget\n"; // no slider. Cannot happen in theory => skip it } } } diff --git a/gui/viewsliders.h b/gui/viewsliders.h index 401ab477..3c38cdc1 100644 --- a/gui/viewsliders.h +++ b/gui/viewsliders.h @@ -1,65 +1,65 @@ //-*-C++-*- /* * KMix -- KDE's full featured mini mixer * * Copyright Christian Esken * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef ViewSliders_h #define ViewSliders_h #include "viewbase.h" class QBoxLayout; class QGridLayout; -class QLabel; class QPushButton; +class KMessageWidget; class Mixer; #include "gui/viewbase.h" #include "core/ControlManager.h" class ViewSliders : public ViewBase { Q_OBJECT public: ViewSliders(QWidget *parent, const QString &id, Mixer *mixer, ViewBase::ViewFlags vflags, const QString &guiProfileId, KActionCollection *actColl); virtual ~ViewSliders(); QWidget *add(const shared_ptr md) Q_DECL_OVERRIDE; void constructionFinished() Q_DECL_OVERRIDE; void configurationUpdate() Q_DECL_OVERRIDE; public slots: void controlsChange(ControlManager::ChangeType changeType); protected: void initLayout() Q_DECL_OVERRIDE; private: void refreshVolumeLevels() Q_DECL_OVERRIDE; QGridLayout *m_layoutMDW; QBoxLayout *m_layoutSliders; QBoxLayout *m_layoutSwitches; QPushButton *m_configureViewButton; - QLabel *m_emptyStreamHint; + KMessageWidget *m_emptyStreamHint; }; #endif