diff --git a/gui/viewbase.cpp b/gui/viewbase.cpp index 5f72212d..2b9620d9 100644 --- a/gui/viewbase.cpp +++ b/gui/viewbase.cpp @@ -1,499 +1,509 @@ /* * 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. */ #include "viewbase.h" // Qt #include #include #include // KDE #include #include #include #include #include #include // KMix #include "dialogviewconfiguration.h" #include "gui/guiprofile.h" #include "gui/kmixtoolbox.h" #include "gui/mixdevicewidget.h" #include "gui/mdwslider.h" #include "core/ControlManager.h" #include "core/GlobalConfig.h" #include "core/mixer.h" #include "core/mixertoolbox.h" /** * Creates an empty View. To populate it with MixDevice instances, you must implement * _setMixSet() in your derived class. */ ViewBase::ViewBase(QWidget* parent, QString id, Qt::WindowFlags f, ViewBase::ViewFlags vflags, QString guiProfileId, KActionCollection *actionColletion) : QWidget(parent, f), _popMenu(NULL), _actions(actionColletion), _vflags(vflags), _guiProfileId(guiProfileId) , guiLevel(GuiVisibility::GuiSIMPLE) { setObjectName(id); // When loading the View from the XML profile, guiLevel can get overridden m_viewId = id; configureIcon = QIcon::fromTheme( QLatin1String( "configure" )); if ( _actions == 0 ) { // We create our own action collection, if the actionColletion was 0. // This is currently done for the ViewDockAreaPopup, but only because it has not been converted to use the app-wide // actionCollection(). This is a @todo. _actions = new KActionCollection( this ); } _localActionColletion = new KActionCollection( this ); // Plug in the "showMenubar" action, if the caller wants it. Typically this is only necessary for views in the KMix main window. if ( vflags & ViewBase::HasMenuBar ) { KToggleAction *m = static_cast( _actions->action( name(KStandardAction::ShowMenubar) ) ) ; if ( m != 0 ) { bool visible = ( vflags & ViewBase::MenuBarVisible ); m->setChecked(visible); } } } ViewBase::~ViewBase() { // Hint: The GUI profile will not be removed, as it is pooled and might be applied to a new View. } void ViewBase::addMixer(Mixer *mixer) { _mixers.append(mixer); } //void ViewBase::configurationUpdate() { //} QPushButton* ViewBase::createConfigureViewButton() { QPushButton* configureViewButton = new QPushButton(configureIcon, "", this); configureViewButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); configureViewButton->setToolTip(i18n( "Configure this view" )); connect(configureViewButton, SIGNAL(clicked(bool)), SLOT(configureView())); return configureViewButton; } void ViewBase::updateGuiOptions() { setTicks(GlobalConfig::instance().data.showTicks); setLabels(GlobalConfig::instance().data.showLabels); updateMediaPlaybackIcons(); } QString ViewBase::id() const { return m_viewId; } bool ViewBase::isValid() const { return ( !_mixSet.isEmpty() || isDynamic() ); } void ViewBase::setIcons (bool on) { KMixToolBox::setIcons (_mdws, on ); } void ViewBase::setLabels(bool on) { KMixToolBox::setLabels(_mdws, on ); } void ViewBase::setTicks (bool on) { KMixToolBox::setTicks (_mdws, on ); } /** * Updates all playback icons to their (new) state, e.g. Paused, or Playing */ void ViewBase::updateMediaPlaybackIcons() { for (int i = 0; i < _mdws.count(); ++i) { // Currently media controls are always attached to sliders => use MDWSlider MDWSlider* mdw = qobject_cast(_mdws[i]); if (mdw != 0) { mdw->updateMediaButton(); } } } /** * Create all widgets. * This is a loop over all supported devices of the corresponding view. * On each device add() is called - the derived class must implement add() for creating and placing * the real MixDeviceWidget. * The added MixDeviceWidget is appended to the _mdws list. */ void ViewBase::createDeviceWidgets() { - _setMixSet(); - foreach ( shared_ptr md, _mixSet ) + // If this widget is currently visible, it is hidden while the device widgets are + // being added and shown again afterwards. This is because, if we are called as + // a result of change of orientation or a control being added or removed while we + // are visible, isVisibleTo() in ViewSliders::configurationUpdate() appears to + // return false even for controls that should be visible. This means that they + // do not get included in the label extent calculation and the labels will not + // be resized evenly. + const bool wasVisible = isVisible(); + if (wasVisible) hide(); // hide if currently visible + + _setMixSet(); + foreach (const shared_ptr md, _mixSet) { - QWidget* mdw = add(md); // a) Let the View implementation do its work - _mdws.append(mdw); // b) Add it to the local list + QWidget *mdw = add(md); // a) Let the implementation do its work + _mdws.append(mdw); // b) Add it to the local list connect(mdw, SIGNAL(guiVisibilityChange(MixDeviceWidget*,bool)), SLOT(guiVisibilitySlot(MixDeviceWidget*,bool))); } - if ( !isDynamic() ) + if (!isDynamic()) { - QAction *action = _localActionColletion->addAction("toggle_channels"); - action->setText(i18n("Configure Channels...")); - connect(action, SIGNAL(triggered(bool)), SLOT(configureView())); - } + QAction *action = _localActionColletion->addAction("toggle_channels"); + action->setText(i18n("Configure Channels...")); + connect(action, SIGNAL(triggered(bool)), SLOT(configureView())); + } - // allow view to "polish" itself - constructionFinished(); + constructionFinished(); // allow view to "polish" itself + if (wasVisible) show(); // show again if originally visible } /** * Called when a specific control is to be shown or hidden. At the moment it is only called via * the "hide" action in the MDW context menu. * * @param mdw * @param enable */ void ViewBase::guiVisibilitySlot(MixDeviceWidget* mdw, bool enable) { MixDevice* md = mdw->mixDevice().get(); qCDebug(KMIX_LOG) << "Change " << md->id() << " to visible=" << enable; ProfControl* pctl = findMdw(md->id()); if (pctl == 0) { qCWarning(KMIX_LOG) << "MixDevice not found, and cannot be hidden, id=" << md->id(); return; // Ignore } pctl->setVisible(enable); ControlManager::instance().announce(md->mixer()->id(), ControlManager::ControlList, QString("ViewBase::guiVisibilitySlot")); } // ---------- Popup stuff START --------------------- void ViewBase::mousePressEvent( QMouseEvent *e ) { if ( e->button() == Qt::RightButton ) showContextMenu(); } /** * Return a popup menu. This contains basic entries. * More can be added by the caller. */ QMenu* ViewBase::getPopup() { popupReset(); return _popMenu; } void ViewBase::popupReset() { QAction *act; delete _popMenu; _popMenu = new QMenu( this ); _popMenu->addSection( QIcon::fromTheme( QLatin1String( "kmix" ) ), i18n("Device Settings" )); act = _localActionColletion->action( "toggle_channels" ); if ( act ) _popMenu->addAction(act); act = _actions->action( "options_show_menubar" ); if ( act ) _popMenu->addAction(act); } /** This will only get executed, when the user has removed all items from the view. Don't remove this method, because then the user cannot get a menu for getting his channels back */ void ViewBase::showContextMenu() { //qCDebug(KMIX_LOG) << "ViewBase::showContextMenu()"; popupReset(); QPoint pos = QCursor::pos(); _popMenu->popup( pos ); } void ViewBase::refreshVolumeLevels() { // is virtual } /** * Check all Mixer instances of this view. * If at least on is dynamic, return true. * Please note that usually there is only one Mixer instance per View. * The only exception as of today (June 2011) is the Tray Popup, which * can contain controls from e.g. ALSA and MPRIS2 backends. */ bool ViewBase::isDynamic() const { foreach (Mixer* mixer , _mixers ) { if ( mixer->isDynamic() ) return true; } return false; } bool ViewBase::pulseaudioPresent() const { // We do not use Mixer::pulseaudioPresent(), as we are only interested in Mixer instances contained in this View. foreach (Mixer* mixer , _mixers ) { if ( mixer->getDriverName() == "PulseAudio" ) return true; } return false; } void ViewBase::resetMdws() { // We need to delete the current MixDeviceWidgets so we can redraw them while (!_mdws.isEmpty()) delete _mdws.takeFirst(); // _mixSet contains shared_ptr instances, so clear() should be enough to prevent mem leak _mixSet.clear(); // Clean up our _mixSet so we can reapply our GUIProfile } int ViewBase::visibleControls() { int visibleCount = 0; foreach (QWidget* qw, _mdws) { if (qw->isVisible()) ++ visibleCount; } return visibleCount; } /** * Open the View configuration dialog. The user can select which channels he wants * to see and which not. */ void ViewBase::configureView() { Q_ASSERT( !isDynamic() ); Q_ASSERT( !pulseaudioPresent() ); DialogViewConfiguration* dvc = new DialogViewConfiguration(0, *this); dvc->show(); } void ViewBase::toggleMenuBarSlot() { //qCDebug(KMIX_LOG) << "ViewBase::toggleMenuBarSlot() start\n"; emit toggleMenuBar(); //qCDebug(KMIX_LOG) << "ViewBase::toggleMenuBarSlot() done\n"; } /** * Loads the configuration of this view. *

