2014-11-19 02:23:05 +00:00
/*
* KMix - - KDE ' s full featured mini mixer
*
* Copyright 1996 - 2014 The KMix authors . Maintainer : Christian Esken < esken @ kde . org >
*
* 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 "apps/kmix.h"
// include files for QT
# include <QCheckBox>
# include <QLabel>
# include <QDesktopWidget>
# include <QPushButton>
# include <qradiobutton.h>
# include <QCursor>
# include <QString>
2016-03-26 18:49:15 +00:00
# include <QMenuBar>
2014-11-19 02:23:05 +00:00
// include files for KDE
# include <KConfigSkeleton>
# include <kcombobox.h>
# include <kiconloader.h>
# include <kmessagebox.h>
# include <klocale.h>
# include <kconfig.h>
# include <kaction.h>
# include <kapplication.h>
# include <kstandardaction.h>
# include <kmenu.h>
# include <khelpmenu.h>
# include <kdebug.h>
# include <kxmlguifactory.h>
# include <kglobal.h>
# include <kactioncollection.h>
# include <ktoggleaction.h>
# include <KProcess>
# include <KTabWidget>
// KMix
# include "gui/guiprofile.h"
# include "core/ControlManager.h"
# include "core/GlobalConfig.h"
# include "core/MasterControl.h"
# include "core/MediaController.h"
# include "core/mixertoolbox.h"
# include "core/kmixdevicemanager.h"
# include "gui/kmixerwidget.h"
# include "gui/kmixprefdlg.h"
# include "gui/kmixdockwidget.h"
# include "gui/kmixtoolbox.h"
# include "core/version.h"
# include "gui/viewdockareapopup.h"
# include "gui/dialogaddview.h"
# include "gui/dialogselectmaster.h"
# include "dbus/dbusmixsetwrapper.h"
# include "gui/osdwidget.h"
/* KMixWindow
* Constructs a mixer window ( KMix main window )
*/
KMixWindow : : KMixWindow ( bool invisible ) :
2015-11-20 01:47:07 +02:00
KXmlGuiWindow ( 0 , Qt : : WindowContextHelpButtonHint ) ,
m_multiDriverMode ( false ) , // -<- I never-ever want the multi-drivermode to be activated by accident
2014-11-19 02:23:05 +00:00
m_dockWidget ( ) , m_dsm ( 0 ) , m_dontSetDefaultCardOnStart ( false )
{
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 ) ;
initActions ( ) ; // init actions first, so we can use them in the loadConfig() already
loadConfig ( ) ; // Load config before initMixer(), e.g. due to "MultiDriver" keyword
initActionsLate ( ) ; // init actions that require a loaded config
KGlobal : : locale ( ) - > insertCatalog ( QLatin1String ( " kmix-controls " ) ) ;
initWidgets ( ) ;
initPrefDlg ( ) ;
DBusMixSetWrapper : : initialize ( this , " /Mixers " ) ;
MixerToolBox : : instance ( ) - > initMixer ( m_multiDriverMode , m_backendFilter ,
m_hwInfoString ) ;
KMixDeviceManager * theKMixDeviceManager = KMixDeviceManager : : instance ( ) ;
initActionsAfterInitMixer ( ) ; // init actions that require initialized mixer backend(s).
recreateGUI ( false ) ;
if ( m_wsMixers - > count ( ) < 1 )
{
// Something is wrong. Perhaps a hardware or driver or backend change. Let KMix search harder
recreateGUI ( false , QString ( ) , true ) ;
}
if ( ! kapp - > isSessionRestored ( ) ) // done by the session manager otherwise
setInitialSize ( ) ;
fixConfigAfterRead ( ) ;
theKMixDeviceManager - > initHotplug ( ) ;
connect ( theKMixDeviceManager , SIGNAL ( plugged ( const char * , QString , QString & ) ) ,
SLOT ( plugged ( const char * , QString , QString & ) ) ) ;
connect ( theKMixDeviceManager , SIGNAL ( unplugged ( QString ) ) ,
SLOT ( unplugged ( QString ) ) ) ;
if ( m_startVisible & & ! invisible )
show ( ) ; // Started visible
connect ( kapp , SIGNAL ( aboutToQuit ( ) ) , SLOT ( saveConfig ( ) ) ) ;
ControlManager : : instance ( ) . addListener (
QString ( ) , // All mixers (as the Global master Mixer might change)
( ControlChangeType : : Type ) ( ControlChangeType : : ControlList | ControlChangeType : : MasterChanged ) ,
this ,
QString ( " KMixWindow " )
) ;
// Send an initial volume refresh (otherwise all volumes are 0 until the next change)
ControlManager : : instance ( ) . announce ( QString ( ) , ControlChangeType : : Volume , QString ( " Startup " ) ) ;
}
KMixWindow : : ~ KMixWindow ( )
{
ControlManager : : instance ( ) . removeListener ( this ) ;
delete m_dsm ;
delete osdWidget ;
// -1- Cleanup Memory: clearMixerWidgets
while ( m_wsMixers - > count ( ) ! = 0 )
{
QWidget * mw = m_wsMixers - > widget ( 0 ) ;
m_wsMixers - > removeTab ( 0 ) ;
delete mw ;
}
// -2- Mixer HW
MixerToolBox : : instance ( ) - > deinitMixer ( ) ;
// -3- Action collection (just to please Valgrind)
actionCollection ( ) - > clear ( ) ;
// GUIProfile cache should be cleared very very late, as GUIProfile instances are used in the Views, which
// means main window and potentially also in the tray popup (at least we might do so in the future).
// This place here could be to early, if we would start to GUIProfile outside KMixWIndow, e.g. in the tray popup.
// Until we do so, this is the best place to call clearCache(). Later, e.g. in main() would likely be problematic.
GUIProfile : : clearCache ( ) ;
}
void KMixWindow : : controlsChange ( int changeType )
{
ControlChangeType : : Type type = ControlChangeType : : fromInt ( changeType ) ;
switch ( type )
{
case ControlChangeType : : ControlList :
case ControlChangeType : : MasterChanged :
updateDocking ( ) ;
break ;
default :
ControlManager : : warnUnexpectedChangeType ( type , this ) ;
break ;
}
}
void
KMixWindow : : initActions ( )
{
// file menu
KStandardAction : : quit ( this , SLOT ( quit ( ) ) , actionCollection ( ) ) ;
// settings menu
_actionShowMenubar = KStandardAction : : showMenubar ( this , SLOT ( toggleMenuBar ( ) ) ,
actionCollection ( ) ) ;
//actionCollection()->addAction( a->objectName(), a );
KStandardAction : : preferences ( this , SLOT ( showSettings ( ) ) , actionCollection ( ) ) ;
KStandardAction : : keyBindings ( guiFactory ( ) , SLOT ( configureShortcuts ( ) ) ,
actionCollection ( ) ) ;
KAction * action = actionCollection ( ) - > addAction ( " launch_kdesoundsetup " ) ;
action - > setText ( i18n ( " Audio Setup " ) ) ;
connect ( action , SIGNAL ( triggered ( bool ) ) , SLOT ( slotKdeAudioSetupExec ( ) ) ) ;
action = actionCollection ( ) - > addAction ( " hwinfo " ) ;
action - > setText ( i18n ( " Hardware &Information " ) ) ;
connect ( action , SIGNAL ( triggered ( bool ) ) , SLOT ( slotHWInfo ( ) ) ) ;
action = actionCollection ( ) - > addAction ( " hide_kmixwindow " ) ;
action - > setText ( i18n ( " Hide Mixer Window " ) ) ;
connect ( action , SIGNAL ( triggered ( bool ) ) , SLOT ( hideOrClose ( ) ) ) ;
action - > setShortcut ( QKeySequence ( Qt : : Key_Escape ) ) ;
action = actionCollection ( ) - > addAction ( " toggle_channels_currentview " ) ;
action - > setText ( i18n ( " Configure &Channels... " ) ) ;
connect ( action , SIGNAL ( triggered ( bool ) ) , SLOT ( slotConfigureCurrentView ( ) ) ) ;
action = actionCollection ( ) - > addAction ( " select_master " ) ;
action - > setText ( i18n ( " Select Master Channel... " ) ) ;
connect ( action , SIGNAL ( triggered ( bool ) ) , SLOT ( slotSelectMaster ( ) ) ) ;
action = actionCollection ( ) - > addAction ( " save_1 " ) ;
action - > setShortcut ( KShortcut ( Qt : : CTRL + Qt : : SHIFT + Qt : : Key_1 ) ) ;
action - > setText ( i18n ( " Save volume profile 1 " ) ) ;
connect ( action , SIGNAL ( triggered ( bool ) ) , SLOT ( saveVolumes1 ( ) ) ) ;
action = actionCollection ( ) - > addAction ( " save_2 " ) ;
action - > setShortcut ( KShortcut ( Qt : : CTRL + Qt : : SHIFT + Qt : : Key_2 ) ) ;
action - > setText ( i18n ( " Save volume profile 2 " ) ) ;
connect ( action , SIGNAL ( triggered ( bool ) ) , SLOT ( saveVolumes2 ( ) ) ) ;
action = actionCollection ( ) - > addAction ( " save_3 " ) ;
action - > setShortcut ( KShortcut ( Qt : : CTRL + Qt : : SHIFT + Qt : : Key_3 ) ) ;
action - > setText ( i18n ( " Save volume profile 3 " ) ) ;
connect ( action , SIGNAL ( triggered ( bool ) ) , SLOT ( saveVolumes3 ( ) ) ) ;
action = actionCollection ( ) - > addAction ( " save_4 " ) ;
action - > setShortcut ( KShortcut ( Qt : : CTRL + Qt : : SHIFT + Qt : : Key_4 ) ) ;
action - > setText ( i18n ( " Save volume profile 4 " ) ) ;
connect ( action , SIGNAL ( triggered ( bool ) ) , SLOT ( saveVolumes4 ( ) ) ) ;
action = actionCollection ( ) - > addAction ( " load_1 " ) ;
action - > setShortcut ( KShortcut ( Qt : : CTRL + Qt : : Key_1 ) ) ;
action - > setText ( i18n ( " Load volume profile 1 " ) ) ;
connect ( action , SIGNAL ( triggered ( bool ) ) , SLOT ( loadVolumes1 ( ) ) ) ;
action = actionCollection ( ) - > addAction ( " load_2 " ) ;
action - > setShortcut ( KShortcut ( Qt : : CTRL + Qt : : Key_2 ) ) ;
action - > setText ( i18n ( " Load volume profile 2 " ) ) ;
connect ( action , SIGNAL ( triggered ( bool ) ) , SLOT ( loadVolumes2 ( ) ) ) ;
action = actionCollection ( ) - > addAction ( " load_3 " ) ;
action - > setShortcut ( KShortcut ( Qt : : CTRL + Qt : : Key_3 ) ) ;
action - > setText ( i18n ( " Load volume profile 3 " ) ) ;
connect ( action , SIGNAL ( triggered ( bool ) ) , SLOT ( loadVolumes3 ( ) ) ) ;
action = actionCollection ( ) - > addAction ( " load_4 " ) ;
action - > setShortcut ( KShortcut ( Qt : : CTRL + Qt : : Key_4 ) ) ;
action - > setText ( i18n ( " Load volume profile 4 " ) ) ;
connect ( action , SIGNAL ( triggered ( bool ) ) , SLOT ( loadVolumes4 ( ) ) ) ;
osdWidget = new OSDWidget ( ) ;
createGUI ( QLatin1String ( " kmixui.rc " ) ) ;
}
void
KMixWindow : : initActionsLate ( )
{
if ( m_autouseMultimediaKeys )
{
KAction * globalAction = actionCollection ( ) - > addAction ( " increase_volume " ) ;
globalAction - > setText ( i18n ( " Increase Volume " ) ) ;
globalAction - > setGlobalShortcut ( KShortcut ( Qt : : Key_VolumeUp ) ) ;
connect ( globalAction , SIGNAL ( triggered ( bool ) ) ,
SLOT ( slotIncreaseVolume ( ) ) ) ;
globalAction = actionCollection ( ) - > addAction ( " decrease_volume " ) ;
globalAction - > setText ( i18n ( " Decrease Volume " ) ) ;
globalAction - > setGlobalShortcut ( KShortcut ( Qt : : Key_VolumeDown ) ) ;
connect ( globalAction , SIGNAL ( triggered ( bool ) ) ,
SLOT ( slotDecreaseVolume ( ) ) ) ;
globalAction = actionCollection ( ) - > addAction ( " mute " ) ;
globalAction - > setText ( i18n ( " Mute " ) ) ;
globalAction - > setGlobalShortcut ( KShortcut ( Qt : : Key_VolumeMute ) ) ;
connect ( globalAction , SIGNAL ( triggered ( bool ) ) , SLOT ( slotMute ( ) ) ) ;
}
}
void
KMixWindow : : initActionsAfterInitMixer ( )
{
// Only show the new tab widget if Pulseaudio is not used. Hint: The Pulseaudio backend always
// runs with 4 fixed Tabs.
if ( ! Mixer : : pulseaudioPresent ( ) )
{
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 ( )
{
KMixPrefDlg * prefDlg = KMixPrefDlg : : createInstance ( this , GlobalConfig : : instance ( ) ) ;
connect ( prefDlg , SIGNAL ( kmixConfigHasChanged ( ) ) , SLOT ( applyPrefs ( ) ) ) ;
}
void
KMixWindow : : initWidgets ( )
{
m_wsMixers = new KTabWidget ( ) ;
m_wsMixers - > setDocumentMode ( true ) ;
setCentralWidget ( m_wsMixers ) ;
m_wsMixers - > setTabsClosable ( false ) ;
connect ( m_wsMixers , SIGNAL ( tabCloseRequested ( int ) ) ,
SLOT ( saveAndCloseView ( int ) ) ) ;
QPixmap cornerNewPM = KIconLoader : : global ( ) - > loadIcon ( " tab-new " ,
KIconLoader : : Toolbar , KIconLoader : : SizeSmall ) ;
connect ( m_wsMixers , SIGNAL ( currentChanged ( int ) ) , SLOT ( newMixerShown ( int ) ) ) ;
// show menubar if the actions says so (or if the action does not exist)
menuBar ( ) - > setVisible (
( _actionShowMenubar = = 0 ) | | _actionShowMenubar - > isChecked ( ) ) ;
}
void
KMixWindow : : setInitialSize ( )
{
KConfigGroup config ( KGlobal : : config ( ) , " Global " ) ;
// HACK: QTabWidget will bound its sizeHint to 200x200 unless scrollbuttons
// are disabled, so we disable them, get a decent sizehint and enable them
// back
m_wsMixers - > setUsesScrollButtons ( false ) ;
QSize defSize = sizeHint ( ) ;
m_wsMixers - > setUsesScrollButtons ( true ) ;
QSize size = config . readEntry ( " Size " , defSize ) ;
if ( ! size . isEmpty ( ) )
resize ( size ) ;
QPoint defPos = pos ( ) ;
QPoint pos = config . readEntry ( " Position " , defPos ) ;
move ( pos ) ;
}
void KMixWindow : : removeDock ( )
{
if ( m_dockWidget )
{
m_dockWidget - > deleteLater ( ) ;
m_dockWidget = 0 ;
}
}
/**
* Creates or deletes the KMixDockWidget , depending on whether there is a Mixer instance available .
*
* @ returns true , if the docking succeeded . Failure usually means that there
* was no suitable mixer control selected .
*/
bool KMixWindow : : updateDocking ( )
{
GlobalConfigData & gcd = GlobalConfig : : instance ( ) . data ;
if ( ! gcd . showDockWidget | | Mixer : : mixers ( ) . isEmpty ( ) )
{
removeDock ( ) ;
return false ;
}
if ( ! m_dockWidget )
{
m_dockWidget = new KMixDockWidget ( this ) ;
}
return true ;
}
void
KMixWindow : : saveConfig ( )
{
saveBaseConfig ( ) ;
saveViewConfig ( ) ;
saveVolumes ( ) ;
# ifdef __GNUC_
# warn We must Sync here, or we will lose configuration data. The reson for that is unknown.
# endif
// TODO cesken The reason for not writing might be that we have multiple cascaded KConfig objects. I must migrate to KSharedConfig !!!
KGlobal : : config ( ) - > sync ( ) ;
kDebug ( )
< < " Saved config ... sync finished " ;
}
void KMixWindow : : saveBaseConfig ( )
{
GlobalConfig : : instance ( ) . writeConfig ( ) ;
KConfigGroup config ( KGlobal : : config ( ) , " Global " ) ;
config . writeEntry ( " Size " , size ( ) ) ;
config . writeEntry ( " Position " , pos ( ) ) ;
// Cannot use isVisible() here, as in the "aboutToQuit()" case this widget is already hidden.
// (Please note that the problem was only there when quitting via Systray - esken).
// Using it again, as internal behaviour has changed with KDE4
config . writeEntry ( " Visible " , isVisible ( ) ) ;
config . writeEntry ( " Menubar " , _actionShowMenubar - > isChecked ( ) ) ;
config . writeEntry ( " Soundmenu.Mixers " , GlobalConfig : : instance ( ) . getMixersForSoundmenu ( ) . toList ( ) ) ;
config . writeEntry ( " DefaultCardOnStart " , m_defaultCardOnStart ) ;
config . writeEntry ( " ConfigVersion " , KMIX_CONFIG_VERSION ) ;
config . writeEntry ( " AutoUseMultimediaKeys " , m_autouseMultimediaKeys ) ;
MasterControl & master = Mixer : : getGlobalMasterPreferred ( ) ;
if ( master . isValid ( ) )
{
config . writeEntry ( " MasterMixer " , master . getCard ( ) ) ;
config . writeEntry ( " MasterMixerDevice " , master . getControl ( ) ) ;
}
QString mixerIgnoreExpression = MixerToolBox : : instance ( ) - > mixerIgnoreExpression ( ) ;
config . writeEntry ( " MixerIgnoreExpression " , mixerIgnoreExpression ) ;
kDebug ( )
< < " Base configuration saved " ;
}
void KMixWindow : : saveViewConfig ( )
{
QMap < QString , QStringList > mixerViews ;
// 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.
// 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 )
{
QWidget * w = m_wsMixers - > widget ( i ) ;
if ( w - > inherits ( " KMixerWidget " ) )
{
KMixerWidget * mw = ( KMixerWidget * ) w ;
// Here also Views are saved. even for Mixers that are closed. This is necessary when unplugging cards.
// 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
if ( ! mw - > mixer ( ) - > isDynamic ( ) )
{
QStringList & qsl = mixerViews [ mw - > mixer ( ) - > id ( ) ] ;
qsl . append ( mw - > getGuiprof ( ) - > getId ( ) ) ;
}
}
}
// -2- Save Meta-Information (which views, and in which order). views-per-mixer list
KConfigGroup pconfig ( KGlobal : : config ( ) , " Profiles " ) ;
QMap < QString , QStringList > : : const_iterator itEnd = mixerViews . constEnd ( ) ;
for ( QMap < QString , QStringList > : : const_iterator it = mixerViews . constBegin ( ) ; it ! = itEnd ; + + it )
{
const QString & mixerProfileKey = it . key ( ) ; // this is actually some mixer->id()
const QStringList & qslProfiles = it . value ( ) ;
pconfig . writeEntry ( mixerProfileKey , qslProfiles ) ;
kDebug ( )
< < " Save Profile List for " < < mixerProfileKey < < " , number of views is " < < qslProfiles . count ( ) ;
}
kDebug ( )
< < " View configuration saved " ;
}
/**
* Stores the volumes of all mixers Can be restored via loadVolumes ( ) or
* the kmixctrl application .
*/
void
KMixWindow : : saveVolumes ( )
{
saveVolumes ( QString ( ) ) ;
}
void
KMixWindow : : saveVolumes ( QString postfix )
{
const QString & kmixctrlRcFilename = getKmixctrlRcFilename ( postfix ) ;
KConfig * cfg = new KConfig ( kmixctrlRcFilename ) ;
for ( int i = 0 ; i < Mixer : : mixers ( ) . count ( ) ; + + i )
{
Mixer * mixer = ( Mixer : : mixers ( ) ) [ i ] ;
if ( mixer - > isOpen ( ) )
{ // protect from unplugged devices (better do *not* save them)
mixer - > volumeSave ( cfg ) ;
}
}
cfg - > sync ( ) ;
delete cfg ;
kDebug ( )
< < " Volume configuration saved " ;
}
QString
KMixWindow : : getKmixctrlRcFilename ( QString postfix )
{
QString kmixctrlRcFilename ( " kmixctrlrc " ) ;
if ( ! postfix . isEmpty ( ) )
{
kmixctrlRcFilename . append ( " . " ) . append ( postfix ) ;
}
return kmixctrlRcFilename ;
}
void
KMixWindow : : loadConfig ( )
{
loadBaseConfig ( ) ;
//loadViewConfig(); // mw->loadConfig() explicitly called always after creating mw.
//loadVolumes(); // not in use
// create an initial snapshot, so we have a reference of the state before changes through the preferences dialog
configDataSnapshot = GlobalConfig : : instance ( ) . data ;
}
void
KMixWindow : : loadBaseConfig ( )
{
KConfigGroup config ( KGlobal : : config ( ) , " Global " ) ;
// GlobalConfig& gcfg = GlobalConfig::instance();
GlobalConfigData & gcd = GlobalConfig : : instance ( ) . data ;
QList < QString > preferredMixersInSoundMenu ;
preferredMixersInSoundMenu = config . readEntry ( " Soundmenu.Mixers " , preferredMixersInSoundMenu ) ;
GlobalConfig : : instance ( ) . setMixersForSoundmenu ( preferredMixersInSoundMenu . toSet ( ) ) ;
setBeepOnVolumeChange ( gcd . volumeFeedback ) ;
m_startVisible = config . readEntry ( " Visible " , false ) ;
m_multiDriverMode = config . readEntry ( " MultiDriver " , false ) ;
m_defaultCardOnStart = config . readEntry ( " DefaultCardOnStart " , " " ) ;
m_configVersion = config . readEntry ( " ConfigVersion " , 0 ) ;
// WARNING Don't overwrite m_configVersion with the "correct" value, before having it
// evaluated. Better only write that in saveBaseConfig()
m_autouseMultimediaKeys = config . readEntry ( " AutoUseMultimediaKeys " , true ) ;
QString mixerMasterCard = config . readEntry ( " MasterMixer " , " " ) ;
QString masterDev = config . readEntry ( " MasterMixerDevice " , " " ) ;
Mixer : : setGlobalMaster ( mixerMasterCard , masterDev , true ) ;
QString mixerIgnoreExpression = config . readEntry ( " MixerIgnoreExpression " ,
" Modem " ) ;
MixerToolBox : : instance ( ) - > setMixerIgnoreExpression ( mixerIgnoreExpression ) ;
// --- Advanced options, without GUI: START -------------------------------------
QString volumePercentageStepString = config . readEntry ( " VolumePercentageStep " ) ;
if ( ! volumePercentageStepString . isNull ( ) )
{
float volumePercentageStep = volumePercentageStepString . toFloat ( ) ;
if ( volumePercentageStep > 0 & & volumePercentageStep < = 100 )
Volume : : VOLUME_STEP_DIVISOR = ( 100 / volumePercentageStep ) ;
}
// --- Advanced options, without GUI: END -------------------------------------
m_backendFilter = config . readEntry < > ( " Backends " , QList < QString > ( ) ) ;
kDebug ( ) < < " Backends: " < < m_backendFilter ;
// show/hide menu bar
bool showMenubar = config . readEntry ( " Menubar " , true ) ;
if ( _actionShowMenubar )
_actionShowMenubar - > setChecked ( showMenubar ) ;
}
/**
* Loads the volumes of all mixers from kmixctrlrc .
* In other words :
* Restores the default voumes as stored via saveVolumes ( ) or the
* execution of " kmixctrl --save "
*/
void
KMixWindow : : loadVolumes ( )
{
loadVolumes ( QString ( ) ) ;
}
void
KMixWindow : : loadVolumes ( QString postfix )
{
kDebug ( )
< < " About to load config (Volume) " ;
const QString & kmixctrlRcFilename = getKmixctrlRcFilename ( postfix ) ;
KConfig * cfg = new KConfig ( kmixctrlRcFilename ) ;
for ( int i = 0 ; i < Mixer : : mixers ( ) . count ( ) ; + + i )
{
Mixer * mixer = ( Mixer : : mixers ( ) ) [ i ] ;
mixer - > volumeLoad ( cfg ) ;
}
delete cfg ;
}
void
KMixWindow : : recreateGUIwithSavingView ( )
{
// saveViewConfig();
recreateGUI ( true ) ;
}
void
KMixWindow : : recreateGUI ( bool saveConfig )
{
recreateGUI ( saveConfig , QString ( ) , false ) ;
}
/**
* Create or recreate the Mixer GUI elements
*
* @ param saveConfig Whether to save all View configurations before recreating
* @ param forceNewTab To enforce opening a new tab , even when the profileList in the kmixrc is empty .
* It should only be set to " true " in case of a Hotplug ( because then the user definitely expects a new Tab to show ) .
*/
void
KMixWindow : : recreateGUI ( bool saveConfig , const QString & mixerId ,
bool forceNewTab )
{
// -1- Remember which of the tabs is currently selected for restoration for re-insertion
int oldTabPosition = m_wsMixers - > currentIndex ( ) ;
if ( saveConfig )
saveViewConfig ( ) ; // save the state before recreating
// -2- RECREATE THE ALREADY EXISTING TABS **********************************
QMap < Mixer * , bool > mixerHasProfile ;
// -2a- Build a list of all active profiles in the main window (that means: from all tabs)
QList < GUIProfile * > activeGuiProfiles ;
for ( int i = 0 ; i < m_wsMixers - > count ( ) ; + + i )
{
KMixerWidget * kmw = dynamic_cast < KMixerWidget * > ( m_wsMixers - > widget ( i ) ) ;
if ( kmw )
{
activeGuiProfiles . append ( kmw - > getGuiprof ( ) ) ;
}
}
// TODO The following loop is a bit buggy, as it iterates over all cached Profiles. But that is wrong for Tabs that got closed.
// I need to loop over something else, e.g. a profile list built from the currently open Tabs.
// Or (if it that is easier) I might discard the Profile from the cache on "close-tab" (but that must also include unplug actions).
foreach ( GUIProfile * guiprof , activeGuiProfiles ) {
KMixerWidget * kmw = findKMWforTab ( guiprof - > getId ( ) ) ;
Mixer * mixer = Mixer : : findMixer ( guiprof - > getMixerId ( ) ) ;
if ( mixer = = 0 )
{
kError ( ) < < " MixerToolBox::find() hasn't found the Mixer for the profile " < < guiprof - > getId ( ) ;
continue ;
}
mixerHasProfile [ mixer ] = true ;
if ( kmw = = 0 )
{
// does not yet exist => create
addMixerWidget ( mixer - > id ( ) , guiprof - > getId ( ) , - 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 ;
addMixerWidget ( mixer - > id ( ) , guiprof - > getId ( ) , indexOfTab ) ;
}
} // Loop over all GUIProfile's
// -3- ADD TABS FOR Mixer instances that have no tab yet **********************************
KConfigGroup pconfig ( KGlobal : : config ( ) , " Profiles " ) ;
foreach ( Mixer * mixer , Mixer : : mixers ( ) ) {
if ( mixerHasProfile . contains ( mixer ) )
{
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 = false ;
QStringList profileList ;
bool aProfileWasAddedSucesufully = false ;
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 - > getId ( ) , - 1 ) ;
aProfileWasAddedSucesufully = true ;
}
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.
bool we_need_a_fallback = ! aProfileWasAddedSucesufully ; // we *possibly* want a fallback, if we couldn't add one
bool thisMixerShouldBeForced = forceNewTab & & ( mixerId . isEmpty ( ) | | ( mixer - > id ( ) = = mixerId ) ) ;
we_need_a_fallback = we_need_a_fallback & & ( thisMixerShouldBeForced | | ! profileListHasKey ) ; // Additional requirement: "forced-tab-for-this-mixer" OR "no key stored in kmixrc yet"
if ( we_need_a_fallback )
{
// The profileList was empty or nothing could be loaded
// (Hint: This means the user cannot hide a device completely
// 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 )
{
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 ) ;
}
if ( guiprof ! = 0 )
{
guiprof - > setDirty ( ) ; // All fallback => dirty
addMixerWidget ( mixer - > id ( ) , guiprof - > getId ( ) , - 1 ) ;
}
else
{
kError ( ) < < " Cannot use ANY profile (including Fallback) for mixer " < < mixer - > id ( ) < < " . This is impossible, and thus this mixer can NOT be used. " ;
}
}
}
mixerHasProfile . clear ( ) ;
// -4- FINALIZE **********************************
if ( m_wsMixers - > count ( ) > 0 )
{
if ( oldTabPosition > = 0 )
{
m_wsMixers - > setCurrentIndex ( oldTabPosition ) ;
}
bool dockingSucceded = updateDocking ( ) ;
if ( ! dockingSucceded & & ! Mixer : : mixers ( ) . empty ( ) )
{
show ( ) ; // avoid invisible and unaccessible main window
}
}
else
{
// No soundcard found. Do not complain, but sit in the background, and wait for newly plugged soundcards.
updateDocking ( ) ; // -<- removes the DockIcon
hide ( ) ;
}
}
KMixerWidget *
KMixWindow : : findKMWforTab ( const QString & kmwId )
{
for ( int i = 0 ; i < m_wsMixers - > count ( ) ; + + i )
{
KMixerWidget * kmw = ( KMixerWidget * ) m_wsMixers - > widget ( i ) ;
if ( kmw - > getGuiprof ( ) - > getId ( ) = = kmwId )
{
return kmw ;
}
}
return 0 ;
}
void
KMixWindow : : newView ( )
{
if ( Mixer : : mixers ( ) . empty ( ) )
{
kError ( ) < < " Trying to create a View, but no Mixer exists " ;
return ; // should never happen
}
Mixer * mixer = Mixer : : mixers ( ) [ 0 ] ;
QPointer < DialogAddView > dav = new DialogAddView ( this , mixer ) ;
int ret = dav - > exec ( ) ;
if ( QDialog : : Accepted = = ret )
{
QString profileName = dav - > getresultViewName ( ) ;
QString mixerId = dav - > getresultMixerId ( ) ;
mixer = Mixer : : findMixer ( mixerId ) ;
kDebug ( )
< < " >>> mixer = " < < mixerId < < " -> " < < mixer ;
GUIProfile * guiprof = GUIProfile : : find ( mixer , profileName , false , false ) ;
if ( guiprof = = 0 )
{
guiprof = GUIProfile : : find ( mixer , profileName , false , true ) ;
}
if ( guiprof = = 0 )
{
static const QString msg (
i18n ( " Cannot add view - GUIProfile is invalid. " ) ) ;
errorPopup ( msg ) ;
}
else
{
bool ret = addMixerWidget ( mixer - > id ( ) , guiprof - > getId ( ) , - 1 ) ;
if ( ret = = false )
{
errorPopup ( i18n ( " View already exists. Cannot add View. " ) ) ;
}
}
delete dav ;
}
//kDebug() << "Exit";
}
/**
* Save the view and close it
*
* @ arg idx The index in the TabWidget
*/
void
KMixWindow : : saveAndCloseView ( int idx )
{
kDebug ( )
< < " Enter " ;
QWidget * w = m_wsMixers - > widget ( idx ) ;
KMixerWidget * kmw = : : qobject_cast < KMixerWidget * > ( w ) ;
if ( kmw )
{
kmw - > saveConfig ( KGlobal : : config ( ) . data ( ) ) ; // -<- This alone is not enough, as I need to save the META information as well. Thus use saveViewConfig() below
m_wsMixers - > removeTab ( idx ) ;
updateTabsClosable ( ) ;
saveViewConfig ( ) ;
delete kmw ;
}
kDebug ( )
< < " Exit " ;
}
void
KMixWindow : : fixConfigAfterRead ( )
{
KConfigGroup grp ( KGlobal : : config ( ) , " Global " ) ;
unsigned int configVersion = grp . readEntry ( " ConfigVersion " , 0 ) ;
if ( configVersion < 3 )
{
// Fix the "double Base" bug, by deleting all groups starting with "View.Base.Base.".
// The group has been copied over by KMixToolBox::loadView() for all soundcards, so
// we should be fine now
QStringList cfgGroups = KGlobal : : config ( ) - > groupList ( ) ;
QStringListIterator it ( cfgGroups ) ;
while ( it . hasNext ( ) )
{
QString groupName = it . next ( ) ;
if ( groupName . indexOf ( " View.Base.Base " ) = = 0 )
{
kDebug ( 67100 )
< < " Fixing group " < < groupName ;
KConfigGroup buggyDevgrpCG = KGlobal : : config ( ) - > group ( groupName ) ;
buggyDevgrpCG . deleteGroup ( ) ;
} // remove buggy group
} // for all groups
} // if config version < 3
}
void
KMixWindow : : plugged ( const char * driverName , const QString & udi , QString & dev )
{
kDebug ( )
< < " Plugged: dev= " < < dev < < " ( " < < driverName < < " ) udi= " < < udi < < " \n " ;
QString driverNameString ;
driverNameString = driverName ;
int devNum = dev . toInt ( ) ;
Mixer * mixer = new Mixer ( driverNameString , devNum ) ;
if ( mixer ! = 0 )
{
kDebug ( )
< < " Plugged: dev= " < < dev < < " \n " ;
MixerToolBox : : instance ( ) - > possiblyAddMixer ( mixer ) ;
recreateGUI ( true , mixer - > id ( ) , true ) ;
}
}
void
KMixWindow : : unplugged ( const QString & udi )
{
kDebug ( )
< < " Unplugged: udi= " < < udi < < " \n " ;
for ( int i = 0 ; i < Mixer : : mixers ( ) . count ( ) ; + + i )
{
Mixer * mixer = ( Mixer : : mixers ( ) ) [ i ] ;
// kDebug(67100) << "Try Match with:" << mixer->udi() << "\n";
if ( mixer - > udi ( ) = = udi )
{
kDebug ( ) < < " Unplugged Match: Removing udi= " < < udi < < " \n " ;
//KMixToolBox::notification("MasterFallback", "aaa");
bool globalMasterMixerDestroyed = ( mixer = = Mixer : : getGlobalMasterMixer ( ) ) ;
// Part 1) Remove Tab
for ( int i = 0 ; i < m_wsMixers - > count ( ) ; + + i )
{
QWidget * w = m_wsMixers - > widget ( i ) ;
KMixerWidget * kmw = : : qobject_cast < KMixerWidget * > ( w ) ;
if ( kmw & & kmw - > mixer ( ) = = mixer )
{
saveAndCloseView ( i ) ;
i = - 1 ; // Restart loop from scratch (indices are most likely invalidated at removeTab() )
}
}
MixerToolBox : : instance ( ) - > removeMixer ( mixer ) ;
// Check whether the Global Master disappeared, and select a new one if necessary
2020-02-19 16:13:43 +00:00
std : : shared_ptr < MixDevice > md = Mixer : : getGlobalMasterMD ( ) ;
2014-11-19 02:23:05 +00:00
if ( globalMasterMixerDestroyed | | md . get ( ) = = 0 )
{
// We don't know what the global master should be now.
// So lets play stupid, and just select the recommended master of the first device
if ( Mixer : : mixers ( ) . count ( ) > 0 )
{
2020-02-19 16:13:43 +00:00
std : : shared_ptr < MixDevice > master =
2014-11-19 02:23:05 +00:00
( ( Mixer : : mixers ( ) ) [ 0 ] ) - > getLocalMasterMD ( ) ;
if ( master . get ( ) ! = 0 )
{
QString localMaster = master - > id ( ) ;
Mixer : : setGlobalMaster ( ( ( Mixer : : mixers ( ) ) [ 0 ] ) - > id ( ) , localMaster , false ) ;
QString text ;
text =
i18n (
" The soundcard containing the master device was unplugged. Changing to control %1 on card %2. " ,
master - > readableName ( ) ,
( ( Mixer : : mixers ( ) ) [ 0 ] ) - > readableName ( ) ) ;
KMixToolBox : : notification ( " MasterFallback " , text ) ;
}
}
}
if ( Mixer : : mixers ( ) . count ( ) = = 0 )
{
QString text ;
text = i18n ( " The last soundcard was unplugged. " ) ;
KMixToolBox : : notification ( " MasterFallback " , text ) ;
}
recreateGUI ( true ) ;
break ;
}
}
}
/**
* Create a widget with an error message
* This widget shows an error message like " no mixers detected.
void KMixWindow : : setErrorMixerWidget ( )
{
QString s = i18n ( " Please plug in your soundcard.No soundcard found. Probably you have not set it up or are missing soundcard drivers. Please check your operating system manual for installing your soundcard. " ) ; // !! better text
m_errorLabel = new QLabel ( s , this ) ;
m_errorLabel - > setAlignment ( Qt : : AlignCenter ) ;
m_errorLabel - > setWordWrap ( true ) ;
m_errorLabel - > setSizePolicy ( QSizePolicy : : Minimum , QSizePolicy : : Minimum ) ;
m_wsMixers - > addTab ( m_errorLabel , i18n ( " No soundcard found " ) ) ;
}
*/
/**
*
*/
bool
KMixWindow : : profileExists ( QString guiProfileId )
{
for ( int i = 0 ; i < m_wsMixers - > count ( ) ; + + i )
{
KMixerWidget * kmw = dynamic_cast < KMixerWidget * > ( m_wsMixers - > widget ( i ) ) ;
if ( kmw & & kmw - > getGuiprof ( ) - > getId ( ) = = guiProfileId )
return true ;
}
return false ;
}
bool
KMixWindow : : addMixerWidget ( const QString & mixer_ID , QString guiprofId , int insertPosition )
{
kDebug ( ) < < " Add " < < guiprofId ;
GUIProfile * guiprof = GUIProfile : : find ( guiprofId ) ;
if ( guiprof ! = 0 & & profileExists ( guiprof - > getId ( ) ) ) // TODO Bad place. Should be checked in the add-tab-dialog
return false ; // already present => don't add again
Mixer * mixer = Mixer : : findMixer ( mixer_ID ) ;
if ( mixer = = 0 )
return false ; // no such Mixer
// kDebug(67100) << "KMixWindow::addMixerWidget() " << mixer_ID << " is being added";
ViewBase : : ViewFlags vflags = ViewBase : : HasMenuBar ;
if ( ( _actionShowMenubar = = 0 ) | | _actionShowMenubar - > isChecked ( ) )
vflags | = ViewBase : : MenuBarVisible ;
if ( GlobalConfig : : instance ( ) . data . getToplevelOrientation ( ) = = Qt : : Vertical )
vflags | = ViewBase : : Horizontal ;
else
vflags | = ViewBase : : Vertical ;
KMixerWidget * kmw = new KMixerWidget ( mixer , this , vflags , guiprofId ,
actionCollection ( ) ) ;
/* A newly added mixer will automatically added at the top
* and thus the window title is also set appropriately */
/*
* Skip the name from the profile for now . I would at least have to do the ' & ' quoting for the tab label . But I am
* also not 100 % sure whether the current name from the profile is any good - it does ( likely ) not even contain the
* card ID . This means you cannot distinguish between cards with an identical name .
*/
// QString tabLabel = guiprof->getName();
// if (tabLabel.isEmpty())
// QString tabLabel = kmw->mixer()->readableName(true);
QString tabLabel = kmw - > mixer ( ) - > readableName ( true ) ;
m_dontSetDefaultCardOnStart = true ; // inhibit implicit setting of m_defaultCardOnStart
if ( insertPosition = = - 1 )
m_wsMixers - > addTab ( kmw , tabLabel ) ;
else
m_wsMixers - > insertTab ( insertPosition , kmw , tabLabel ) ;
if ( kmw - > getGuiprof ( ) - > getId ( ) = = m_defaultCardOnStart )
{
m_wsMixers - > setCurrentWidget ( kmw ) ;
}
updateTabsClosable ( ) ;
m_dontSetDefaultCardOnStart = false ;
kmw - > loadConfig ( KGlobal : : config ( ) . data ( ) ) ;
// Now force to read for new tabs, especially after hotplug. Note: Doing it here is bad design and possibly
// obsolete, as the backend should take care of upating itself.
kmw - > mixer ( ) - > readSetFromHWforceUpdate ( ) ;
return true ;
}
void KMixWindow : : updateTabsClosable ( )
{
// Pulseaudio runs with 4 fixed tabs - don't allow to close them.
// Also do not allow to close the last view
m_wsMixers - > setTabsClosable ( ! Mixer : : pulseaudioPresent ( ) & & m_wsMixers - > count ( ) > 1 ) ;
}
bool KMixWindow : : queryClose ( )
{
GlobalConfigData & gcd = GlobalConfig : : instance ( ) . data ;
if ( gcd . showDockWidget & & ! kapp - > sessionSaving ( ) )
{
// Hide (don't close and destroy), if docking is enabled. Except when session saving (shutdown) is in process.
hide ( ) ;
return false ;
}
else
{
// Accept the close, if:
// The user has disabled docking
// or SessionSaving() is running
// kDebug(67100) << "close";
return true ;
}
}
void KMixWindow : : hideOrClose ( )
{
GlobalConfigData & gcd = GlobalConfig : : instance ( ) . data ;
if ( gcd . showDockWidget & & m_dockWidget ! = 0 )
{
// we can hide if there is a dock widget
hide ( ) ;
}
else
{
// if there is no dock widget, we will quit
quit ( ) ;
}
}
// internal helper to prevent code duplication in slotIncreaseVolume and slotDecreaseVolume
void
KMixWindow : : increaseOrDecreaseVolume ( bool increase )
{
Mixer * mixer = Mixer : : getGlobalMasterMixer ( ) ; // only needed for the awkward construct below
if ( mixer = = 0 )
return ; // e.g. when no soundcard is available
2020-02-19 16:13:43 +00:00
std : : shared_ptr < MixDevice > md = Mixer : : getGlobalMasterMD ( ) ;
2014-11-19 02:23:05 +00:00
if ( md . get ( ) = = 0 )
return ; // shouldn't happen, but lets play safe
Volume : : VolumeTypeFlag volumeType = md - > playbackVolume ( ) . hasVolume ( ) ? Volume : : Playback : Volume : : Capture ;
md - > increaseOrDecreaseVolume ( ! increase , volumeType ) ;
md - > mixer ( ) - > commitVolumeChange ( md ) ;
showVolumeDisplay ( ) ;
}
void
KMixWindow : : slotIncreaseVolume ( )
{
increaseOrDecreaseVolume ( true ) ;
}
void
KMixWindow : : slotDecreaseVolume ( )
{
increaseOrDecreaseVolume ( false ) ;
}
void
KMixWindow : : showVolumeDisplay ( )
{
Mixer * mixer = Mixer : : getGlobalMasterMixer ( ) ;
if ( mixer = = 0 )
return ; // e.g. when no soundcard is available
2020-02-19 16:13:43 +00:00
std : : shared_ptr < MixDevice > md = Mixer : : getGlobalMasterMD ( ) ;
2014-11-19 02:23:05 +00:00
if ( md . get ( ) = = 0 )
return ; // shouldn't happen, but lets play safe
// Current volume
// Setting not required any more, as the OSD updates the volume level itself
// Volume& vol = md->playbackVolume();
// osdWidget->setCurrentVolume(vol.getAvgVolumePercent(Volume::MALL),
// md->isMuted());
if ( GlobalConfig : : instance ( ) . data . showOSD )
{
osdWidget - > show ( ) ;
osdWidget - > activateOSD ( ) ; //Enable the hide timer
}
//Center the OSD
QRect rect = KApplication : : kApplication ( ) - > desktop ( ) - > screenGeometry (
QCursor : : pos ( ) ) ;
QSize size = osdWidget - > sizeHint ( ) ;
int posX = rect . x ( ) + ( rect . width ( ) - size . width ( ) ) / 2 ;
int posY = rect . y ( ) + 4 * rect . height ( ) / 5 ;
osdWidget - > setGeometry ( posX , posY , size . width ( ) , size . height ( ) ) ;
}
/**
* Mutes the global master . ( SLOT )
*/
void KMixWindow : : slotMute ( )
{
Mixer * mixer = Mixer : : getGlobalMasterMixer ( ) ;
if ( mixer = = 0 )
return ; // e.g. when no soundcard is available
2020-02-19 16:13:43 +00:00
std : : shared_ptr < MixDevice > md = Mixer : : getGlobalMasterMD ( ) ;
2014-11-19 02:23:05 +00:00
if ( md . get ( ) = = 0 )
return ; // shouldn't happen, but lets play safe
md - > toggleMute ( ) ;
mixer - > commitVolumeChange ( md ) ;
showVolumeDisplay ( ) ;
}
void
KMixWindow : : quit ( )
{
// kDebug(67100) << "quit";
kapp - > quit ( ) ;
}
/**
* Shows the configuration dialog , with the " general " tab opened .
*/
void KMixWindow : : showSettings ( )
{
KMixPrefDlg : : getInstance ( ) - > switchToPage ( KMixPrefDlg : : PrefGeneral ) ;
KMixPrefDlg : : getInstance ( ) - > show ( ) ;
}
void
KMixWindow : : showHelp ( )
{
actionCollection ( ) - > action ( " help_contents " ) - > trigger ( ) ;
}
void
KMixWindow : : showAbout ( )
{
actionCollection ( ) - > action ( " help_about_app " ) - > trigger ( ) ;
}
/**
* Apply the Preferences from the preferences dialog . Depending on what has been changed ,
* the corresponding announcemnts are made .
*/
void KMixWindow : : applyPrefs ( )
{
// -1- Determine what has changed ------------------------------------------------------------------
GlobalConfigData & config = GlobalConfig : : instance ( ) . data ;
GlobalConfigData & configBefore = configDataSnapshot ;
bool labelsHasChanged = config . showLabels ^ configBefore . showLabels ;
bool ticksHasChanged = config . showTicks ^ configBefore . showTicks ;
bool dockwidgetHasChanged = config . showDockWidget ^ configBefore . showDockWidget ;
bool toplevelOrientationHasChanged = config . getToplevelOrientation ( ) ! = configBefore . getToplevelOrientation ( ) ;
bool traypopupOrientationHasChanged = config . getTraypopupOrientation ( ) ! = configBefore . getTraypopupOrientation ( ) ;
kDebug ( ) < < " toplevelOrientationHasChanged= " < < toplevelOrientationHasChanged < <
" , config= " < < config . getToplevelOrientation ( ) < < " , configBefore= " < < configBefore . getToplevelOrientation ( ) ;
kDebug ( ) < < " trayOrientationHasChanged= " < < traypopupOrientationHasChanged < <
" , config= " < < config . getTraypopupOrientation ( ) < < " , configBefore= " < < configBefore . getTraypopupOrientation ( ) ;
// -2- Determine what effect the changes have ------------------------------------------------------------------
if ( dockwidgetHasChanged | | toplevelOrientationHasChanged
| | traypopupOrientationHasChanged )
{
// These might need a complete relayout => announce a ControlList change to rebuild everything
ControlManager : : instance ( ) . announce ( QString ( ) , ControlChangeType : : ControlList , QString ( " Preferences Dialog " ) ) ;
}
else if ( labelsHasChanged | | ticksHasChanged )
{
ControlManager : : instance ( ) . announce ( QString ( ) , ControlChangeType : : GUI , QString ( " Preferences Dialog " ) ) ;
}
// showOSD does not require any information. It reads on-the-fly from GlobalConfig.
// -3- Apply all changes ------------------------------------------------------------------
// this->repaint(); // make KMix look fast (saveConfig() often uses several seconds)
kapp - > processEvents ( ) ;
configDataSnapshot = GlobalConfig : : instance ( ) . data ; // create a new snapshot as all current changes are applied now
// Remove saveConfig() IF aa changes have been migrated to GlobalConfig.
// Currently there is still stuff like "show menu bar".
saveConfig ( ) ;
}
/**
* Sets whether a beep on volume change should be done .
* This method store the value internally and also propagates
* this to the Mixer core .
*
* @ param beep true , if a beep should be changed
*/
void KMixWindow : : setBeepOnVolumeChange ( bool beep )
{
Mixer : : setBeepOnVolumeChange ( beep ) ;
}
void
KMixWindow : : toggleMenuBar ( )
{
menuBar ( ) - > setVisible ( _actionShowMenubar - > isChecked ( ) ) ;
}
void
KMixWindow : : slotHWInfo ( )
{
KMessageBox : : information ( 0 , m_hwInfoString ,
i18n ( " Mixer Hardware Information " ) ) ;
}
void
KMixWindow : : slotKdeAudioSetupExec ( )
{
QStringList args ;
2016-04-10 14:57:29 +00:00
args < < " kcmshell4 " < < " kcmplayer " ;
2014-11-19 02:23:05 +00:00
forkExec ( args ) ;
}
void
KMixWindow : : forkExec ( const QStringList & args )
{
int pid = KProcess : : startDetached ( args ) ;
if ( pid = = 0 )
{
static const QString startErrorMessage (
2016-04-10 14:57:29 +00:00
i18n ( " The helper application is either not installed or not working. " ) ) ;
2014-11-19 02:23:05 +00:00
QString msg ;
msg + = startErrorMessage ;
msg + = " \n ( " ;
msg + = args . join ( QLatin1String ( " " ) ) ;
msg + = ' ) ' ;
errorPopup ( msg ) ;
}
}
void
KMixWindow : : errorPopup ( const QString & msg )
{
QPointer < KDialog > dialog = new KDialog ( this ) ;
dialog - > setButtons ( KDialog : : Ok ) ;
dialog - > setCaption ( i18n ( " Error " ) ) ;
QLabel * qlbl = new QLabel ( msg ) ;
dialog - > setMainWidget ( qlbl ) ;
dialog - > exec ( ) ;
delete dialog ;
kWarning ( ) < < msg ;
}
void
KMixWindow : : slotConfigureCurrentView ( )
{
KMixerWidget * mw = ( KMixerWidget * ) m_wsMixers - > currentWidget ( ) ;
ViewBase * view = 0 ;
if ( mw )
view = mw - > currentView ( ) ;
if ( view )
view - > configureView ( ) ;
}
void KMixWindow : : slotSelectMasterClose ( QObject * )
{
m_dsm = 0 ;
}
void KMixWindow : : slotSelectMaster ( )
{
Mixer * mixer = Mixer : : getGlobalMasterMixer ( ) ;
if ( mixer ! = 0 )
{
if ( ! m_dsm ) {
m_dsm = new DialogSelectMaster ( Mixer : : getGlobalMasterMixer ( ) , this ) ;
connect ( m_dsm , SIGNAL ( destroyed ( QObject * ) ) , this , SLOT ( slotSelectMasterClose ( QObject * ) ) ) ;
m_dsm - > setAttribute ( Qt : : WA_DeleteOnClose , true ) ;
m_dsm - > show ( ) ;
}
m_dsm - > raise ( ) ;
m_dsm - > activateWindow ( ) ;
}
else
{
KMessageBox : : error ( 0 , i18n ( " No sound card is installed or currently plugged in. " ) ) ;
}
}
void
KMixWindow : : newMixerShown ( int /*tabIndex*/ )
{
KMixerWidget * kmw = ( KMixerWidget * ) m_wsMixers - > currentWidget ( ) ;
if ( kmw )
{
// I am using the app name as a PREFIX, as KMix is a single window app, and it is
// more helpful to the user to see "KDE Mixer" in a window list than a possibly cryptic
// soundcard name like "HDA ATI SB"
setWindowTitle ( i18n ( " KDE Mixer " ) + " - " + kmw - > mixer ( ) - > readableName ( ) ) ;
if ( ! m_dontSetDefaultCardOnStart )
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 - > isDynamic ( ) ) ;
}
}
2015-02-27 11:02:43 +00:00
# include "moc_kmix.cpp"