summaryrefslogtreecommitdiff
path: root/extra/kdemultimedia
diff options
context:
space:
mode:
authorroot <root@rshg047.dnsready.net>2011-04-07 04:58:41 +0000
committerroot <root@rshg047.dnsready.net>2011-04-07 04:58:41 +0000
commitdb464f25fec54ddfa7825a17b20fda025c296f8c (patch)
tree4bb5b8ac4dfd67096125b92d047323e6acbb86cd /extra/kdemultimedia
parentbd65dc7107bfdd881c3b5451d0300824cf435428 (diff)
Thu Apr 7 04:58:41 UTC 2011
Diffstat (limited to 'extra/kdemultimedia')
-rw-r--r--extra/kdemultimedia/PKGBUILD17
-rw-r--r--extra/kdemultimedia/kmix-locale-pulseaudio.patch1117
2 files changed, 1128 insertions, 6 deletions
diff --git a/extra/kdemultimedia/PKGBUILD b/extra/kdemultimedia/PKGBUILD
index f73f5d382..afdc0a8a1 100644
--- a/extra/kdemultimedia/PKGBUILD
+++ b/extra/kdemultimedia/PKGBUILD
@@ -1,4 +1,4 @@
-# $Id: PKGBUILD 111780 2011-02-28 23:55:31Z andrea $
+# $Id: PKGBUILD 118074 2011-04-05 10:08:23Z andrea $
# Maintainer: Andrea Scarpino <andrea@archlinux.org>
# Contributor: Pierre Schmitz <pierre@archlinux.de>
@@ -10,7 +10,7 @@ pkgname=('kdemultimedia-dragonplayer'
'kdemultimedia-kmix'
'kdemultimedia-kscd'
'kdemultimedia-mplayerthumbs')
-pkgver=4.6.1
+pkgver=4.6.2
pkgrel=1
arch=('i686' 'x86_64')
url='http://www.kde.org'
@@ -19,12 +19,17 @@ groups=('kde' 'kdemultimedia')
makedepends=('pkgconfig' 'cmake' 'automoc4' 'kdebase-runtime' 'tunepimp'
'ffmpeg' 'mplayer' 'docbook-xsl' 'pulseaudio' 'libmusicbrainz3')
source=("http://download.kde.org/stable/${pkgver}/src/${pkgbase}-${pkgver}.tar.bz2"
- 'mplayerthumbs.config')
-sha1sums=('ad8f6e45d60d2b9cc0fd806e000076fcc0c07a5f'
- 'ba016fa2563c14ffcba852c62506b66bfc6ee683')
+ 'mplayerthumbs.config'
+ 'kmix-locale-pulseaudio.patch')
+sha1sums=('4447d26488e3b97c43580d86ad47cee77ff98578'
+ 'ba016fa2563c14ffcba852c62506b66bfc6ee683'
+ '5aa443ace9f4b1906c227664b4e50bace666cf99')
build() {
- cd $srcdir
+ cd ${srcdir}/${pkgbase}-${pkgver}
+ patch -Np0 -i ${srcdir}/kmix-locale-pulseaudio.patch
+
+ cd ${srcdir}
mkdir build
cd build
cmake ../${pkgbase}-${pkgver} \
diff --git a/extra/kdemultimedia/kmix-locale-pulseaudio.patch b/extra/kdemultimedia/kmix-locale-pulseaudio.patch
new file mode 100644
index 000000000..88be8dbde
--- /dev/null
+++ b/extra/kdemultimedia/kmix-locale-pulseaudio.patch
@@ -0,0 +1,1117 @@
+Index: kmix/apps/kmix.cpp
+===================================================================
+--- kmix/apps/kmix.cpp (revision 1226955)
++++ kmix/apps/kmix.cpp (revision 1226956)
+@@ -78,8 +78,6 @@
+ m_dockWidget(),
+ m_dontSetDefaultCardOnStart (false)
+ {
+- _cornerLabelNew = 0;
+-
+ setObjectName( QLatin1String("KMixWindow" ));
+ // disable delete-on-close because KMix might just sit in the background waiting for cards to be plugged in
+ setAttribute(Qt::WA_DeleteOnClose, false);
+@@ -184,16 +182,29 @@
+
+ void KMixWindow::initActionsAfterInitMixer()
+ {
++ bool isPulseAudio = false;
+ // Add "launch_pavucontrol" to menu, if Pulseaudio backend is in use
+ foreach( Mixer* mixer, Mixer::mixers() )
+ {
+- if ( mixer->getDriverName() == "PulseAudio") {
++ if ( mixer->getDriverName() == "PulseAudio")
++ {
++ isPulseAudio = true;
+ KAction* action = actionCollection()->addAction( "launch_pavucontrol" );
+ action->setText( i18n( "Audio setup (&Pulseaudio)" ) );
+ connect(action, SIGNAL(triggered(bool) ), SLOT( slotPavucontrolExec() ));
+ break;
+ }
+ }
++
++ if (! isPulseAudio )
++ {
++ QPixmap cornerNewPM = KIconLoader::global()->loadIcon( "tab-new", KIconLoader::Toolbar, KIconLoader::SizeSmall );
++ QPushButton* _cornerLabelNew = new QPushButton();
++ _cornerLabelNew->setIcon(cornerNewPM);
++ //cornerLabelNew->setSizePolicy(QSizePolicy());
++ m_wsMixers->setCornerWidget(_cornerLabelNew, Qt::TopLeftCorner);
++ connect ( _cornerLabelNew, SIGNAL( clicked() ), SLOT (newView() ) );
++ }
+ }
+
+ void KMixWindow::initPrefDlg()
+@@ -210,15 +221,10 @@
+ m_wsMixers = new KTabWidget();
+ m_wsMixers->setDocumentMode(true);
+ setCentralWidget(m_wsMixers);
+- m_wsMixers->setTabsClosable(true);
++ m_wsMixers->setTabsClosable(false);
+ connect (m_wsMixers, SIGNAL(tabCloseRequested(int)), SLOT(saveAndCloseView(int)) );
+
+ QPixmap cornerNewPM = KIconLoader::global()->loadIcon( "tab-new", KIconLoader::Toolbar, KIconLoader::SizeSmall );
+- _cornerLabelNew = new QPushButton();
+- _cornerLabelNew->setIcon(cornerNewPM);
+- //cornerLabelNew->setSizePolicy(QSizePolicy());
+- m_wsMixers->setCornerWidget(_cornerLabelNew, Qt::TopLeftCorner);
+- connect ( _cornerLabelNew, SIGNAL( clicked() ), SLOT (newView() ) );
+
+ connect( m_wsMixers, SIGNAL( currentChanged ( int ) ), SLOT( newMixerShown(int)) );
+
+@@ -516,6 +522,7 @@
+ addMixerWidget(mixer->id(), guiprof, -1);
+ }
+ else {
++ // did exist => remove and insert new guiprof at old position
+ int indexOfTab = m_wsMixers->indexOf(kmw);
+ if ( indexOfTab != -1 ) m_wsMixers->removeTab(indexOfTab);
+ delete kmw;
+@@ -676,9 +683,9 @@
+ m_wsMixers->removeTab(idx);
+ delete kmw;
+
+- if ( m_wsMixers->count() < 2 ) {
+- m_wsMixers->setTabsClosable(false);
+- }
++ bool isPulseAudio = kmw->mixer()->getDriverName() == "PulseAudio";
++ m_wsMixers->setTabsClosable(!isPulseAudio && m_wsMixers->count() > 1);
++
+ saveViewConfig();
+ }
+ kDebug() << "Exit";
+@@ -874,9 +881,9 @@
+ if ( kmw->getGuiprof()->getId() == m_defaultCardOnStart ) {
+ m_wsMixers->setCurrentWidget(kmw);
+ }
+- if ( m_wsMixers->count() > 1 ) {
+- m_wsMixers->setTabsClosable(true);
+- }
++
++ bool isPulseAudio = mixer->getDriverName() == "PulseAudio";
++ m_wsMixers->setTabsClosable(!isPulseAudio && m_wsMixers->count() > 1);
+ m_dontSetDefaultCardOnStart = false;
+
+
+Index: kmix/apps/kmix.h
+===================================================================
+--- kmix/apps/kmix.h (revision 1226955)
++++ kmix/apps/kmix.h (revision 1226956)
+@@ -116,7 +116,6 @@
+ Qt::Orientation m_toplevelOrientation;
+
+ KTabWidget *m_wsMixers;
+- QPushButton* _cornerLabelNew;
+
+ KMixPrefDlg *m_prefDlg;
+ KMixDockWidget *m_dockWidget;
+Index: kmix/gui/viewbase.cpp
+===================================================================
+--- kmix/gui/viewbase.cpp (revision 1226956)
++++ kmix/gui/viewbase.cpp (revision 1226957)
+@@ -226,34 +226,35 @@
+
+ // Check the guiprofile... if it is not the fallback GUIProfile, then
+ // make sure that we add a specific entry for any devices not present.
+- if ( 0 != _guiprof && GUIProfile::fallbackProfile(_mixer) != _guiprof ) {
++ if ( 0 != _guiprof && GUIProfile::fallbackProfile(_mixer) != _guiprof ) // TODO colin/cesken IMO calling GUIProfile::fallbackProfile(_mixer) is wrong, as it ALWAYS creates a new Object. fallbackProfile() would need to cache the created fallback profiles so this should make any sense.
++ {
+ kDebug(67100) << "Dynamic mixer " << _mixer->id() << " is NOT using Fallback GUIProfile. Checking to see if new controls are present";
+
+ QList<QString> new_mix_devices;
+ MixSet ms = _mixer->getMixSet();
+ for (int i=0; i < ms.count(); ++i)
++ {
+ new_mix_devices.append("^" + ms[i]->id() + "$");
++ kDebug(67100) << "new_mix_devices.append => " << ms[i]->id();
++ }
+
++ GUIProfile::ControlSet& ctlSet = _guiprof->getControls();
++
+ // std::vector<ProfControl*>::const_iterator itEnd = _guiprof->_controls.end();
+ // for ( std::vector<ProfControl*>::const_iterator it = _guiprof->_controls.begin(); it != itEnd; ++it)
+ // new_mix_devices.removeAll((*it)->id);
+ // TODO Please check this change, Colin
+- foreach ( ProfControl* pctl, _guiprof->getControls() ) {
++ foreach ( ProfControl* pctl, ctlSet ) {
+ new_mix_devices.removeAll(pctl->id);
+ }
+
+
+ if ( new_mix_devices.count() > 0 ) {
+ kDebug(67100) << "Found " << new_mix_devices.count() << " new controls. Adding to GUIProfile";
++ QString sctlMatchAll("*");
+ while ( new_mix_devices.count() > 0 ) {
+- QString sctlMatchAll("*");
+ QString new_mix_devices0 = new_mix_devices.takeAt(0);
+- ProfControl* ctl = new ProfControl(new_mix_devices0, sctlMatchAll);
+-// ctl->id = new_mix_devices.takeAt(0);
+-// ctl->setSubcontrols(QString("*"));
+-// ctl->tab = (_guiprof->tabs())[0]->name(); // Use the first tab... not ideal but should work most of the time;
+-// ctl->show = "simple";
+- _guiprof->getControls().push_back(ctl);
++ ctlSet.push_back(new ProfControl(new_mix_devices0, sctlMatchAll));
+ }
+ _guiprof->setDirty();
+ }
+Index: kmix/gui/viewbase.cpp
+===================================================================
+--- kmix/gui/viewbase.cpp (revision 1226957)
++++ kmix/gui/viewbase.cpp (revision 1226958)
+@@ -33,6 +33,7 @@
+ #include <kactioncollection.h>
+ #include <ktoggleaction.h>
+ #include <kstandardaction.h>
++#include <kmessagebox.h>
+ // KMix
+ #include "dialogviewconfiguration.h"
+ #include "gui/guiprofile.h"
+@@ -43,7 +44,7 @@
+
+
+ ViewBase::ViewBase(QWidget* parent, const char* id, Mixer* mixer, Qt::WFlags f, ViewBase::ViewFlags vflags, GUIProfile *guiprof, KActionCollection *actionColletion)
+- : QWidget(parent, f), _actions(actionColletion), _vflags(vflags), _guiprof(guiprof)
++ : QWidget(parent, f), _popMenu(NULL), _actions(actionColletion), _vflags(vflags), _guiprof(guiprof)
+ {
+ setObjectName(id);
+ m_viewId = id;
+@@ -73,9 +74,11 @@
+ }
+ }
+ }
+- QAction *action = _localActionColletion->addAction("toggle_channels");
+- action->setText(i18n("&Channels"));
+- connect(action, SIGNAL(triggered(bool) ), SLOT(configureView()));
++ if ( !_mixer->isDynamic() ) {
++ QAction *action = _localActionColletion->addAction("toggle_channels");
++ action->setText(i18n("&Channels"));
++ connect(action, SIGNAL(triggered(bool) ), SLOT(configureView()));
++ }
+ connect ( _mixer, SIGNAL(controlChanged()), this, SLOT(refreshVolumeLevels()) );
+ connect ( _mixer, SIGNAL(controlsReconfigured(const QString&)), this, SLOT(controlsReconfigured(const QString&)) );
+ }
+@@ -95,7 +98,7 @@
+
+ bool ViewBase::isValid() const
+ {
+- return ( _mixSet->count() > 0 || _mixer->dynamic() );
++ return ( _mixSet->count() > 0 || _mixer->isDynamic() );
+ }
+
+ void ViewBase::setIcons (bool on) { KMixToolBox::setIcons (_mdws, on ); }
+@@ -170,6 +173,8 @@
+ {
+ QAction *a;
+
++ if ( _popMenu )
++ delete _popMenu;
+ _popMenu = new KMenu( this );
+ _popMenu->addTitle( KIcon( QLatin1String( "kmix" ) ), i18n("Device Settings" ));
+
+@@ -222,44 +227,8 @@
+
+ void ViewBase::setMixSet()
+ {
+- if ( _mixer->dynamic()) {
++ if ( _mixer->isDynamic() ) {
+
+- // Check the guiprofile... if it is not the fallback GUIProfile, then
+- // make sure that we add a specific entry for any devices not present.
+- if ( 0 != _guiprof && GUIProfile::fallbackProfile(_mixer) != _guiprof ) // TODO colin/cesken IMO calling GUIProfile::fallbackProfile(_mixer) is wrong, as it ALWAYS creates a new Object. fallbackProfile() would need to cache the created fallback profiles so this should make any sense.
+- {
+- kDebug(67100) << "Dynamic mixer " << _mixer->id() << " is NOT using Fallback GUIProfile. Checking to see if new controls are present";
+-
+- QList<QString> new_mix_devices;
+- MixSet ms = _mixer->getMixSet();
+- for (int i=0; i < ms.count(); ++i)
+- {
+- new_mix_devices.append("^" + ms[i]->id() + "$");
+- kDebug(67100) << "new_mix_devices.append => " << ms[i]->id();
+- }
+-
+- GUIProfile::ControlSet& ctlSet = _guiprof->getControls();
+-
+-// std::vector<ProfControl*>::const_iterator itEnd = _guiprof->_controls.end();
+-// for ( std::vector<ProfControl*>::const_iterator it = _guiprof->_controls.begin(); it != itEnd; ++it)
+-// new_mix_devices.removeAll((*it)->id);
+- // TODO Please check this change, Colin
+- foreach ( ProfControl* pctl, ctlSet ) {
+- new_mix_devices.removeAll(pctl->id);
+- }
+-
+-
+- if ( new_mix_devices.count() > 0 ) {
+- kDebug(67100) << "Found " << new_mix_devices.count() << " new controls. Adding to GUIProfile";
+- QString sctlMatchAll("*");
+- while ( new_mix_devices.count() > 0 ) {
+- QString new_mix_devices0 = new_mix_devices.takeAt(0);
+- ctlSet.push_back(new ProfControl(new_mix_devices0, sctlMatchAll));
+- }
+- _guiprof->setDirty();
+- }
+- }
+-
+ // We need to delete the current MixDeviceWidgets so we can redraw them
+ while (!_mdws.isEmpty()) {
+ QWidget* mdw = _mdws.last();
+@@ -280,6 +249,8 @@
+ */
+ void ViewBase::configureView() {
+
++ Q_ASSERT( !_mixer->isDynamic() );
++
+ DialogViewConfiguration* dvc = new DialogViewConfiguration(0, *this);
+ dvc->show();
+ // !! The dialog is modal. Does it delete itself?
+@@ -302,6 +273,10 @@
+ kDebug(67100) << "KMixToolBox::loadView() grp=" << grp.toAscii();
+
+ static char guiComplexity[3][20] = { "simple", "extended", "all" };
++
++ // Certain bits are not saved for dynamic mixers (e.g. PulseAudio)
++ bool dynamic = _mixer->isDynamic();
++
+ for ( int tries = 0; tries < 3; tries++ )
+ {
+ bool atLeastOneControlIsShown = false;
+@@ -315,12 +290,12 @@
+ Workaround: If found, write back correct group name.
+ */
+ MixDeviceWidget* mdw = (MixDeviceWidget*)qmdw;
+- QString devgrp;
+- devgrp.sprintf( "%s.%s.%s", grp.toAscii().data(), mdw->mixDevice()->mixer()->id().toAscii().data(), mdw->mixDevice()->id().toAscii().data() );
++ MixDevice* md = mdw->mixDevice();
++
++ QString devgrp = QString("%1.%2.%3").arg(grp).arg(md->mixer()->id()).arg(md->id());
+ KConfigGroup devcg = config->group( devgrp );
+
+- QString buggyDevgrp;
+- buggyDevgrp.sprintf( "%s.%s.%s", grp.toAscii().data(), view->id().toAscii().data(), mdw->mixDevice()->id().toAscii().data() );
++ QString buggyDevgrp = QString("%1.%2.%3").arg(grp).arg(view->id()).arg(md->id());
+ KConfigGroup buggyDevgrpCG = config->group( buggyDevgrp );
+ if ( buggyDevgrpCG.exists() ) {
+ buggyDevgrpCG.copyTo(&devcg);
+@@ -335,7 +310,7 @@
+ }
+
+ bool mdwEnabled = false;
+- if ( devcg.hasKey("Show") )
++ if ( !dynamic && devcg.hasKey("Show") )
+ {
+ mdwEnabled = ( true == devcg.readEntry("Show", true) );
+ //kDebug() << "Load devgrp" << devgrp << "show=" << mdwEnabled;
+@@ -382,20 +357,23 @@
+ QString grp = "View.";
+ grp += view->id();
+ // KConfigGroup cg = config->group( grp );
+- kDebug(67100) << "KMixToolBox::saveView() grp=" << grp.toAscii();
++ kDebug(67100) << "KMixToolBox::saveView() grp=" << grp;
+
++ // Certain bits are not saved for dynamic mixers (e.g. PulseAudio)
++ bool dynamic = _mixer->isDynamic();
++
+ for (int i=0; i < view->_mdws.count(); ++i ){
+ QWidget *qmdw = view->_mdws[i];
+ if ( qmdw->inherits("MixDeviceWidget") )
+ {
+ MixDeviceWidget* mdw = (MixDeviceWidget*)qmdw;
++ MixDevice* md = mdw->mixDevice();
+
+ //kDebug(67100) << " grp=" << grp.toAscii();
+ //kDebug(67100) << " mixer=" << view->id().toAscii();
+ //kDebug(67100) << " mdwPK=" << mdw->mixDevice()->id().toAscii();
+
+- QString devgrp;
+- devgrp.sprintf( "%s.%s.%s", grp.toAscii().data(), mdw->mixDevice()->mixer()->id().toAscii().data(), mdw->mixDevice()->id().toAscii().data() );
++ QString devgrp = QString("%1.%2.%3").arg(grp).arg(md->mixer()->id()).arg(md->id());
+ KConfigGroup devcg = config->group( devgrp );
+
+ if ( mdw->inherits("MDWSlider") )
+@@ -403,15 +381,19 @@
+ // only sliders have the ability to split apart in mutliple channels
+ devcg.writeEntry( "Split", ! mdw->isStereoLinked() );
+ }
+- devcg.writeEntry( "Show" , mdw->isVisibleTo(view) );
+-kDebug() << "Save devgrp" << devgrp << "show=" << mdw->isVisibleTo(view);
++ if ( !dynamic ) {
++ devcg.writeEntry( "Show" , mdw->isVisibleTo(view) );
++ kDebug() << "Save devgrp" << devgrp << "show=" << mdw->isVisibleTo(view);
++ }
+
+ } // inherits MixDeviceWidget
+ } // for all MDW's
+
+- kDebug(67100) << "GUIProfile is dirty: " << guiProfile()->isDirty();
+- if ( guiProfile()->isDirty() ) {
+- guiProfile()->writeProfile();
++ if ( !dynamic ) {
++ // We do not save GUIProfiles (as they cannot be customised) for dynamic mixers (e.g. PulseAudio)
++ kDebug(67100) << "GUIProfile is dirty: " << guiProfile()->isDirty();
++ if ( guiProfile()->isDirty() )
++ guiProfile()->writeProfile();
+ }
+ }
+
+Index: kmix/gui/mdwslider.cpp
+===================================================================
+--- kmix/gui/mdwslider.cpp (revision 1226957)
++++ kmix/gui/mdwslider.cpp (revision 1226958)
+@@ -86,10 +86,14 @@
+ KToggleAction *taction = _mdwActions->add<KToggleAction>( "stereo" );
+ taction->setText( i18n("&Split Channels") );
+ connect( taction, SIGNAL( triggered(bool) ), SLOT( toggleStereoLinked() ) );
+- KAction *action = _mdwActions->add<KToggleAction>( "hide" );
+- action->setText( i18n("&Hide") );
+- connect( action, SIGNAL( triggered(bool) ), SLOT( setDisabled() ) );
+
++ KAction *action;
++ if ( ! m_mixdevice->mixer()->isDynamic() ) {
++ action = _mdwActions->add<KToggleAction>( "hide" );
++ action->setText( i18n("&Hide") );
++ connect( action, SIGNAL( triggered(bool) ), SLOT( setDisabled() ) );
++ }
++
+ if( m_mixdevice->playbackVolume().hasSwitch() ) {
+ taction = _mdwActions->add<KToggleAction>( "mute" );
+ taction->setText( i18n("&Muted") );
+@@ -129,8 +133,8 @@
+ #ifdef __GNUC__
+ #warning GLOBAL SHORTCUTS ARE NOW ASSIGNED TO ALL CONTROLS, as enableGlobalShortcut(), has not been committed
+ #endif
+- if ( ! mixDevice()->isEthereal() ) {
+- // virtual / ethereal controls won't get shortcuts
++ if ( ! mixDevice()->mixer()->isDynamic() ) {
++ // virtual / dynamic controls won't get shortcuts
+ b->setGlobalShortcut(dummyShortcut); // -<- enableGlobalShortcut() is not there => use workaround
+ // b->enableGlobalShortcut();
+ connect( b, SIGNAL( triggered(bool) ), SLOT( increaseVolume() ) );
+@@ -143,8 +147,8 @@
+ #ifdef __GNUC__
+ #warning GLOBAL SHORTCUTS ARE NOW ASSIGNED TO ALL CONTROLS, as enableGlobalShortcut(), has not been committed
+ #endif
+- if ( ! mixDevice()->isEthereal() ) {
+- // virtual / ethereal controls won't get shortcuts
++ if ( ! mixDevice()->mixer()->isDynamic() ) {
++ // virtual / dynamic controls won't get shortcuts
+ b->setGlobalShortcut(dummyShortcut); // -<- enableGlobalShortcut() is not there => use workaround
+ // b->enableGlobalShortcut();
+ connect( b, SIGNAL( triggered(bool) ), SLOT( decreaseVolume() ) );
+@@ -157,8 +161,8 @@
+ #ifdef __GNUC__
+ #warning GLOBAL SHORTCUTS ARE NOW ASSIGNED TO ALL CONTROLS, as enableGlobalShortcut(), has not been committed
+ #endif
+- if ( ! mixDevice()->isEthereal() ) {
+- // virtual / ethereal controls won't get shortcuts
++ if ( ! mixDevice()->mixer()->isDynamic() ) {
++ // virtual / dynamic controls won't get shortcuts
+ b->setGlobalShortcut(dummyShortcut); // -<- enableGlobalShortcut() is not there => use workaround
+ // b->enableGlobalShortcut();
+ connect( b, SIGNAL( triggered(bool) ), SLOT( toggleMuted() ) );
+Index: kmix/gui/viewdockareapopup.h
+===================================================================
+--- kmix/gui/viewdockareapopup.h (revision 1226957)
++++ kmix/gui/viewdockareapopup.h (revision 1226958)
+@@ -24,7 +24,6 @@
+ #include "viewbase.h"
+
+ class QGridLayout;
+-class QPushButton;
+ class QWidget;
+
+ class Mixer;
+@@ -48,8 +47,6 @@
+
+ protected:
+ KMixWindow *_dock;
+- //MixDevice *_dockDevice;
+- QPushButton *_showPanelBox;
+
+ void wheelEvent ( QWheelEvent * e );
+ virtual void _setMixSet();
+Index: kmix/gui/viewsliders.cpp
+===================================================================
+--- kmix/gui/viewsliders.cpp (revision 1226957)
++++ kmix/gui/viewsliders.cpp (revision 1226958)
+@@ -148,7 +148,7 @@
+ {
+ const MixSet& mixset = _mixer->getMixSet();
+
+- if ( _mixer->dynamic() ) {
++ if ( _mixer->isDynamic() ) {
+ // We will be recreating our sliders, so make sure we trash all the separators too.
+ qDeleteAll(_separators);
+ _separators.clear();
+Index: kmix/gui/guiprofile.cpp
+===================================================================
+--- kmix/gui/guiprofile.cpp (revision 1226957)
++++ kmix/gui/guiprofile.cpp (revision 1226958)
+@@ -158,8 +158,12 @@
+ {
+ GUIProfile* guiprof = 0;
+
+- if ( mixer == 0 || profileName.isEmpty() ) {
++ if ( mixer == 0 || profileName.isEmpty() )
+ return 0;
++
++ if ( mixer->isDynamic() ) {
++ kDebug(67100) << "GUIProfile::find() Not loading GUIProfile for Dynamic Mixer (e.g. PulseAudio)";
++ return 0;
+ }
+
+ QString requestedProfileName;
+Index: kmix/gui/viewbase.h
+===================================================================
+--- kmix/gui/viewbase.h (revision 1226957)
++++ kmix/gui/viewbase.h (revision 1226958)
+@@ -133,7 +133,7 @@
+
+ ViewFlags _vflags;
+ GUIProfile* _guiprof;
+- KActionCollection *_localActionColletion;
++ KActionCollection *_localActionColletion;
+
+ virtual void _setMixSet() = 0;
+
+Index: kmix/gui/viewdockareapopup.cpp
+===================================================================
+--- kmix/gui/viewdockareapopup.cpp (revision 1226957)
++++ kmix/gui/viewdockareapopup.cpp (revision 1226958)
+@@ -88,8 +88,8 @@
+ {
+ // kDebug(67100) << "ViewDockAreaPopup::setMixSet()\n";
+
+- if ( _mixer->dynamic() ) {
+- // Our _layoutMDW now should only contain spacer widgets from the QSpacerItems's in add() below.
++ if ( _mixer->isDynamic() ) {
++ // Our _layoutMDW now should only contain spacer widgets from the QSpacerItem's in add() below.
+ // We need to trash those too otherwise all sliders gradually migrate away from the edge :p
+ QLayoutItem *li;
+ while ( ( li = _layoutMDW->takeAt(0) ) )
+@@ -114,9 +114,9 @@
+ QString matchAllPlaybackAndTheCswitch("pvolume,pswitch,cswitch");
+ ProfControl *pctl = new ProfControl( dummyMatchAll, matchAllPlaybackAndTheCswitch);
+ MixDeviceWidget *mdw = new MDWSlider(
+- md, // only 1 device. This is actually _dockDevice
++ md, // only 1 device.
+ true, // Show Mute LED
+- false, // Show Record LED
++ false, // Show Record LED
+ false, // Small
+ Qt::Vertical, // Direction: only 1 device, so doesn't matter
+ this, // parent
+@@ -128,10 +128,10 @@
+ _layoutMDW->addWidget( mdw, 0, 1 );
+
+ // Add button to show main panel
+- _showPanelBox = new QPushButton( i18n("Mixer"), this );
+- _showPanelBox->setObjectName( QLatin1String("MixerPanel" ));
+- connect ( _showPanelBox, SIGNAL( clicked() ), SLOT( showPanelSlot() ) );
+- _layoutMDW->addWidget( _showPanelBox, 1, 0, 1, 3 );
++ QPushButton *pb = new QPushButton( i18n("Mixer"), this );
++ pb->setObjectName( QLatin1String("MixerPanel" ));
++ connect ( pb, SIGNAL( clicked() ), SLOT( showPanelSlot() ) );
++ _layoutMDW->addWidget( pb, 1, 0, 1, 3 );
+
+ return mdw;
+ }
+Index: kmix/core/mixdevice.h
+===================================================================
+--- kmix/core/mixdevice.h (revision 1226957)
++++ kmix/core/mixdevice.h (revision 1226958)
+@@ -157,15 +157,6 @@
+ _artificial = artificial;
+ }
+
+- bool isEthereal() const
+- {
+- return _ethereal;
+- }
+- void setEthereal(bool _ethereal)
+- {
+- this->_ethereal = _ethereal;
+- }
+-
+ void setControlProfile(ProfControl* control);
+ ProfControl* controlProfile();
+
+@@ -191,14 +182,12 @@
+ int _enumCurrentId;
+ QList<QString> _enumValues; // A MixDevice, that is an ENUM, has these _enumValues
+
+- //bool _doNotRestore;
+ // A virtual control. It will not be saved/restored and/or doesn't get shortcuts
+- // Actually we discriminate those "virtual" controls in artificial controls and ethereal controls:
++ // Actually we discriminate those "virtual" controls in artificial controls and dynamic controls:
+ // Type Shortcut Restore
+ // Artificial: yes no Virtual::GlobalMaster or Virtual::CaptureGroup_3 (controls that are constructed artificially from other controls)
+- // Ethereal : no no Controls that come and go, like Pulse Stream controls
++ // Dynamic : no no Controls that come and go, like Pulse Stream controls
+ bool _artificial;
+- bool _ethereal;
+ MixSet *_moveDestinationMixSet;
+ QString _iconName;
+
+Index: kmix/core/mixer.cpp
+===================================================================
+--- kmix/core/mixer.cpp (revision 1226957)
++++ kmix/core/mixer.cpp (revision 1226958)
+@@ -748,7 +748,7 @@
+ m_dynamic = dynamic;
+ }
+
+-bool Mixer::dynamic()
++bool Mixer::isDynamic()
+ {
+ return m_dynamic;
+ }
+Index: kmix/core/mixer.h
+===================================================================
+--- kmix/core/mixer.h (revision 1226957)
++++ kmix/core/mixer.h (revision 1226958)
+@@ -164,7 +164,7 @@
+
+ /// Says if we are dynamic (e.g. widgets can come and go)
+ virtual void setDynamic( bool dynamic = true );
+- virtual bool dynamic();
++ virtual bool isDynamic();
+
+ virtual bool moveStream( const QString id, const QString& destId );
+
+Index: kmix/core/mixdevice.cpp
+===================================================================
+--- kmix/core/mixdevice.cpp (revision 1226957)
++++ kmix/core/mixdevice.cpp (revision 1226958)
+@@ -23,6 +23,7 @@
+ #include <klocale.h>
+
+ #include "core/mixdevice.h"
++#include "core/mixer.h"
+ #include "gui/guiprofile.h"
+ #include "core/volume.h"
+
+@@ -96,14 +97,12 @@
+
+ MixDevice::MixDevice( Mixer* mixer, const QString& id, const QString& name, const QString& iconName, MixSet* moveDestinationMixSet )
+ {
+- // doNotRestore is superseded by the more generic concepts isEthereal(), isArtificial()
+ init(mixer, id, name, iconName, moveDestinationMixSet);
+ }
+
+ void MixDevice::init( Mixer* mixer, const QString& id, const QString& name, const QString& iconName, MixSet* moveDestinationMixSet )
+ {
+ _artificial = false;
+- _ethereal = false;
+ _mixer = mixer;
+ _id = id;
+ if( name.isEmpty() )
+@@ -117,9 +116,10 @@
+ _moveDestinationMixSet = moveDestinationMixSet;
+ if ( _id.contains(' ') ) {
+ // The key is used in the config file. It MUST NOT contain spaces
+- kError(67100) << "MixDevice::setId(\"" << id << "\") . Invalid key - it might not contain spaces" << endl;
++ kError(67100) << "MixDevice::setId(\"" << id << "\") . Invalid key - it must not contain spaces" << endl;
+ _id.replace(' ', '_');
+ }
++ kDebug(67100) << "MixDevice::init() _id=" << _id;
+ }
+
+ void MixDevice::addPlaybackVolume(Volume &playbackVol)
+@@ -216,11 +216,10 @@
+ */
+ void MixDevice::read( KConfig *config, const QString& grp )
+ {
+- if ( isEthereal() || isArtificial() ) {
++ if ( _mixer->isDynamic() || isArtificial() ) {
+ kDebug(67100) << "MixDevice::read(): This MixDevice does not permit volume restoration (i.e. because it is handled lower down in the audio stack). Ignoring.";
+ } else {
+- QString devgrp;
+- devgrp.sprintf( "%s.Dev%s", grp.toAscii().data(), _id.toAscii().data() );
++ QString devgrp = QString("%1.Dev%2").arg(grp).arg(_id);
+ KConfigGroup cg = config->group( devgrp );
+ //kDebug(67100) << "MixDevice::read() of group devgrp=" << devgrp;
+
+@@ -264,11 +263,10 @@
+ */
+ void MixDevice::write( KConfig *config, const QString& grp )
+ {
+- if (isEthereal() || isArtificial()) {
++ if (_mixer->isDynamic() || isArtificial()) {
+ kDebug(67100) << "MixDevice::write(): This MixDevice does not permit volume saving (i.e. because it is handled lower down in the audio stack). Ignoring.";
+ } else {
+- QString devgrp;
+- devgrp.sprintf( "%s.Dev%s", grp.toAscii().data(), _id.toAscii().data() );
++ QString devgrp = QString("%1.Dev%2").arg(grp).arg(_id);
+ KConfigGroup cg = config->group(devgrp);
+ // kDebug(67100) << "MixDevice::write() of group devgrp=" << devgrp;
+
+Index: kmix/apps/kmix.cpp
+===================================================================
+--- kmix/apps/kmix.cpp (revision 1226957)
++++ kmix/apps/kmix.cpp (revision 1226958)
+@@ -182,21 +182,20 @@
+
+ void KMixWindow::initActionsAfterInitMixer()
+ {
+- bool isPulseAudio = false;
+- // Add "launch_pavucontrol" to menu, if Pulseaudio backend is in use
++ // Only show the new tab widget if some of the mixers are not Dynamic.
++ // The GUI that then pops up could then make a new mixer from a dynamic one,
++ // if mixed dynamic and non-dynamic mixers were allowed, but this is generally not the case.
++ bool allDynamic = true;
+ foreach( Mixer* mixer, Mixer::mixers() )
+ {
+- if ( mixer->getDriverName() == "PulseAudio")
++ if ( !mixer->isDynamic() )
+ {
+- isPulseAudio = true;
+- KAction* action = actionCollection()->addAction( "launch_pavucontrol" );
+- action->setText( i18n( "Audio setup (&Pulseaudio)" ) );
+- connect(action, SIGNAL(triggered(bool) ), SLOT( slotPavucontrolExec() ));
++ allDynamic = false;
+ break;
+ }
+ }
+
+- if (! isPulseAudio )
++ if (! allDynamic )
+ {
+ QPixmap cornerNewPM = KIconLoader::global()->loadIcon( "tab-new", KIconLoader::Toolbar, KIconLoader::SizeSmall );
+ QPushButton* _cornerLabelNew = new QPushButton();
+@@ -339,8 +338,11 @@
+
+ // The following loop is necessary for the case that the user has hidden all views for a Mixer instance.
+ // Otherwise we would not save the Meta information (step -2- below for that mixer.
+- foreach ( Mixer* mixer, Mixer::mixers() )
+- mixerViews[mixer->id()]; // just insert a map entry
++ // We also do not save dynamic mixers (e.g. PulseAudio)
++ foreach ( Mixer* mixer, Mixer::mixers() ) {
++ if ( !mixer->isDynamic() )
++ mixerViews[mixer->id()]; // just insert a map entry
++ }
+
+ // -1- Save the views themselves
+ for ( int i=0; i<m_wsMixers->count() ; ++i ) {
+@@ -351,8 +353,10 @@
+ // Otherwise the user will be confused afer re-plugging the card (as the config was not saved).
+ mw->saveConfig( KGlobal::config().data() );
+ // add the view to the corresponding mixer list, so we can save a views-per-mixer list below
+- QStringList& qsl = mixerViews[mw->mixer()->id()];
+- qsl.append(mw->getGuiprof()->getId());
++ if ( !mw->mixer()->isDynamic() ) {
++ QStringList& qsl = mixerViews[mw->mixer()->id()];
++ qsl.append(mw->getGuiprof()->getId());
++ }
+ }
+ }
+
+@@ -539,22 +543,29 @@
+ continue; // OK, this mixer already has a profile => skip it
+ }
+ // No TAB YET => This should mean KMix is just started, or the user has just plugged in a card
+- bool profileListHasKey = pconfig.hasKey( mixer->id() ); // <<< SHOULD be before the following line
+- QStringList profileList = pconfig.readEntry( mixer->id(), QStringList() );
++ bool profileListHasKey = false;
++ QStringList profileList;
++ bool aProfileWasAddedSucesufully = false;
+
+- bool aProfileWasAddedSucesufully = false;
+- foreach ( QString profileId, profileList)
+- {
+- // This handles the profileList form the kmixrc
+- kDebug() << "Now searching for profile: " << profileId ;
+- GUIProfile* guiprof = GUIProfile::find(mixer, profileId, true, false); // ### Card specific profile ###
+- if ( guiprof != 0 ) {
+- addMixerWidget(mixer->id(), guiprof, -1);
+- aProfileWasAddedSucesufully = true;
++ if ( !mixer->isDynamic() ) {
++ // We do not support save profiles for dynamic mixers (i.e. PulseAudio)
++
++ profileListHasKey = pconfig.hasKey( mixer->id() ); // <<< SHOULD be before the following line
++ profileList = pconfig.readEntry( mixer->id(), QStringList() );
++
++ foreach ( QString profileId, profileList)
++ {
++ // This handles the profileList form the kmixrc
++ kDebug() << "Now searching for profile: " << profileId ;
++ GUIProfile* guiprof = GUIProfile::find(mixer, profileId, true, false); // ### Card specific profile ###
++ if ( guiprof != 0 ) {
++ addMixerWidget(mixer->id(), guiprof, -1);
++ aProfileWasAddedSucesufully = true;
++ }
++ else {
++ kError() << "Cannot load profile " << profileId << " . It was removed by the user, or the KMix config file is defective.";
++ }
+ }
+- else {
+- kError() << "Cannot load profile " << profileId << " . It was removed by the user, or the KMix config file is defective.";
+- }
+ }
+
+ // The we_need_a_fallback case is a bit tricky. Please ask the author (cesken) before even considering to change the code.
+@@ -568,11 +579,17 @@
+
+ // Lets try a bunch of fallback strategies:
+ GUIProfile* guiprof = 0;
++ if ( !mixer->isDynamic() ) {
++ // We know that GUIProfile::find() will return 0 if the mixer is dynamic, so don't bother checking.
++ kDebug() << "Attempting to find a card-specific GUI Profile for the mixer " << mixer->id();
+ guiprof = GUIProfile::find(mixer, QString("default"), false, false); // ### Card specific profile ###
+- if ( guiprof == 0 ) {
+- guiprof = GUIProfile::find(mixer, QString("default"), false, true); // ### Card unspecific profile ###
++ if ( guiprof == 0 ) {
++ kDebug() << "Not found. Attempting to find a generic GUI Profile for the mixer " << mixer->id();
++ guiprof = GUIProfile::find(mixer, QString("default"), false, true); // ### Card unspecific profile ###
++ }
+ }
+ if ( guiprof == 0) {
++ kDebug() << "Using fallback GUI Profile for the mixer " << mixer->id();
+ // This means there is neither card specific nor card unspecific profile
+ // This is the case for some backends (as they don't ship profiles).
+ guiprof = GUIProfile::fallbackProfile(mixer);
+@@ -683,8 +700,7 @@
+ m_wsMixers->removeTab(idx);
+ delete kmw;
+
+- bool isPulseAudio = kmw->mixer()->getDriverName() == "PulseAudio";
+- m_wsMixers->setTabsClosable(!isPulseAudio && m_wsMixers->count() > 1);
++ m_wsMixers->setTabsClosable(!kmw->mixer()->isDynamic() && m_wsMixers->count() > 1);
+
+ saveViewConfig();
+ }
+@@ -882,8 +898,7 @@
+ m_wsMixers->setCurrentWidget(kmw);
+ }
+
+- bool isPulseAudio = mixer->getDriverName() == "PulseAudio";
+- m_wsMixers->setTabsClosable(!isPulseAudio && m_wsMixers->count() > 1);
++ m_wsMixers->setTabsClosable(!mixer->isDynamic() && m_wsMixers->count() > 1);
+ m_dontSetDefaultCardOnStart = false;
+
+
+@@ -1071,12 +1086,6 @@
+ KMessageBox::information( 0, m_hwInfoString, i18n("Mixer Hardware Information") );
+ }
+
+-void KMixWindow::slotPavucontrolExec()
+-{
+- QStringList args("pavucontrol");
+- forkExec(args);
+-}
+-
+ void KMixWindow::slotKdeAudioSetupExec()
+ {
+ QStringList args;
+@@ -1133,6 +1142,12 @@
+ m_defaultCardOnStart = kmw->getGuiprof()->getId();
+ // As switching the tab does NOT mean switching the master card, we do not need to update dock icon here.
+ // It would lead to unnecesary flickering of the (complete) dock area.
++
++ // We only show the "Configure Channels..." menu item if the mixer is not dynamic
++ ViewBase* view = kmw->currentView();
++ QAction* action = actionCollection()->action( "toggle_channels_currentview" );
++ if (view && action)
++ action->setVisible( !view->getMixer()->isDynamic() );
+ }
+ }
+
+Index: kmix/apps/kmix.h
+===================================================================
+--- kmix/apps/kmix.h (revision 1226957)
++++ kmix/apps/kmix.h (revision 1226958)
+@@ -136,7 +136,6 @@
+ private slots:
+ void saveConfig();
+ void slotHWInfo();
+- void slotPavucontrolExec();
+ void slotKdeAudioSetupExec();
+ void slotConfigureCurrentView();
+ void slotSelectMaster();
+Index: kmix/kmixui.rc
+===================================================================
+--- kmix/kmixui.rc (revision 1226957)
++++ kmix/kmixui.rc (revision 1226958)
+@@ -17,7 +17,6 @@
+ <Action name="toggle_channels_currentview" append="save_merge"/>
+ <Action name="select_master" append="save_merge"/>
+ <Action name="launch_kdesoundsetup" append="save_merge"/>
+- <Action name="launch_pavucontrol" append="save_merge"/>
+ </Menu>
+ <Menu name="help" append="about_merge"><text>&amp;Help</text>
+ <Action name="hwinfo"/>
+Index: kmix/backends/mixer_backend.cpp
+===================================================================
+--- kmix/backends/mixer_backend.cpp (revision 1226957)
++++ kmix/backends/mixer_backend.cpp (revision 1226958)
+@@ -48,7 +48,7 @@
+ bool Mixer_Backend::openIfValid() {
+ bool valid = false;
+ int ret = open();
+- if ( ret == 0 && (m_mixDevices.count() > 0 || _mixer->dynamic())) {
++ if ( ret == 0 && (m_mixDevices.count() > 0 || _mixer->isDynamic())) {
+ valid = true;
+ // A better ID is now calculated in mixertoolbox.cpp, and set via setID(),
+ // but we want a somehow usable fallback just in case.
+@@ -139,7 +139,7 @@
+ return m_mixDevices.at(0); // Backend has NOT set a recommended master. Evil backend => lets help out.
+ } //first device (if exists)
+ else {
+- if ( !_mixer->dynamic()) {
++ if ( !_mixer->isDynamic()) {
+ // This should never ever happen, as KMix doe NOT accept soundcards without controls
+ kError(67100) << "Mixer_Backend::recommendedMaster(): returning invalid master. This is a bug in KMix. Please file a bug report stating how you produced this." << endl;
+ }
+Index: kmix/backends/mixer_pulse.cpp
+===================================================================
+--- kmix/backends/mixer_pulse.cpp (revision 1226957)
++++ kmix/backends/mixer_pulse.cpp (revision 1226958)
+@@ -36,6 +36,8 @@
+ #define KMIXPA_APP_CAPTURE 3
+ #define KMIXPA_WIDGET_MAX KMIXPA_APP_CAPTURE
+
++#define KMIXPA_EVENT_KEY "sink-input-by-media-role:event"
++
+ static unsigned int refcount = 0;
+ static pa_glib_mainloop *s_mainloop = NULL;
+ static pa_context *s_context = NULL;
+@@ -189,7 +191,7 @@
+
+ devinfo s;
+ s.index = s.device_index = i->index;
+- s.name = QString(i->name).replace(' ', '_');
++ s.name = QString::fromUtf8(i->name).replace(' ', '_');
+ s.description = QString::fromUtf8(i->description);
+ s.icon_name = QString::fromUtf8(pa_proplist_gets(i->proplist, PA_PROP_DEVICE_ICON_NAME));
+ s.volume = i->volume;
+@@ -242,7 +244,7 @@
+
+ devinfo s;
+ s.index = s.device_index = i->index;
+- s.name = QString(i->name).replace(' ', '_');
++ s.name = QString::fromUtf8(i->name).replace(' ', '_');
+ s.description = QString::fromUtf8(i->description);
+ s.icon_name = QString::fromUtf8(pa_proplist_gets(i->proplist, PA_PROP_DEVICE_ICON_NAME));
+ s.volume = i->volume;
+@@ -307,26 +309,28 @@
+
+ const char *t;
+ if ((t = pa_proplist_gets(i->proplist, "module-stream-restore.id"))) {
+- if (strcmp(t, "sink-input-by-media-role:event") == 0) {
++ if (strcmp(t, KMIXPA_EVENT_KEY) == 0) {
+ kWarning(67100) << "Ignoring sink-input due to it being designated as an event and thus handled by the Event slider";
+ return;
+ }
+ }
+
+- QString prefix = QString("%1: ").arg(i18n("Unknown Application"));
++ QString appname = i18n("Unknown Application");
+ if (clients.contains(i->client))
+- prefix = QString("%1: ").arg(clients[i->client]);
++ appname = clients[i->client];
+
++ QString prefix = QString("%1: ").arg(appname);
++
+ devinfo s;
+ s.index = i->index;
+ s.device_index = i->sink;
+ s.description = prefix + QString::fromUtf8(i->name);
+- s.name = QString("stream:") + i->index;
++ s.name = QString("stream:") + QString::number(i->index); //appname.replace(' ', '_').toLower();
+ s.icon_name = getIconNameFromProplist(i->proplist);
+ s.volume = i->volume;
+ s.channel_map = i->channel_map;
+ s.mute = !!i->mute;
+- s.stream_restore_rule = t;
++ s.stream_restore_rule = QString::fromUtf8(t);
+
+ translateMasksAndMaps(s);
+
+@@ -370,22 +374,24 @@
+ return;
+ }
+
+- QString prefix = QString("%1: ").arg(i18n("Unknown Application"));
++ QString appname = i18n("Unknown Application");
+ if (clients.contains(i->client))
+- prefix = QString("%1: ").arg(clients[i->client]);
++ appname = clients[i->client];
+
++ QString prefix = QString("%1: ").arg(appname);
++
+ devinfo s;
+ s.index = i->index;
+ s.device_index = i->source;
+ s.description = prefix + QString::fromUtf8(i->name);
+- s.name = QString("stream:") + i->index;
++ s.name = QString("stream:") + QString::number(i->index); //appname.replace(' ', '_').toLower();
+ s.icon_name = getIconNameFromProplist(i->proplist);
+ //s.volume = i->volume;
+ s.volume = captureDevices[i->source].volume;
+ s.channel_map = i->channel_map;
+ //s.mute = !!i->mute;
+ s.mute = captureDevices[i->source].mute;
+- s.stream_restore_rule = pa_proplist_gets(i->proplist, "module-stream-restore.id");
++ s.stream_restore_rule = QString::fromUtf8(pa_proplist_gets(i->proplist, "module-stream-restore.id"));
+
+ translateMasksAndMaps(s);
+
+@@ -407,7 +413,7 @@
+ }
+
+
+-static devinfo create_role_devinfo(const char* name) {
++static devinfo create_role_devinfo(QString name) {
+
+ Q_ASSERT(s_RestoreRules.contains(name));
+
+@@ -436,9 +442,10 @@
+
+ if (eol > 0) {
+ dec_outstanding(c);
++
+ // Special case: ensure that our media events exists.
+ // On first login by a new users, this wont be in our database so we should create it.
+- if (!outputRoles.contains(PA_INVALID_INDEX)) {
++ if (!s_RestoreRules.contains(KMIXPA_EVENT_KEY)) {
+ // Create a fake rule
+ restoreRule rule;
+ rule.channel_map.channels = 1;
+@@ -447,37 +454,56 @@
+ rule.volume.values[0] = PA_VOLUME_NORM;
+ rule.mute = false;
+ rule.device = "";
+- s_RestoreRules["sink-input-by-media-role:event"] = rule;
++ s_RestoreRules[KMIXPA_EVENT_KEY] = rule;
++ kDebug(67100) << "Initialising restore rule for new user: " << i18n("Event Sounds");
++ }
+
+- devinfo s = create_role_devinfo("sink-input-by-media-role:event");
+- outputRoles[s.index] = s;
+- kDebug(67100) << "Initialising restore rule for new user: " << s.description;
++ if (s_mixers.contains(KMIXPA_APP_PLAYBACK)) {
++ // If we have rules, it will be created below... but if no rules
++ // then we add it here.
++ if (!outputRoles.contains(PA_INVALID_INDEX)) {
++ devinfo s = create_role_devinfo(KMIXPA_EVENT_KEY);
++ outputRoles[s.index] = s;
+
+- if (s_mixers.contains(KMIXPA_APP_PLAYBACK))
+ s_mixers[KMIXPA_APP_PLAYBACK]->addWidget(s.index);
++ }
++
++ s_mixers[KMIXPA_APP_PLAYBACK]->triggerUpdate();
+ }
+
+- if (s_mixers.contains(KMIXPA_APP_PLAYBACK))
+- s_mixers[KMIXPA_APP_PLAYBACK]->triggerUpdate();
+ return;
+ }
+
+- kDebug(67100) << "Got some info about restore rule: " << i->name << i->device;
++
++ QString name = QString::fromUtf8(i->name);
++ kDebug(67100) << QString("Got some info about restore rule: '%1' (Device: %2)").arg(name).arg(i->device ? i->device : "None");
+ restoreRule rule;
+ rule.channel_map = i->channel_map;
+ rule.volume = i->volume;
+ rule.mute = !!i->mute;
+ rule.device = i->device;
+- s_RestoreRules[i->name] = rule;
+
+- // We only want to know about Sound Events for now...
+- if (strcmp(i->name, "sink-input-by-media-role:event") == 0) {
+- devinfo s = create_role_devinfo(i->name);
+- bool is_new = !outputRoles.contains(s.index);
+- outputRoles[s.index] = s;
++ if (rule.channel_map.channels < 1 && name == KMIXPA_EVENT_KEY) {
++ // Stream restore rules may not have valid volumes/channel maps (as these are optional)
++ // but we need a valid volume+channelmap for our events sounds so fix it up.
++ rule.channel_map.channels = 1;
++ rule.channel_map.map[0] = PA_CHANNEL_POSITION_MONO;
++ rule.volume.channels = 1;
++ rule.volume.values[0] = PA_VOLUME_NORM;
++ }
+
+- if (is_new && s_mixers.contains(KMIXPA_APP_PLAYBACK))
+- s_mixers[KMIXPA_APP_PLAYBACK]->addWidget(s.index);
++ s_RestoreRules[name] = rule;
++
++ if (s_mixers.contains(KMIXPA_APP_PLAYBACK)) {
++ // We only want to know about Sound Events for now...
++ if (name == KMIXPA_EVENT_KEY) {
++ devinfo s = create_role_devinfo(name);
++ bool is_new = !outputRoles.contains(s.index);
++ outputRoles[s.index] = s;
++
++ if (is_new)
++ s_mixers[KMIXPA_APP_PLAYBACK]->addWidget(s.index);
++ }
+ }
+ }
+
+@@ -788,7 +814,6 @@
+ Volume v(dev.chanMask, PA_VOLUME_NORM, PA_VOLUME_MUTED, true, false);
+ setVolumeFromPulse(v, dev);
+ MixDevice* md = new MixDevice( _mixer, dev.name, dev.description, dev.icon_name, ms);
+- md->setEthereal(true);
+ md->addPlaybackVolume(v);
+ md->setMuted(dev.mute);
+ m_mixDevices.append(md);
+@@ -1095,10 +1120,10 @@
+ {
+ restoreRule &rule = s_RestoreRules[iter->stream_restore_rule];
+ pa_ext_stream_restore_info info;
+- info.name = iter->stream_restore_rule.toAscii().constData();
++ info.name = iter->stream_restore_rule.toUtf8().constData();
+ info.channel_map = rule.channel_map;
+ info.volume = genVolumeForPulse(*iter, md->playbackVolume());
+- info.device = rule.device.isEmpty() ? NULL : rule.device.toAscii().constData();
++ info.device = rule.device.isEmpty() ? NULL : rule.device.toUtf8().constData();
+ info.mute = (md->isMuted() ? 1 : 0);
+
+ pa_operation* o;
+@@ -1153,13 +1178,13 @@
+
+ // Lookup the stream index.
+ uint32_t stream_index = PA_INVALID_INDEX;
+- const char* stream_restore_rule = NULL;
++ QString stream_restore_rule = "";
+ devmap::iterator iter;
+ devmap *map = get_widget_map(m_devnum);
+ for (iter = map->begin(); iter != map->end(); ++iter) {
+ if (iter->name == id) {
+ stream_index = iter->index;
+- stream_restore_rule = iter->stream_restore_rule.isEmpty() ? NULL : iter->stream_restore_rule.toAscii().constData();
++ stream_restore_rule = iter->stream_restore_rule;
+ break;
+ }
+ }
+@@ -1171,12 +1196,12 @@
+
+ if (destId.isEmpty()) {
+ // We want to remove any specific device in the stream restore rule.
+- if (!stream_restore_rule || !s_RestoreRules.contains(stream_restore_rule)) {
++ if (stream_restore_rule.isEmpty() || !s_RestoreRules.contains(stream_restore_rule)) {
+ kWarning(67100) << "Mixer_PULSE::moveStream(): Trying to set Automatic on a stream with no rule";
+ } else {
+ restoreRule &rule = s_RestoreRules[stream_restore_rule];
+ pa_ext_stream_restore_info info;
+- info.name = stream_restore_rule;
++ info.name = stream_restore_rule.toUtf8().constData();
+ info.channel_map = rule.channel_map;
+ info.volume = rule.volume;
+ info.device = NULL;
+@@ -1192,12 +1217,12 @@
+ } else {
+ pa_operation* o;
+ if (KMIXPA_APP_PLAYBACK == m_devnum) {
+- if (!(o = pa_context_move_sink_input_by_name(s_context, stream_index, destId.toAscii().constData(), NULL, NULL))) {
++ if (!(o = pa_context_move_sink_input_by_name(s_context, stream_index, destId.toUtf8().constData(), NULL, NULL))) {
+ kWarning(67100) << "pa_context_move_sink_input_by_name() failed";
+ return false;
+ }
+ } else {
+- if (!(o = pa_context_move_source_output_by_name(s_context, stream_index, destId.toAscii().constData(), NULL, NULL))) {
++ if (!(o = pa_context_move_source_output_by_name(s_context, stream_index, destId.toUtf8().constData(), NULL, NULL))) {
+ kWarning(67100) << "pa_context_move_source_output_by_name() failed";
+ return false;
+ }