* Future directions: The view should probably know its config in advance, so we can use it in #load() and #save() * * * @param config The view for this config */ void ViewBase::load(KConfig *config) { ViewBase *view = this; QString grp = "View."; grp += view->id(); //KConfigGroup cg = config->group( grp ); qCDebug(KMIX_LOG) << "KMixToolBox::loadView() grp=" << grp.toLatin1(); static GuiVisibility guiVisibilities[3] = { GuiVisibility::GuiSIMPLE, GuiVisibility::GuiEXTENDED, GuiVisibility::GuiFULL }; bool guiLevelSet = false; for (int i=0; i<3; ++i) { GuiVisibility& guiCompl = guiVisibilities[i]; bool atLeastOneControlIsShown = false; foreach(QWidget *qmdw, view->_mdws) { MixDeviceWidget *mdw = qobject_cast(qmdw); if (mdw!=nullptr) { shared_ptr md = mdw->mixDevice(); QString devgrp = md->configGroupName(grp); KConfigGroup devcg = config->group(devgrp); if (mdw->inherits("MDWSlider")) { // only sliders have the ability to split apart in multiple channels bool splitChannels = devcg.readEntry("Split", !mdw->isStereoLinked()); mdw->setStereoLinked(!splitChannels); } // Future directions: "Visibility" is very dirty: It is read from either config file or // GUIProfile. Thus we have a lot of doubled mdw visibility code all throughout KMix. bool mdwEnabled = false; // Consult GuiProfile for visibility mdwEnabled = findMdw(mdw->mixDevice()->id(), guiCompl) != 0; // Match GUI complexity // qCWarning(KMIX_LOG) << "---------- FIRST RUN: md=" << md->id() << ", guiVisibility=" << guiCompl.getId() << ", enabled=" << mdwEnabled; if (mdwEnabled) { atLeastOneControlIsShown = true; } mdw->setVisible(mdwEnabled); } // inherits MixDeviceWidget } // for all MDW's if (atLeastOneControlIsShown) { guiLevelSet = true; setGuiLevel(guiCompl); break; // If there were controls in this complexity level, don't try more } } // for try = 0 ... 1 if (!guiLevelSet) setGuiLevel(guiVisibilities[2]); } void ViewBase::setGuiLevel(GuiVisibility& guiLevel) { this->guiLevel = guiLevel; } /** * Checks whether the given mixDevice shall be shown according to the requested * GuiVisibility. All ProfControl objects are inspected. The first found is returned. * * @param mdwId The control ID * @param requestedGuiComplexityName The GUI name * @return The corresponding ProfControl* * */ ProfControl* ViewBase::findMdw(const QString& mdwId, GuiVisibility visibility) { foreach ( ProfControl* pControl, guiProfile()->getControls() ) { QRegExp idRegExp(pControl->id); if ( mdwId.contains(idRegExp) ) { if (pControl->getVisibility().satisfiesVisibility(visibility)) { // qCDebug(KMIX_LOG) << " MATCH " << (*pControl).id << " for " << mdwId << " with visibility " << pControl->getVisibility().getId() << " to " << visibility.getId(); return pControl; } else { // qCDebug(KMIX_LOG) << "NOMATCH " << (*pControl).id << " for " << mdwId << " with visibility " << pControl->getVisibility().getId() << " to " << visibility.getId(); } } } // iterate over all ProfControl entries return 0; // not found } /** * Returns the ProfControl* to the given id. The first found is returned. * GuiVisibilityis not taken into account. . All ProfControl objects are inspected. * * @param id The control ID * @return The corresponding ProfControl*, or 0 if no match was found */ ProfControl* ViewBase::findMdw(const QString& id) { foreach ( ProfControl* pControl, guiProfile()->getControls() ) { QRegExp idRegExp(pControl->id); //qCDebug(KMIX_LOG) << "KMixToolBox::loadView() try match " << (*pControl).id << " for " << mdw->mixDevice()->id(); if ( id.contains(idRegExp) ) { return pControl; } } // iterate over all ProfControl entries return 0;// not found } /* * Saves the View configuration */ void ViewBase::save(KConfig *config) { ViewBase *view = this; QString grp = "View."; grp += view->id(); // Certain bits are not saved for dynamic mixers (e.g. PulseAudio) bool dynamic = isDynamic(); // TODO 11 Dynamic view configuration for (int i = 0; i < view->_mdws.count(); ++i) { QWidget *qmdw = view->_mdws[i]; MixDeviceWidget *mdw = qobject_cast(qmdw); if (mdw!=nullptr) { shared_ptr md = mdw->mixDevice(); //qCDebug(KMIX_LOG) << " grp=" << grp.toLatin1(); //qCDebug(KMIX_LOG) << " mixer=" << view->id().toLatin1(); //qCDebug(KMIX_LOG) << " mdwPK=" << mdw->mixDevice()->id().toLatin1(); QString devgrp = QString("%1.%2.%3").arg(grp, md->mixer()->id(), md->id()); KConfigGroup devcg = config->group(devgrp); if (mdw->inherits("MDWSlider")) { // only sliders have the ability to split apart in multiple channels devcg.writeEntry("Split", !mdw->isStereoLinked()); } /* if (!dynamic) { devcg.writeEntry("Show", mdw->isVisibleTo(view)); // qCDebug(KMIX_LOG) << "Save devgrp" << devgrp << "show=" << mdw->isVisibleTo(view); } */ } // inherits MixDeviceWidget } // for all MDW's if (!dynamic) { // We do not save GUIProfiles (as they cannot be customized) for dynamic mixers (e.g. PulseAudio) if (guiProfile()->isDirty()) { qCDebug(KMIX_LOG) << "Writing dirty profile. grp=" << grp; guiProfile()->writeProfile(); } } } // ---------- Popup stuff END --------------------- diff --git a/gui/viewsliders.cpp b/gui/viewsliders.cpp index c4d840fc..7f01b764 100644 --- a/gui/viewsliders.cpp +++ b/gui/viewsliders.cpp @@ -1,444 +1,444 @@ /* * 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 "gui/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" #include "gui/verticaltext.h" // KDE #include // Qt #include #include #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, QString id, Mixer* mixer, ViewBase::ViewFlags vflags, QString guiProfileId, KActionCollection *actColl) : ViewBase(parent, id, Qt::FramelessWindowHint, vflags, guiProfileId, actColl), _layoutEnum(0) { addMixer(mixer); _configureViewButton = 0; _layoutMDW = 0; _layoutSliders = 0; _layoutEnum = 0; emptyStreamHint = 0; 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 _layoutMDW; // qDeleteAll(_separators); } 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(shared_ptr md) { MixDeviceWidget *mdw; Qt::Orientation orientation = GlobalConfig::instance().data.getToplevelOrientation(); if ( md->isEnum() ) { mdw = new MDWEnum( md, // MixDevice (parameter) orientation, // Orientation this, // parent this // View widget , md->controlProfile() ); // if ( _layoutEnum == 0 ) { // // lazily creation of Layout for the first enum // _layoutEnum = new QVBoxLayout(); // _layoutMDW->addLayout( _layoutEnum ); // } _layoutEnum->addWidget(mdw); } // an enum else { 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 , md->controlProfile() ); // View widget _layoutSliders->addWidget(mdw); } return mdw; } void ViewSliders::_setMixSet() { resetMdws(); delete emptyStreamHint; emptyStreamHint = 0; // Our _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 (_layoutSliders != 0) { QLayoutItem *li; while ((li = _layoutSliders->takeAt(0))) delete li; // delete _layoutSliders; _layoutSliders = 0; } delete _configureViewButton; _configureViewButton = 0; if (_layoutEnum != 0) { QLayoutItem *li2; while ((li2 = _layoutEnum->takeAt(0)) != 0) delete li2; // delete _layoutEnum; _layoutEnum = 0; } delete _layoutMDW; _layoutMDW = 0; // We will be recreating our sliders, so make sure we trash all the separators too. //qDeleteAll(_separators); _separators.clear(); if (GlobalConfig::instance().data.getToplevelOrientation() == Qt::Horizontal) { // Horizontal slider => put them vertically _layoutMDW = new QVBoxLayout(this); _layoutMDW->setAlignment(Qt::AlignLeft | Qt::AlignTop); _layoutSliders = new QVBoxLayout(); _layoutSliders->setAlignment(Qt::AlignVCenter | Qt::AlignLeft); } else { // Vertical slider => put them horizontally _layoutMDW = new QHBoxLayout(this); _layoutMDW->setAlignment(Qt::AlignHCenter | Qt::AlignTop); _layoutSliders = new QHBoxLayout(); _layoutSliders->setAlignment(Qt::AlignHCenter | Qt::AlignTop); } _layoutSliders->setContentsMargins(0, 0, 0, 0); _layoutSliders->setSpacing(0); _layoutMDW->setContentsMargins(0, 0, 0, 0); _layoutMDW->setSpacing(0); // The stretch added here is so that, if there is width to spare, the // controls are centered in the window but the configure button is // always at the bottom right. _layoutMDW->addStretch(); _layoutMDW->addItem(_layoutSliders); _layoutMDW->addStretch(); _layoutEnum = new QVBoxLayout(); _layoutMDW->addLayout(_layoutEnum); // Hint: This text comparison is not a clean solution, but one that will work for quite a while. QString viewId(id()); if (viewId.contains(".Capture_Streams.")) emptyStreamHint = new QLabel(i18n("Nothing is capturing audio.")); else if (viewId.contains(".Playback_Streams.")) emptyStreamHint = new QLabel(i18n("Nothing is playing audio.")); else if (viewId.contains(".Capture_Devices.")) emptyStreamHint = new QLabel(i18n("No capture devices.")); else if (viewId.contains(".Playback_Devices.")) emptyStreamHint = new QLabel(i18n("No playback devices.")); else emptyStreamHint = new QLabel(i18n("Nothing is playing audio.")); // Fallback. Assume Playback stream emptyStreamHint->setAlignment(Qt::AlignCenter); emptyStreamHint->setWordWrap(true); emptyStreamHint->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); _layoutMDW->addWidget(emptyStreamHint); #ifdef TEST_MIXDEVICE_COMPOSITE QList > mds; // For temporary test #endif // This method iterates the controls from the 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. GUIProfile* guiprof = guiProfile(); if (guiprof != 0) { foreach (Mixer* mixer , _mixers ){ const MixSet& mixset = mixer->getMixSet(); foreach ( ProfControl* control, guiprof->getControls() ) { //ProfControl* control = *it; // The TabName of the control matches this View name (!! attention: Better use some ID, due to i18n() ) QRegExp idRegexp(control->id); //bool isExactRegexp = control->id.startsWith('^') && control->id.endsWith('$'); // for optimizing //isExactRegexp &= ( ! control->id.contains(".*") ); // For now. Might be removed in the future, as it cannot be done properly !!! //qCDebug(KMIX_LOG) << "ViewSliders::setMixSet(): Check GUIProfile id==" << control->id << "\n"; // 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 ) ) { continue; // dup check } // Now check whether subcontrols match bool subcontrolPlaybackWanted = (control->useSubcontrolPlayback() && ( md->playbackVolume().hasVolume() || md->hasMuteSwitch()) ); bool subcontrolCaptureWanted = (control->useSubcontrolCapture() && ( md->captureVolume() .hasVolume() || md->captureVolume() .hasSwitch()) ); bool subcontrolEnumWanted = (control->useSubcontrolEnum() && md->isEnum()); bool subcontrolWanted = subcontrolPlaybackWanted | subcontrolCaptureWanted | subcontrolEnumWanted; if ( !subcontrolWanted ) { continue; } md->setControlProfile(control); if ( !control->name.isNull() ) { // Apply the custom name from the profile md->setReadableName(control->name);// @todo: This is the wrong place. It only applies to controls in THIS type of view } 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") { mds.append(md);} // For temporary test #endif // We use no "break;" ,as multiple devices could match the regexp (e.g. "^.*$") } // name matches } // loop for finding a suitable MixDevice } // iteration over all controls from the Profile } // if there is a profile } // Iteration over all Mixers emptyStreamHint->setVisible(_mixSet.isEmpty() && isDynamic()); // show a hint why a tab is empty (dynamic controls!!!) // visibleControls() == 0 could be used for the !isDynamic() case #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 ( !pulseaudioPresent() ) // TODO 11 Dynamic view configuration if ( !isDynamic() ) { _configureViewButton = createConfigureViewButton(); _layoutEnum->addStretch(); _layoutEnum->addWidget(_configureViewButton); } updateGuiOptions(); } void ViewSliders::configurationUpdate() { // Adjust height of top part by setting it to the maximum of all mdw's bool haveCaptureLEDs = false; int labelExtent = 0; bool haveMuteButtons = false; // Find out whether any MDWSlider has Switches. If one has, then we need "extents" for (int i = 0; i < _mdws.count(); i++) { MDWSlider* mdw = ::qobject_cast(_mdws[i]); if (mdw && mdw->isVisibleTo(this)) { labelExtent = qMax(labelExtent, mdw->labelExtentHint()); //qCDebug(KMIX_LOG) << "########## EXTENT for " << id() << " is " << labelExtent; haveCaptureLEDs = haveCaptureLEDs || mdw->hasCaptureLED(); haveMuteButtons = haveMuteButtons || mdw->hasMuteButton(); } // The following "break" cannot be done any longer, as we need to calculate the highest labelExtent from ALL mdw's // if (haveCaptureLEDs && haveMuteButtons) // break; // We know all we want. Lets break. } //qCDebug(KMIX_LOG) << "topPartExtent is " << topPartExtent; bool firstVisibleControlFound = false; for ( int i=0; i<_mdws.count(); i++ ) { MixDeviceWidget *mdw = qobject_cast(_mdws[i]); MDWSlider *mdwSlider = qobject_cast(_mdws[i]); if ( mdw ) { // guiLevel has been set earlier, by inspecting the controls ProfControl* matchingControl = findMdw(mdw->mixDevice()->id(), guiLevel); mdw->setVisible(matchingControl != 0); - if (mdwSlider!=nullptr) + if (mdwSlider!=nullptr && labelExtent>0) { // additional options for sliders mdwSlider->setLabelExtent(labelExtent); } bool thisControlIsVisible = mdw->isVisibleTo(this); bool showSeparator = ( firstVisibleControlFound && thisControlIsVisible); if ( _separators.contains( mdw->mixDevice()->id() )) { QFrame* sep = _separators[mdw->mixDevice()->id()]; sep->setVisible(showSeparator); } if ( thisControlIsVisible ) firstVisibleControlFound=true; } } // for all MDW's _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 } } }