diff --git a/gui/mixdevicewidget.h b/gui/mixdevicewidget.h index 80ecef4b..a1078be4 100644 --- a/gui/mixdevicewidget.h +++ b/gui/mixdevicewidget.h @@ -1,94 +1,94 @@ //-*-C++-*- /* * KMix -- KDE's full featured mini mixer * * * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> * 1996-2000 Christian Esken * Sven Fischer * * 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 MIXDEVICEWIDGET_H #define MIXDEVICEWIDGET_H #include #include "core/mixdevice.h" #include "core/volume.h" #include class KActionCollection; class KShortcutsDialog; class MixDevice; class ProfControl; class ViewBase; class MixDeviceWidget : public QWidget { Q_OBJECT public: MixDeviceWidget( shared_ptr md, bool small, Qt::Orientation orientation, QWidget* parent, ViewBase*, ProfControl * ); virtual ~MixDeviceWidget() = default; void addActionToPopup( QAction *action ); - shared_ptr mixDevice() { return (m_mixdevice); } + shared_ptr mixDevice() const { return (m_mixdevice); } virtual void setColors( QColor high, QColor low, QColor back ); virtual void setIcons( bool value ); virtual void setMutedColors( QColor high, QColor low, QColor back ); virtual bool isStereoLinked() const { return (false); } virtual void setStereoLinked(bool) {} virtual void setLabeled(bool); virtual void setTicks(bool) {} public slots: virtual void defineKeys(); virtual void showContextMenu(const QPoint &pos = QCursor::pos()) = 0; /** * Called whenever there are volume updates pending from the hardware for this MDW. */ virtual void update() = 0; signals: void guiVisibilityChange(MixDeviceWidget* source, bool enable); protected slots: virtual void setDisabled(bool value) = 0; void volumeChange(int); protected: void contextMenuEvent(QContextMenuEvent *ev) Q_DECL_OVERRIDE; protected: shared_ptr m_mixdevice; KActionCollection* _mdwActions; KActionCollection* _mdwPopupActions; ViewBase* m_view; ProfControl* _pctl; Qt::Orientation m_orientation; bool m_small; KShortcutsDialog* m_shortcutsDialog; }; #endif diff --git a/gui/viewbase.cpp b/gui/viewbase.cpp index 2b9620d9..c89dd546 100644 --- a/gui/viewbase.cpp +++ b/gui/viewbase.cpp @@ -1,509 +1,508 @@ /* * 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. + * initLayout() 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; + // TODO: does not need to be a member 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() { // 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(); + initLayout(); foreach (const shared_ptr md, _mixSet) { 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()) { QAction *action = _localActionColletion->addAction("toggle_channels"); action->setText(i18n("Configure Channels...")); connect(action, SIGNAL(triggered(bool)), SLOT(configureView())); } 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(); + // We need to delete the current MixDeviceWidgets so we can recreate them +// while (!_mdws.isEmpty()) +// delete _mdws.takeFirst(); + qDeleteAll(_mdws); + _mdws.clear(); // _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/viewbase.h b/gui/viewbase.h index 7e098139..4d53bbf8 100644 --- a/gui/viewbase.h +++ b/gui/viewbase.h @@ -1,171 +1,171 @@ //-*-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 ViewBase_h #define ViewBase_h // Qt #include #include #include #include // KDE #include class QMenu; class Mixer; class MixDevice; // KMix #include "core/mixdevice.h" #include "core/mixset.h" #include "gui/guiprofile.h" #include "gui/mixdevicewidget.h" /** * The ViewBase is a virtual base class, to be used for subclassing the real Mixer Views. */ class ViewBase : public QWidget { Q_OBJECT friend class KMixToolBox; // the toolbox is everybodys friend :-) public: typedef uint ViewFlags; enum ViewFlagsEnum { // Regular flags HasMenuBar = 0x0001, MenuBarVisible = 0x0002, Horizontal = 0x0004, Vertical = 0x0008 }; ViewBase(QWidget* parent, QString id, Qt::WindowFlags f, ViewFlags vflags, QString guiProfileId, KActionCollection* actionCollection = 0); virtual ~ViewBase(); void addMixer(Mixer *mixer); QString id() const; // This method is called by ViewBase at the end of createDeviceWidgets(). The default // implementation does nothing. Subclasses can override this method for doing final // touches. This is very much like polish(), but called at an exactly well-known time. // Also I do not want Views to interfere with polish() virtual void constructionFinished() = 0; /** * Creates a suitable representation for the given MixDevice. */ - virtual QWidget* add(shared_ptr) = 0; + virtual QWidget* add(const shared_ptr) = 0; // This method is called after a configuration update (show/hide controls, split/unsplit). virtual void configurationUpdate() = 0; void load(KConfig *config); void save(KConfig *config); /** * Creates the widgets for all supported devices. The default implementation loops * over the supported MixDevice's and calls add() for each of it. */ virtual void createDeviceWidgets(); int visibleControls(); bool isDynamic() const; bool pulseaudioPresent() const; /** * Popup stuff */ virtual QMenu* getPopup(); virtual void popupReset(); virtual void showContextMenu(); virtual bool isValid() const; void setIcons(bool on); void setLabels(bool on); void setTicks(bool on); GUIProfile* guiProfile() { return GUIProfile::find(_guiProfileId); }; // GUIComplexity getGuiComplexity() { return guiComplexity; }; ProfControl* findMdw(const QString& id); ProfControl* findMdw(const QString& mdwId, GuiVisibility visibility); KActionCollection* actionCollection() { return _actions; }; QList& getMixers() { return _mixers; }; /** * Contains the widgets for the _mixSet. There is a 1:1 relationship, which means: * _mdws[i] is the Widget for the MixDevice _mixSet[i] - please see ViewBase::createDeviceWidgets(). * Hint: !! The new ViewSurround class shows that a 1:1 relationship does not work in a general scenario. * I actually DID expect this. The solution is unclear yet, probably there will be a virtual mapper method. */ QList _mdws; protected: MixSet _mixSet; QList _mixers; QMenu *_popMenu; KActionCollection* _actions; // -<- application wide action collection ViewFlags _vflags; const QString _guiProfileId; KActionCollection *_localActionColletion; QIcon configureIcon; - virtual void _setMixSet() = 0; + virtual void initLayout() = 0; void resetMdws(); void updateGuiOptions(); QPushButton* createConfigureViewButton(); void setGuiLevel(GuiVisibility& guiLevel); GuiVisibility guiLevel; public slots: virtual void refreshVolumeLevels(); // TODO remove virtual void configureView(); void toggleMenuBarSlot(); protected slots: void mousePressEvent( QMouseEvent *e ) Q_DECL_OVERRIDE; signals: void toggleMenuBar(); private: QString m_viewId; void updateMediaPlaybackIcons(); private slots: void guiVisibilitySlot(MixDeviceWidget* source, bool enable); }; #endif diff --git a/gui/viewdockareapopup.cpp b/gui/viewdockareapopup.cpp index 88308b80..01c3fdaf 100644 --- a/gui/viewdockareapopup.cpp +++ b/gui/viewdockareapopup.cpp @@ -1,446 +1,446 @@ /* * 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 "gui/viewdockareapopup.h" // Qt #include #include #include #include #include #include #include // KDE #include #include // KMix #include "apps/kmix.h" #include "core/mixer.h" #include "core/ControlManager.h" #include "core/GlobalConfig.h" #include "gui/dialogchoosebackends.h" #include "gui/guiprofile.h" #include "gui/kmixprefdlg.h" #include "gui/mdwslider.h" #include "dialogbase.h" // Restore volume button feature is incomplete => disabling for KDE 4.10 #undef RESTORE_VOLUME_BUTTON QString ViewDockAreaPopup::InternedString_Star = QString("*"); QString ViewDockAreaPopup::InternedString_Subcontrols = QString("pvolume,cvolume,pswitch,cswitch"); ProfControl* ViewDockAreaPopup::MatchAllForSoundMenu = 0; //ProfControl(ViewDockAreaPopup::InternedString_Star, ViewDockAreaPopup::InternedString_Subcontrols); ViewDockAreaPopup::ViewDockAreaPopup(QWidget* parent, QString id, ViewBase::ViewFlags vflags, QString guiProfileId, KMixWindow *dockW) : ViewBase(parent, id, 0, vflags, guiProfileId), _kmixMainWindow(dockW) { resetRefs(); setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); /* * Bug 206724: * Following are excerpts of trying to receive key events while this popup is open. * The best I could do with lots of hacks is to get the keyboard events after a mouse-click in the popup. * But such a solution is neither intuitive nor helpful - if one clicks, then usage of keyboard makes not much sense any longer. * I finally gave up on fixing Bug 206724. */ /* releaseKeyboard(); setFocusPolicy(Qt::StrongFocus); setFocus(Qt::TabFocusReason); releaseKeyboard(); mainWindowButton->setFocusPolicy(Qt::StrongFocus); Also implemented the following "event handlers", but they do not show any signs of key presses: keyPressEvent() x11Event() eventFilter() */ foreach ( Mixer* mixer, Mixer::mixers() ) { // Adding all mixers, as we potentially want to show all master controls addMixer(mixer); - // The list will be redone in _setMixSet() with the actual Mixer instances to use + // The list will be redone in initLayout() with the actual Mixer instances to use } restoreVolumeIcon = QIcon::fromTheme(QLatin1String("quickopen-file")); createDeviceWidgets(); // Register listeners for all mixers ControlManager::instance().addListener( QString(), // all mixers ControlManager::GUI|ControlManager::ControlList|ControlManager::Volume|ControlManager::MasterChanged, this, QString("ViewDockAreaPopup")); } ViewDockAreaPopup::~ViewDockAreaPopup() { ControlManager::instance().removeListener(this); delete _layoutMDW; // Hint: optionsLayout and "everything else" is deleted when "delete _layoutMDW" cascades down } void ViewDockAreaPopup::controlsChange(ControlManager::ChangeType changeType) { switch (changeType) { case ControlManager::ControlList: case ControlManager::MasterChanged: createDeviceWidgets(); break; case ControlManager::GUI: updateGuiOptions(); break; case ControlManager::Volume: refreshVolumeLevels(); break; default: ControlManager::warnUnexpectedChangeType(changeType, this); break; } } void ViewDockAreaPopup::wheelEvent ( QWheelEvent * e ) { if ( _mdws.isEmpty() ) return; } void ViewDockAreaPopup::configurationUpdate() { // TODO Do we still need configurationUpdate(). It was never implemented for ViewDockAreaPopup } // TODO Currently no right-click, as we have problems to get the ViewDockAreaPopup resized void ViewDockAreaPopup::showContextMenu() { // no right-button-menu on "dock area popup" return; } void ViewDockAreaPopup::resetRefs() { seperatorBetweenMastersAndStreams = 0; separatorBetweenMastersAndStreamsInserted = false; separatorBetweenMastersAndStreamsRequired = false; configureViewButton = 0; restoreVolumeButton1 = 0; restoreVolumeButton2 = 0; restoreVolumeButton3 = 0; restoreVolumeButton4 = 0; mainWindowButton = 0; optionsLayout = 0; _layoutMDW = 0; } -void ViewDockAreaPopup::_setMixSet() +void ViewDockAreaPopup::initLayout() { resetMdws(); if (optionsLayout != 0) { QLayoutItem *li2; while ((li2 = optionsLayout->takeAt(0))) delete li2; } // Hint : optionsLayout itself is deleted when "delete _layoutMDW" cascades down if (_layoutMDW != 0) { QLayoutItem *li; while ((li = _layoutMDW->takeAt(0))) delete li; } /* * Strangely enough, I cannot delete optionsLayout in a loop. I get a strange stacktrace: * Application: KMix (kmix), signal: Segmentation fault [...] #6 0x00007f9c9a282900 in QString::shared_null () from /usr/lib/x86_64-linux-gnu/libQtCore.so.4 -#7 0x00007f9c9d4286b0 in ViewDockAreaPopup::_setMixSet (this=0x1272b60) at /home/chris/workspace/kmix-git-trunk/gui/viewdockareapopup.cpp:164 +#7 0x00007f9c9d4286b0 in ViewDockAreaPopup::initLayout (this=0x1272b60) at /home/chris/workspace/kmix-git-trunk/gui/viewdockareapopup.cpp:164 #8 0x00007f9c9d425700 in ViewBase::createDeviceWidgets (this=0x1272b60) at /home/chris/workspace/kmix-git-trunk/gui/viewbase.cpp:137 #9 0x00007f9c9d42845b in ViewDockAreaPopup::controlsChange (this=0x1272b60, changeType=2) at /home/chris/workspace/kmix-git-trunk/gui/viewdockareapopup.cpp:91 */ // if ( optionsLayout != 0 ) // { // QLayoutItem *li2; // while ( ( li2 = optionsLayout->takeAt(0) ) ) // strangely enough, it crashes here // delete li2; // } // --- Due to the strange crash, delete everything manually : START --------------- // I am a bit confused why this doesn't crash. I moved the "optionsLayout->takeAt(0) delete" loop at the beginning, // so the objects should already be deleted. ... delete configureViewButton; delete restoreVolumeButton1; delete restoreVolumeButton2; delete restoreVolumeButton3; delete restoreVolumeButton4; delete mainWindowButton; delete seperatorBetweenMastersAndStreams; // --- Due to the strange crash, delete everything manually : END --------------- resetRefs(); setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); /* * BKO 299754: Looks like I need to explicitly delete layout(). I have no idea why * "delete _layoutMDW" is not enough, as that is supposed to be the layout * of this ViewDockAreaPopup * (Hint: it might have been 0 already. Nowadays it is definitely, see #resetRefs()) */ delete layout(); // BKO 299754 _layoutMDW = new QGridLayout(this); _layoutMDW->setSpacing(DialogBase::verticalSpacing()); // Review #121166: Add some space over device icons, otherwise they may hit window border _layoutMDW->setContentsMargins(0,5,0,0); //_layoutMDW->setSizeConstraint(QLayout::SetMinimumSize); _layoutMDW->setSizeConstraint(QLayout::SetMaximumSize); _layoutMDW->setObjectName(QLatin1String("KmixPopupLayout")); setLayout(_layoutMDW); // Adding all mixers, as we potentially want to show all master controls. Due to hotplugging - // we have to redo the list on each _setMixSet() (instead of setting it once in the Constructor) + // we have to redo the list on each initLayout() (instead of setting it once in the Constructor) _mixers.clear(); QSet preferredMixersForSoundmenu = GlobalConfig::instance().getMixersForSoundmenu(); // qCDebug(KMIX_LOG) << "Launch with " << preferredMixersForSoundmenu; foreach ( Mixer* mixer, Mixer::mixers() ) { bool useMixer = preferredMixersForSoundmenu.isEmpty() || preferredMixersForSoundmenu.contains(mixer->id()); if (useMixer) addMixer(mixer); } // The following loop is for the case when everything gets filtered out. We "reset" to show everything then. // Hint: Filtering everything out can only be an "accident", e.g. when restarting KMix with changed hardware or // backends. if ( _mixers.isEmpty() ) { foreach ( Mixer* mixer, Mixer::mixers() ) { addMixer(mixer); } } // A loop that adds the Master control of each card foreach ( Mixer* mixer, _mixers ) { // qCDebug(KMIX_LOG) << "ADD? mixerId=" << mixer->id(); shared_ptrdockMD = mixer->getLocalMasterMD(); if ( !dockMD && mixer->size() > 0 ) { // If we have no dock device yet, we will take the first available mixer device. dockMD = (*mixer)[0]; } if ( dockMD ) { // qCDebug(KMIX_LOG) << "ADD? mixerId=" << mixer->id() << ", md=" << dockMD->id(); if ( !dockMD->isApplicationStream() && dockMD->playbackVolume().hasVolume()) { // qCDebug(KMIX_LOG) << "ADD? mixerId=" << mixer->id() << ", md=" << dockMD->id() << ": YES"; // don't add application streams here. They are handled below, so // we make sure to not add them twice _mixSet.append(dockMD); } } } // loop over all cards // Add all application streams foreach ( Mixer* mixer2 , _mixers ) { foreach ( shared_ptr md, mixer2->getMixSet() ) { if (md->isApplicationStream()) { _mixSet.append(md); } } } } QWidget* ViewDockAreaPopup::add(shared_ptr md) { bool vertical = (GlobalConfig::instance().data.getTraypopupOrientation() == Qt::Vertical); // I am wondering whether using vflags for this would still make sense /* QString dummyMatchAll("*"); QString matchAllPlaybackAndTheCswitch("pvolume,cvolume,pswitch,cswitch"); // Leak | relevant | pctl Each time a stream is added, a new ProfControl gets created. // It cannot be deleted in ~MixDeviceWidget, as ProfControl* ownership is not consistent. // here a new pctl is created (could be deleted), but in ViewSliders the ProcControl is taken from the // MixDevice, which in turn uses it from the GUIProfile. // Summarizing: ProfControl* is either owned by the GUIProfile or created new (ownership unclear). // Hint: dummyMatchAll and matchAllPlaybackAndTheCswitch leak together with pctl ProfControl *pctl = new ProfControl( dummyMatchAll, matchAllPlaybackAndTheCswitch); */ if ( !md->isApplicationStream() ) { separatorBetweenMastersAndStreamsRequired = true; } if ( !separatorBetweenMastersAndStreamsInserted && separatorBetweenMastersAndStreamsRequired && md->isApplicationStream() ) { // First application stream => add separator separatorBetweenMastersAndStreamsInserted = true; int sliderColumn = vertical ? _layoutMDW->columnCount() : _layoutMDW->rowCount(); int row = vertical ? 0 : sliderColumn; int col = vertical ? sliderColumn : 0; seperatorBetweenMastersAndStreams = new QFrame(this); if (vertical) seperatorBetweenMastersAndStreams->setFrameStyle(QFrame::VLine); else seperatorBetweenMastersAndStreams->setFrameStyle(QFrame::HLine); _layoutMDW->addWidget( seperatorBetweenMastersAndStreams, row, col ); //_layoutMDW->addItem( new QSpacerItem( 5, 5 ), row, col ); } if (MatchAllForSoundMenu == 0) { // Lazy init of static member on first use MatchAllForSoundMenu = new ProfControl(ViewDockAreaPopup::InternedString_Star, ViewDockAreaPopup::InternedString_Subcontrols); } ProfControl *pctl = MatchAllForSoundMenu; MixDeviceWidget *mdw = new MDWSlider( md, // only 1 device. true, // Show Mute LE true, // Show Record LED true, // Include Mixer Name false, // Small vertical ? Qt::Vertical : Qt::Horizontal, this, // parent this // NOT ANYMORE!!! -> Is "NULL", so that there is no RMB-popup , pctl ); mdw->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); int sliderColumn = vertical ? _layoutMDW->columnCount() : _layoutMDW->rowCount(); //if (sliderColumn == 1 ) sliderColumn =0; int row = vertical ? 0 : sliderColumn; int col = vertical ? sliderColumn : 0; _layoutMDW->addWidget( mdw, row, col ); //qCDebug(KMIX_LOG) << "ADDED " << md->id() << " at column " << sliderColumn; return mdw; } void ViewDockAreaPopup::constructionFinished() { // qCDebug(KMIX_LOG) << "ViewDockAreaPopup::constructionFinished()\n"; mainWindowButton = new QPushButton(QIcon::fromTheme("show-mixer"), "" , this); mainWindowButton->setObjectName(QLatin1String("MixerPanel")); mainWindowButton->setToolTip(i18n("Show the full mixer window")); connect(mainWindowButton, SIGNAL(clicked()), SLOT(showPanelSlot())); configureViewButton = createConfigureViewButton(); optionsLayout = new QHBoxLayout(); optionsLayout->addWidget(mainWindowButton); optionsLayout->addStretch(1); optionsLayout->addWidget(configureViewButton); #ifdef RESTORE_VOLUME_BUTTON restoreVolumeButton1 = createRestoreVolumeButton(1); optionsLayout->addWidget( restoreVolumeButton1 ); // TODO enable only if user has saved a volume profile // optionsLayout->addWidget( createRestoreVolumeButton(2) ); // optionsLayout->addWidget( createRestoreVolumeButton(3) ); // optionsLayout->addWidget( createRestoreVolumeButton(4) ); #endif int sliderRow = _layoutMDW->rowCount(); _layoutMDW->addLayout(optionsLayout, sliderRow, 0, 1, _layoutMDW->columnCount()); updateGuiOptions(); _layoutMDW->update(); _layoutMDW->activate(); //bool fnc = focusNextChild(); //qCWarning(KMIX_LOG) << "fnc=" <setToolTip(i18n("Load volume profile %1", storageSlot)); profileButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); return profileButton; } void ViewDockAreaPopup::refreshVolumeLevels() { foreach ( QWidget* qw, _mdws ) { //qCDebug(KMIX_LOG) << "rvl: " << qw; MixDeviceWidget* mdw = qobject_cast(qw); if ( mdw != 0 ) mdw->update(); } } void ViewDockAreaPopup::configureView() { // Q_ASSERT( !pulseaudioPresent() ); // QSet currentlyActiveMixersInDockArea; // foreach ( Mixer* mixer, _mixers ) // { // currentlyActiveMixersInDockArea.insert(mixer->id()); // } KMixPrefDlg* prefDlg = KMixPrefDlg::getInstance(); //prefDlg->setActiveMixersInDock(currentlyActiveMixersInDockArea); prefDlg->switchToPage(KMixPrefDlg::PrefSoundMenu); } /** * This gets activated whne a user clicks the "Mixer" PushButton in this popup. */ void ViewDockAreaPopup::showPanelSlot() { _kmixMainWindow->setVisible(true); KWindowSystem::setOnDesktop(_kmixMainWindow->winId(), KWindowSystem::currentDesktop()); KWindowSystem::activateWindow(_kmixMainWindow->winId()); // This is only needed when the window is already visible. static_cast(parent())->hide(); } diff --git a/gui/viewdockareapopup.h b/gui/viewdockareapopup.h index 55cc50a5..185890df 100644 --- a/gui/viewdockareapopup.h +++ b/gui/viewdockareapopup.h @@ -1,89 +1,89 @@ //-*-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 ViewDockAreaPopup_h #define ViewDockAreaPopup_h #include "viewbase.h" #include #include #include "core/ControlManager.h" class QBoxLayout; class QFrame; class QGridLayout; class QWidget; class Mixer; class MixDevice; class KMixWindow; class ViewDockAreaPopup : public ViewBase { Q_OBJECT public: ViewDockAreaPopup(QWidget* parent, QString id, ViewBase::ViewFlags vflags, QString guiProfileId, KMixWindow *dockW); virtual ~ViewDockAreaPopup(); QWidget* add(shared_ptr md) Q_DECL_OVERRIDE; void constructionFinished() Q_DECL_OVERRIDE; void refreshVolumeLevels() Q_DECL_OVERRIDE; void showContextMenu() Q_DECL_OVERRIDE; void configurationUpdate() Q_DECL_OVERRIDE; protected: KMixWindow *_kmixMainWindow; void wheelEvent ( QWheelEvent * e ) Q_DECL_OVERRIDE; - void _setMixSet() Q_DECL_OVERRIDE; + void initLayout() Q_DECL_OVERRIDE; private: QGridLayout* _layoutMDW; QPushButton* createRestoreVolumeButton ( int storageSlot ); bool separatorBetweenMastersAndStreamsInserted; bool separatorBetweenMastersAndStreamsRequired; QFrame* seperatorBetweenMastersAndStreams; QBoxLayout* optionsLayout; QPushButton* configureViewButton; QPushButton *mainWindowButton; QPushButton *restoreVolumeButton1; QPushButton *restoreVolumeButton2; QPushButton *restoreVolumeButton3; QPushButton *restoreVolumeButton4; QIcon restoreVolumeIcon; static ProfControl* MatchAllForSoundMenu; static QString InternedString_Star; static QString InternedString_Subcontrols; public slots: void controlsChange(ControlManager::ChangeType changeType); void configureView() Q_DECL_OVERRIDE; private slots: void showPanelSlot(); void resetRefs(); }; #endif diff --git a/gui/viewsliders.cpp b/gui/viewsliders.cpp index 7f01b764..24b86014 100644 --- a/gui/viewsliders.cpp +++ b/gui/viewsliders.cpp @@ -1,444 +1,410 @@ /* * 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" +#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" -#include "gui/verticaltext.h" - // KDE #include // Qt -#include #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) +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); - _configureViewButton = 0; - _layoutMDW = 0; - _layoutSliders = 0; - _layoutEnum = 0; - emptyStreamHint = 0; + 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 _layoutMDW; -// qDeleteAll(_separators); + 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(shared_ptr md) +QWidget *ViewSliders::add(const shared_ptr md) { MixDeviceWidget *mdw; Qt::Orientation orientation = GlobalConfig::instance().data.getToplevelOrientation(); - if ( md->isEnum() ) + if (md->isEnum()) // control is a switch { - 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 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 - , md->controlProfile() - ); // View widget - _layoutSliders->addWidget(mdw); + 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; + return (mdw); } -void ViewSliders::_setMixSet() +void ViewSliders::initLayout() { resetMdws(); - delete emptyStreamHint; - emptyStreamHint = 0; + delete m_emptyStreamHint; + m_emptyStreamHint = nullptr; - // Our _layoutSliders now should only contain spacer widgets from the addSpacing() calls in add() above. + // 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 (_layoutSliders != 0) + if (m_layoutSliders!=nullptr) { QLayoutItem *li; - while ((li = _layoutSliders->takeAt(0))) - delete li; -// delete _layoutSliders; - _layoutSliders = 0; + while ((li = m_layoutSliders->takeAt(0))!=nullptr) delete li; + m_layoutSliders = nullptr; } - delete _configureViewButton; - _configureViewButton = 0; + delete m_configureViewButton; + m_configureViewButton = nullptr; - if (_layoutEnum != 0) + if (m_layoutSwitches!=nullptr) { - QLayoutItem *li2; - while ((li2 = _layoutEnum->takeAt(0)) != 0) - delete li2; -// delete _layoutEnum; - _layoutEnum = 0; + QLayoutItem *li; + while ((li = m_layoutSwitches->takeAt(0))!=nullptr) delete li; + m_layoutSwitches = nullptr; } - delete _layoutMDW; - _layoutMDW = 0; + 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); - // 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 sliders + m_layoutMDW->setAlignment(Qt::AlignLeft|Qt::AlignTop); - 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); + m_layoutSliders = new QVBoxLayout(); + m_layoutSliders->setAlignment(Qt::AlignVCenter|Qt::AlignLeft); } - else + else // vertical sliders { - // 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); + m_layoutMDW->setAlignment(Qt::AlignHCenter|Qt::AlignTop); - // 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(); + m_layoutSliders = new QHBoxLayout(); + m_layoutSliders->setAlignment(Qt::AlignHCenter|Qt::AlignTop); + } + m_layoutSliders->setContentsMargins(0, 0, 0, 0); + m_layoutSliders->setSpacing(0); - _layoutEnum = new QVBoxLayout(); - _layoutMDW->addLayout(_layoutEnum); + 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. - 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); + 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)); + 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 + QList > mds; // For temporary test #endif - // This method iterates the controls from the Profile + // 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. - GUIProfile* guiprof = guiProfile(); - if (guiprof != 0) + const GUIProfile *guiprof = guiProfile(); + if (guiprof!=nullptr) { - foreach (Mixer* mixer , _mixers ){ - const MixSet& mixset = mixer->getMixSet(); - - foreach ( ProfControl* control, guiprof->getControls() ) + foreach (Mixer *mixer, _mixers) { - //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]; + const MixSet &mixset = mixer->getMixSet(); - if ( md->id().contains(idRegexp) ) + 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; iuseSubcontrolPlayback() && ( 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); + const shared_ptr 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") - { mds.append(md);} // For temporary test + if ( md->id() == "Front:0" || md->id() == "Surround:0") + { // For temporary test + mds.append(md); + } #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 + // 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 ( !pulseaudioPresent() ) // TODO 11 Dynamic view configuration - if ( !isDynamic() ) - { - _configureViewButton = createConfigureViewButton(); - _layoutEnum->addStretch(); - _layoutEnum->addWidget(_configureViewButton); - } +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(); + 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++) + for (int i = 0; i<_mdws.count(); ++i) { - MDWSlider* mdw = ::qobject_cast(_mdws[i]); - if (mdw && mdw->isVisibleTo(this)) + 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; - 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 && labelExtent>0) + + //qCDebug(KMIX_LOG) << "topPartExtent is " << topPartExtent; + for (int i = 0; i<_mdws.count(); ++i) { - // additional options for sliders - mdwSlider->setLabelExtent(labelExtent); - } + 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); - 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(); + 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 127d5d5e..401ab477 100644 --- a/gui/viewsliders.h +++ b/gui/viewsliders.h @@ -1,64 +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; -#include -#include +class QGridLayout; class QLabel; -#include -class QWidget; +class QPushButton; class Mixer; + #include "gui/viewbase.h" #include "core/ControlManager.h" + class ViewSliders : public ViewBase { Q_OBJECT + public: - ViewSliders(QWidget* parent, QString id, Mixer* mixer, ViewBase::ViewFlags vflags, QString guiProfileId, KActionCollection *actColl); + ViewSliders(QWidget *parent, const QString &id, Mixer *mixer, ViewBase::ViewFlags vflags, const QString &guiProfileId, KActionCollection *actColl); virtual ~ViewSliders(); - QWidget* add(shared_ptr) Q_DECL_OVERRIDE; + 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 _setMixSet() Q_DECL_OVERRIDE; + void initLayout() Q_DECL_OVERRIDE; private: void refreshVolumeLevels() Q_DECL_OVERRIDE; - QBoxLayout* _layoutMDW; - QLayout* _layoutSliders; - QBoxLayout* _layoutEnum; - QHash _separators; - QPushButton* _configureViewButton; - QLabel* emptyStreamHint; + QGridLayout *m_layoutMDW; + QBoxLayout *m_layoutSliders; + QBoxLayout *m_layoutSwitches; + QPushButton *m_configureViewButton; + QLabel *m_emptyStreamHint; }; #endif -