/*
* KMix - - KDE ' s full featured mini mixer
*
*
* Copyright ( C ) 2000 Stefan Schimanski < 1 Stein @ gmx . de >
* Copyright ( C ) 2001 Preston Brown < pbrown @ kde . org >
* Copyright ( C ) 2003 Sven Leiber < s . leiber @ web . de >
* Copyright ( C ) 2004 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 <kpanelapplet.h>
# include <kdialog.h>
# include <kaudioplayer.h>
# include <kiconloader.h>
# include <kdebug.h>
# include <khelpmenu.h>
# include <twin.h>
# include <tdeaction.h>
# include <tdeapplication.h>
# include <tdelocale.h>
# include <tdepopupmenu.h>
# include <tdeglobalsettings.h>
# include <kstandarddirs.h>
# include <tdemainwindow.h>
# include <tqapplication.h>
# include <tqcursor.h>
# include <tqtooltip.h>
# include <tqimage.h>
# include <X11/Xlib.h>
# include <fixx11h.h>
# include "dialogselectmaster.h"
# include "mixer.h"
# include "mixdevicewidget.h"
# include "kmixdockwidget.h"
# include "kmixsettings.h"
# include "viewdockareapopup.h"
KMixDockWidget : : KMixDockWidget ( Mixer * mixer , TQWidget * parent , const char * name , bool volumePopup , bool dockIconMuting )
: KSystemTray ( parent , name ) ,
m_mixer ( mixer ) ,
_dockAreaPopup ( 0L ) ,
_audioPlayer ( 0L ) ,
_playBeepOnVolumeChange ( false ) , // disabled due to triggering a "Bug"
_oldToolTipValue ( - 1 ) ,
_oldPixmapType ( ' - ' ) ,
_volumePopup ( volumePopup ) ,
_dockIconMuting ( dockIconMuting ) ,
_dsm ( NULL )
{
Mixer * preferredMasterMixer = Mixer : : masterCard ( ) ;
if ( preferredMasterMixer ! = 0 ) {
m_mixer = preferredMasterMixer ;
}
MixDevice * mdMaster = Mixer : : masterCardDevice ( ) ;
if ( mdMaster ! = 0 ) {
m_mixer - > setMasterDevice ( mdMaster - > getPK ( ) ) ; // !! using both Mixer::masterCard() and m_mixer->masterDevice() is nonsense !!
}
createActions ( ) ;
createMasterVolWidget ( ) ;
connect ( this , TQ_SIGNAL ( quitSelected ( ) ) , kapp , TQ_SLOT ( quitExtended ( ) ) ) ;
TDEGlobal : : dirs ( ) - > addResourceDir ( " icons_crystal " , locate ( " appdata " , " pics/crystal/ " ) ) ;
TDEGlobal : : dirs ( ) - > addResourceDir ( " icons_oldcrystal " , locate ( " appdata " , " pics/oldcrystal/ " ) ) ;
}
KMixDockWidget : : ~ KMixDockWidget ( )
{
if ( _dsm )
{
delete _dsm ;
}
delete _audioPlayer ;
delete _dockAreaPopup ;
}
void KMixDockWidget : : createActions ( )
{
TDEPopupMenu * popupMenu = contextMenu ( ) ;
// Put "Mute" selector in context menu
( void ) new TDEToggleAction ( i18n ( " M&ute " ) , 0 , this , TQ_SLOT ( dockMute ( ) ) ,
actionCollection ( ) , " dock_mute " ) ;
TDEAction * a = actionCollection ( ) - > action ( " dock_mute " ) ;
if ( a )
{
a - > plug ( popupMenu ) ;
}
// Put "Select Master Channel" dialog in context menu
if ( m_mixer )
{
( void ) new TDEAction ( i18n ( " Select Master Channel... " ) , 0 , this , TQ_SLOT ( selectMaster ( ) ) ,
actionCollection ( ) , " select_master " ) ;
a = actionCollection ( ) - > action ( " select_master " ) ;
if ( a )
{
a - > plug ( popupMenu ) ;
}
}
// Show/hide mixer window (use "minimizeRestore" action
a = actionCollection ( ) - > action ( " minimizeRestore " ) ;
if ( a )
{
a - > plug ( popupMenu ) ;
}
popupMenu - > insertSeparator ( ) ;
// KMix Options
TDEMainWindow * toplevel = static_cast < TDEMainWindow * > ( parent ( ) ) ;
a = toplevel - > actionCollection ( ) - > action ( KStdAction : : name ( KStdAction : : Preferences ) ) ;
if ( a )
{
a - > plug ( popupMenu ) ;
}
// Help and quit
popupMenu - > insertItem ( SmallIcon ( " help " ) , KStdGuiItem : : help ( ) . text ( ) , ( new KHelpMenu ( this , TDEGlobal : : instance ( ) - > aboutData ( ) , false ) ) - > menu ( ) , false ) ;
popupMenu - > insertSeparator ( ) ;
a = actionCollection ( ) - > action ( KStdAction : : name ( KStdAction : : Quit ) ) ;
if ( a )
{
a - > plug ( popupMenu ) ;
}
// Setup volume preview
if ( _playBeepOnVolumeChange )
{
_audioPlayer = new KAudioPlayer ( " KDE_Beep_Digital_1.ogg " ) ;
}
}
void KMixDockWidget : : createMasterVolWidget ( )
{
// Reset flags, so that the dock icon will be reconstructed
_oldToolTipValue = - 1 ;
_oldPixmapType = ' - ' ;
if ( m_mixer = = 0 ) {
// In case that there is no mixer installed, there will be no newVolumeLevels() signal's
// Thus we prepare the dock areas manually
setVolumeTip ( ) ;
updatePixmap ( false ) ;
return ;
}
// create devices
if ( _dockAreaPopup )
{
// Delete the old popup widget if we are changing the channel
deleteMasterVolWidget ( ) ;
}
_dockAreaPopup = new ViewDockAreaPopup ( 0 , " dockArea " , m_mixer , 0 , this ) ;
_dockAreaPopup - > createDeviceWidgets ( ) ;
m_mixer - > readSetFromHWforceUpdate ( ) ; // after changing the master device, make sure to re-read (otherwise no "changed()" signals might get sent by the Mixer
/* With the recently introduced TQSocketNotifier stuff, we can't rely on regular timer updates
any longer . Also the readSetFromHWforceUpdate ( ) won ' t be enough . As a workaround , we trigger
all " repaints " manually here .
The call to m_mixer - > readSetFromHWforceUpdate ( ) is most likely superfluous , even if we don ' t use TQSocketNotifier ( e . g . in backends OSS , Solaris , . . . )
*/
setVolumeTip ( ) ;
updatePixmap ( false ) ;
/* We are setting up 3 connections:
* Refreshig the _dockAreaPopup ( not anymore neccesary , because ViewBase already does it )
* Refreshing the Tooltip
* Refreshing the Icon
*
*/
connect ( m_mixer , TQ_SIGNAL ( newVolumeLevels ( ) ) , this , TQ_SLOT ( setVolumeTip ( ) ) ) ;
connect ( m_mixer , TQ_SIGNAL ( newVolumeLevels ( ) ) , this , TQ_SLOT ( slotUpdatePixmap ( ) ) ) ;
}
void KMixDockWidget : : deleteMasterVolWidget ( )
{
if ( _dockAreaPopup )
{
delete _dockAreaPopup ;
_dockAreaPopup = NULL ;
}
if ( m_mixer )
{
disconnect ( m_mixer , TQ_SIGNAL ( newVolumeLevels ( ) ) , this , TQ_SLOT ( setVolumeTip ( ) ) ) ;
disconnect ( m_mixer , TQ_SIGNAL ( newVolumeLevels ( ) ) , this , TQ_SLOT ( slotUpdatePixmap ( ) ) ) ;
}
}
void KMixDockWidget : : slotUpdatePixmap ( )
{
updatePixmap ( false ) ;
}
void KMixDockWidget : : selectMaster ( )
{
if ( ! _dsm )
{
_dsm = new DialogSelectMaster ( m_mixer ) ;
connect ( _dsm , TQ_SIGNAL ( newMasterSelected ( bool , int , const TQString & ) ) , TQ_SLOT ( handleNewMaster ( bool , int , const TQString & ) ) ) ;
}
_dsm - > show ( m_mixer ) ;
}
void KMixDockWidget : : handleNewMaster ( bool defaultMaster , int soundcard_id , const TQString & channel_id )
{
//kdDebug(67100) << "KMixDockWidget::handleNewMaster() default master=" << defaultMaster << ", soundcard_id=" << soundcard_id << ", channel_id=" << channel_id << endl;
kapp - > config ( ) - > setGroup ( 0 ) ;
kapp - > config ( ) - > writeEntry ( " UseDefaultMaster " , defaultMaster ) ;
Mixer * mixer ;
TQString channel = TQString : : null ;
if ( defaultMaster )
{
mixer = Mixer : : mixers ( ) . first ( ) ;
if ( mixer )
{
MixSet ms = mixer - > getMixSet ( ) ;
for ( MixDevice * md = ms . first ( ) ; md ! = 0 ; md = ms . next ( ) )
{
if ( ! md - > isRecordable ( ) & & ! md - > isSwitch ( ) & & ! md - > isEnum ( ) )
{
channel = md - > getPK ( ) ;
break ;
}
}
}
}
else
{
mixer = Mixer : : mixers ( ) . at ( soundcard_id ) ;
channel = channel_id ;
}
if ( ! mixer | | channel . isEmpty ( ) ) {
kdError ( 67100 ) < < " KMixDockWidget::createPage(): Invalid Mixer (default master= " < < defaultMaster < < " , soundcard_id= "
< < soundcard_id < < " , channel_id= " < < channel_id < < " ) " < < endl ;
return ; // can not happen
}
deleteMasterVolWidget ( ) ;
m_mixer = mixer ;
Mixer : : setMasterCard ( mixer - > id ( ) ) ; // We must save this information "somewhere".
Mixer : : setMasterCardDevice ( channel ) ;
createMasterVolWidget ( ) ;
}
long
KMixDockWidget : : getAvgVolume ( )
{
MixDevice * md = 0 ;
if ( _dockAreaPopup ! = 0 ) {
md = _dockAreaPopup - > dockDevice ( ) ;
}
if ( md = = 0 | | md - > maxVolume ( ) = = 0 )
return - 1 ;
return ( md - > getVolume ( ) . getAvgVolume ( Volume : : MMAIN ) * 100 ) / ( md - > maxVolume ( ) ) ;
}
void
KMixDockWidget : : setVolumeTip ( )
{
MixDevice * md = 0 ;
if ( _dockAreaPopup ! = 0 ) {
md = _dockAreaPopup - > dockDevice ( ) ;
}
TQString tip = " " ;
int newToolTipValue = 0 ;
if ( md = = 0 )
{
tip = i18n ( " Mixer cannot be found " ) ; // !! text could be reworked
newToolTipValue = - 2 ;
}
else
{
long val = getAvgVolume ( ) ;
newToolTipValue = val + 10000 * md - > isMuted ( ) ;
if ( _oldToolTipValue ! = newToolTipValue ) {
tip = i18n ( " Volume at %1% " ) . arg ( val ) ;
if ( md - > isMuted ( ) ) {
tip + = i18n ( " (Muted) " ) ;
}
}
// create a new "virtual" value. With that we see "volume changes" as well as "muted changes"
newToolTipValue = val + 10000 * md - > isMuted ( ) ;
}
// The actual updating is only done when the "toolTipValue" was changed
if ( newToolTipValue ! = _oldToolTipValue ) {
// changed (or completely new tooltip)
if ( _oldToolTipValue > = 0 ) {
// there was an old Tooltip: remove it
TQToolTip : : remove ( this ) ;
}
TQToolTip : : add ( this , tip ) ;
}
_oldToolTipValue = newToolTipValue ;
}
void
KMixDockWidget : : updatePixmap ( bool force )
{
MixDevice * md = 0 ;
if ( _dockAreaPopup ! = 0 ) {
md = _dockAreaPopup - > dockDevice ( ) ;
}
char newPixmapType ;
if ( md = = 0 )
{
newPixmapType = ' e ' ;
}
else if ( md - > isMuted ( ) )
{
newPixmapType = ' m ' ;
}
else
{
long avgVol = getAvgVolume ( ) ;
if ( avgVol < = 33 )
newPixmapType = ' L ' ;
else if ( avgVol < = 67 )
newPixmapType = ' M ' ;
else
newPixmapType = ' H ' ;
}
if ( ( newPixmapType ! = _oldPixmapType ) | | ( force = = true ) ) {
// Pixmap must be changed => do so
// Honor Free Desktop specifications that allow for arbitrary system tray icon sizes
TQPixmap origpixmap ;
TQPixmap scaledpixmap ;
TQImage newIcon ;
TQStringList fallback ;
switch ( newPixmapType ) {
case ' m ' : fallback < < " audio-volume-muted " < < " kmixdocked_mute " ; break ;
case ' L ' : fallback < < " audio-volume-low " < < " kmixdocked " ; break ;
case ' M ' : fallback < < " audio-volume-medium " < < " kmixdocked " ; break ;
case ' H ' : fallback < < " audio-volume-high " < < " kmixdocked " ; break ;
}
TQString icon = getIconPath ( fallback ) ;
if ( icon . isNull ( ) )
{
icon = getIconPath ( " audio-volume-error " ) ;
}
origpixmap = isShown ( ) ? loadSizedIcon ( icon , width ( ) ) : loadIcon ( icon ) ;
newIcon = origpixmap ;
if ( isShown ( ) ) {
newIcon = newIcon . smoothScale ( width ( ) , height ( ) ) ;
}
scaledpixmap = newIcon ;
setPixmap ( scaledpixmap ) ;
_oldPixmapType = newPixmapType ;
}
}
TQString KMixDockWidget : : getIconPath ( TQStringList fallback )
{
auto iconTheme = KMixSettings : : iconTheme ( ) ;
TQCString iconThemeName ;
if ( iconTheme ! = KMixSettings : : EnumIconTheme : : System )
{
switch ( iconTheme )
{
case KMixSettings : : EnumIconTheme : : OldCrystal :
iconThemeName = " oldcrystal " ;
break ;
default :
case KMixSettings : : EnumIconTheme : : Crystal :
iconThemeName = " crystal " ;
break ;
}
}
for ( TQStringList : : iterator it = fallback . begin ( ) ; it ! = fallback . end ( ) ; + + it )
{
if ( iconTheme = = KMixSettings : : EnumIconTheme : : System )
{
TQString iconPath = kapp - > iconLoader ( ) - > iconPath ( ( * it ) , TDEIcon : : Panel , true ) ;
if ( ! iconPath . isNull ( ) )
{
return iconPath ;
}
}
else
{
TQCString type = " icons_ " + iconThemeName ;
TQString iconPath = TDEGlobal : : dirs ( ) - > findResource ( type , TQString ( " %1.png " ) . arg ( * it ) ) ;
if ( ! iconPath . isNull ( ) ) return iconPath ;
iconPath = TDEGlobal : : dirs ( ) - > findResource ( type , TQString ( " %1.svg " ) . arg ( * it ) ) ;
if ( ! iconPath . isNull ( ) ) return iconPath ;
}
}
return TQString : : null ;
}
void KMixDockWidget : : resizeEvent ( TQResizeEvent * )
{
updatePixmap ( true ) ;
}
void KMixDockWidget : : showEvent ( TQShowEvent * se )
{
updatePixmap ( true ) ;
}
void
KMixDockWidget : : mousePressEvent ( TQMouseEvent * me )
{
if ( _dockAreaPopup = = 0 ) {
return KSystemTray : : mousePressEvent ( me ) ;
}
// esken: Due to overwhelming request, LeftButton shows the ViewDockAreaPopup, if configured
// to do so. Otherwise the main window will be shown.
if ( me - > button ( ) = = TQt : : LeftButton )
{
if ( ! _volumePopup ) {
// Case 1: User wants to show main window => This is the KSystemTray default action
return KSystemTray : : mousePressEvent ( me ) ;
}
// Case 2: User wants to show volume popup
if ( _dockAreaPopup - > justHidden ( ) )
return ;
if ( _dockAreaPopup - > isVisible ( ) )
{
_dockAreaPopup - > hide ( ) ;
return ;
}
int h = _dockAreaPopup - > height ( ) ;
int x = this - > mapToGlobal ( TQPoint ( 0 , 0 ) ) . x ( ) + this - > width ( ) / 2 - _dockAreaPopup - > width ( ) / 2 ;
int y = this - > mapToGlobal ( TQPoint ( 0 , 0 ) ) . y ( ) - h ;
if ( y < 0 )
y = y + h + this - > height ( ) ;
_dockAreaPopup - > move ( x , y ) ; // so that the mouse is outside of the widget
// Now handle Multihead displays. And also make sure that the dialog is not
// moved out-of-the screen on the right (see Bug 101742).
TQDesktopWidget * vdesktop = TQApplication : : desktop ( ) ;
const TQRect & vScreenSize = vdesktop - > screenGeometry ( _dockAreaPopup ) ;
if ( ( x + _dockAreaPopup - > width ( ) ) > ( vScreenSize . width ( ) + vScreenSize . x ( ) ) ) {
// move horizontally, so that it is completely visible
_dockAreaPopup - > move ( vScreenSize . width ( ) + vScreenSize . x ( ) - _dockAreaPopup - > width ( ) - 1 , y ) ;
} // horizontally out-of bound
else if ( x < vScreenSize . x ( ) ) {
_dockAreaPopup - > move ( vScreenSize . x ( ) , y ) ;
}
// the above stuff could also be implemented vertically
_dockAreaPopup - > show ( ) ;
KWin : : setState ( _dockAreaPopup - > winId ( ) , NET : : StaysOnTop | NET : : SkipTaskbar | NET : : SkipPager ) ;
TQWidget : : mousePressEvent ( me ) ; // KSystemTray's shouldn't do the default action for this
return ;
} // LeftMouseButton pressed
else if ( me - > button ( ) = = TQt : : MidButton ) {
if ( ! _dockIconMuting ) {
toggleActive ( ) ;
} else {
dockMute ( ) ;
}
return ;
}
else {
KSystemTray : : mousePressEvent ( me ) ;
} // Other MouseButton pressed
}
void
KMixDockWidget : : mouseReleaseEvent ( TQMouseEvent * me )
{
KSystemTray : : mouseReleaseEvent ( me ) ;
}
void
KMixDockWidget : : wheelEvent ( TQWheelEvent * e )
{
MixDevice * md = 0 ;
if ( _dockAreaPopup ! = 0 ) {
md = _dockAreaPopup - > dockDevice ( ) ;
}
if ( md ! = 0 )
{
Volume vol = md - > getVolume ( ) ;
int inc = vol . maxVolume ( ) / 20 ;
if ( inc = = 0 ) inc = 1 ;
for ( int i = 0 ; i < vol . count ( ) ; i + + ) {
int newVal = vol [ i ] + ( inc * ( e - > delta ( ) / 120 ) ) ;
if ( newVal < 0 ) newVal = 0 ;
vol . setVolume ( ( Volume : : ChannelID ) i , newVal < vol . maxVolume ( ) ? newVal : vol . maxVolume ( ) ) ;
}
if ( _playBeepOnVolumeChange ) {
_audioPlayer - > play ( ) ;
}
md - > getVolume ( ) . setVolume ( vol ) ;
m_mixer - > commitVolumeChange ( md ) ;
// refresh the toolTip (TQt removes it on a MouseWheel event)
// Mhhh, it doesn't work. TQt does not show it again.
setVolumeTip ( ) ;
// Simulate a mouse move to make TQt show the tooltip again
TQApplication : : postEvent ( this , new TQMouseEvent ( TQEvent : : MouseMove , TQCursor : : pos ( ) , TQt : : NoButton , TQt : : NoButton ) ) ;
}
}
void
KMixDockWidget : : dockMute ( )
{
MixDevice * md = 0 ;
if ( _dockAreaPopup ! = 0 )
{
md = _dockAreaPopup - > dockDevice ( ) ;
if ( md ! = 0 ) {
md - > setMuted ( ! md - > isMuted ( ) ) ;
m_mixer - > commitVolumeChange ( md ) ;
updatePixmap ( false ) ;
}
}
}
void
KMixDockWidget : : contextMenuAboutToShow ( TDEPopupMenu * /* menu */ )
{
TDEAction * showAction = actionCollection ( ) - > action ( " minimizeRestore " ) ;
if ( parentWidget ( ) & & showAction )
{
if ( parentWidget ( ) - > isVisible ( ) )
{
showAction - > setText ( i18n ( " Hide Mixer Window " ) ) ;
}
else
{
showAction - > setText ( i18n ( " Show Mixer Window " ) ) ;
}
}
// Enable/Disable "Muted" menu item
MixDevice * md = 0 ;
if ( _dockAreaPopup ! = 0 )
{
md = _dockAreaPopup - > dockDevice ( ) ;
TDEToggleAction * dockMuteAction = static_cast < TDEToggleAction * > ( actionCollection ( ) - > action ( " dock_mute " ) ) ;
//kdDebug(67100) << "---> md=" << md << "dockMuteAction=" << dockMuteAction << "isMuted=" << md->isMuted() << endl;
if ( md ! = 0 & & dockMuteAction ! = 0 ) {
dockMuteAction - > setChecked ( md - > isMuted ( ) ) ;
}
}
}
# include "kmixdockwidget.moc"