2 * KMix -- KDE's full featured mini mixer
5 * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
6 * Copyright (C) 2001 Preston Brown <pbrown@kde.org>
7 * Copyright (C) 2004 Christian Esken <esken@kde.org>
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this program; if not, write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #include <kapplication.h>
29 #include <kglobalsettings.h>
31 #include <kiconloader.h>
33 #include <kwindowsystem.h>
34 #include <kactioncollection.h>
35 #include <ktoggleaction.h>
36 #include <qapplication.h>
38 #include <QDesktopWidget>
39 #include <QMouseEvent>
41 #include <Phonon/MediaObject>
43 #include "dialogselectmaster.h"
45 #include "kmixdockwidget.h"
47 #include "mixdevicewidget.h"
48 #include "mixertoolbox.h"
49 #include "viewdockareapopup.h"
51 KMixDockWidget::KMixDockWidget(KMixWindow
* parent
, QWidget
*referenceWidget
, bool volumePopup
)
52 : KSystemTrayIcon( referenceWidget
),
54 _playBeepOnVolumeChange(false), // disabled due to triggering a "Bug"
57 _volumePopup(volumePopup
)
58 , _kmixMainWindow(parent
)
60 m_mixer
= Mixer::getGlobalMasterMixer(); // ugly, but we'll live with that for now
61 createMasterVolWidget();
63 //connect(this, SIGNAL(quitSelected()), kapp, SLOT(quitExtended()));
66 KMixDockWidget::~KMixDockWidget()
71 void KMixDockWidget::createActions()
73 // Put "Mute" selector in context menu
74 KToggleAction
*action
= actionCollection()->add
<KToggleAction
>( "dock_mute" );
75 action
->setText( i18n( "M&ute" ) );
76 connect(action
, SIGNAL(triggered(bool) ), SLOT( dockMute() ));
77 QAction
*a
= actionCollection()->action( "dock_mute" );
78 QMenu
*menu
= contextMenu();
79 if ( a
) menu
->addAction( a
);
81 connect ( this, SIGNAL(activated(QSystemTrayIcon::ActivationReason
)), SLOT(kmixSystrayAction(QSystemTrayIcon::ActivationReason
) ) );
83 // Put "Select Master Channel" dialog in context menu
85 QAction
*action
= actionCollection()->addAction( "select_master" );
86 action
->setText( i18n("Select Master Channel...") );
87 connect(action
, SIGNAL(triggered(bool) ), SLOT(selectMaster()));
88 QAction
*a2
= actionCollection()->action( "select_master" );
89 if (a2
) menu
->addAction( a2
);
92 // Setup volume preview
93 if ( _playBeepOnVolumeChange
) {
94 _audioPlayer
= Phonon::createPlayer(Phonon::MusicCategory
);
95 _audioPlayer
->setParent(this);
101 KMixDockWidget::createMasterVolWidget()
103 // Reset flags, so that the dock icon will be reconstructed
104 _oldToolTipValue
= -1;
105 _oldPixmapType
= '-';
107 if (Mixer::getGlobalMasterMD() == 0) {
108 // In case that there is no mixer installed, there will be no controlChanged() signal's
109 // Thus we prepare the dock areas manually
116 m_mixer
->readSetFromHWforceUpdate(); // after changing the master device, make sure to re-read (otherwise no "changed()" signals might get sent by the Mixer
117 /* With the recently introduced QSocketNotifier stuff, we can't rely on regular timer updates
118 any longer. Also the readSetFromHWforceUpdate() won't be enough. As a workaround, we trigger
119 all "repaints" manually here.
120 The call to m_mixer->readSetFromHWforceUpdate() is most likely superfluous, even if we don't use QSocketNotifier (e.g. in backends OSS, Solaris, ...)
124 /* We are setting up 3 connections:
125 * Refreshig the _dockAreaPopup (not anymore necessary, because ViewBase already does it)
126 * Refreshing the Tooltip
127 * Refreshing the Icon
129 // connect( m_mixer, SIGNAL(controlChanged()), _dockAreaPopup, SLOT(refreshVolumeLevels()) );
130 connect( m_mixer
, SIGNAL(controlChanged()), this, SLOT(setVolumeTip() ) );
131 connect( m_mixer
, SIGNAL(controlChanged()), this, SLOT(updatePixmap() ) );
135 void KMixDockWidget::selectMaster()
137 DialogSelectMaster
* dsm
= new DialogSelectMaster(m_mixer
);
138 connect ( dsm
, SIGNAL(newMasterSelected(QString
&, QString
&)), SLOT( handleNewMaster(QString
&, QString
&)) );
140 // !! The dialog is modal. Does it delete itself?
144 void KMixDockWidget::handleNewMaster(QString
& /*mixerID*/, QString
& /*control_id*/)
146 /* When a new master is selected, we will need to destroy *this instance of KMixDockWidget.
147 Reason: This widget is a KSystemTrayIcon, and needs an associated QWidget. This is in
148 our case the ViewDockAreaPopup instance. As that is recreated, we need to recrate ourself as well.
149 The only chance to do so is outside, in fact it is done by KMixWindow::updateDocking(). This needs to
150 use deleteLater(), as we are executing in a SLOT currently.
152 _kmixMainWindow
->updateDocking();
154 Mixer *mixer = MixerToolBox::instance()->find(mixerID);
156 kError(67100) << "KMixDockWidget::createPage(): Invalid Mixer (mixerID=" << mixerID << ")" << endl;
157 return; // can not happen
160 //Mixer::setGlobalMaster(mixer->id(), control_id); // We must save this information "somewhere".
161 createMasterVolWidget();
167 KMixDockWidget::setVolumeTip()
169 MixDevice
*md
= Mixer::getGlobalMasterMD();
171 int newToolTipValue
= 0;
175 tip
= i18n("Mixer cannot be found"); // !! text could be reworked
176 newToolTipValue
= -2;
180 // Playback volume will be used for the DockIcon if available.
181 // This heuristic is "good enough" for the DockIcon for now.
183 Volume
& vol
= md
->playbackVolume();
184 if (! vol
.hasVolume() ) {
185 vol
= md
->captureVolume();
187 if ( vol
.hasVolume() && vol
.maxVolume() != 0 ) {
188 val
= (vol
.getAvgVolume(Volume::MMAIN
)*100 )/( vol
.maxVolume() );
191 // create a new "virtual" value. With that we see "volume changes" as well as "muted changes"
192 newToolTipValue
= val
;
193 if ( md
->isMuted() ) newToolTipValue
+= 10000;
194 if ( _oldToolTipValue
!= newToolTipValue
) {
195 tip
= i18n( "Volume at %1%", val
);
196 if ( md
->playbackVolume().hasSwitch() && md
->isMuted() ) {
197 tip
+= i18n( " (Muted)" );
202 // The actual updating is only done when the "toolTipValue" was changed
203 if ( newToolTipValue
!= _oldToolTipValue
) {
204 // changed (or completely new tooltip)
205 this->setToolTip(tip
);
207 _oldToolTipValue
= newToolTipValue
;
211 KMixDockWidget::updatePixmap()
213 MixDevice
*md
= Mixer::getGlobalMasterMD();
220 else if ( md
->playbackVolume().hasSwitch() && md
->isMuted() )
226 Volume
& vol
= md
->playbackVolume();
227 if (! vol
.hasVolume() ) {
228 vol
= md
->captureVolume();
230 long absoluteVolume
= vol
.getAvgVolume(Volume::MALL
);
231 int percentage
= vol
.percentage(absoluteVolume
);
232 if ( percentage
< 25 ) newPixmapType
= '1'; // Hint: also negative-values
233 else if ( percentage
< 75 ) newPixmapType
= '2';
234 else newPixmapType
= '3';
238 if ( newPixmapType
!= _oldPixmapType
) {
239 // Pixmap must be changed => do so
240 switch ( newPixmapType
) {
241 case 'e': setIcon( loadIcon( "kmixdocked_error" ) ); break;
242 case 'm': setIcon( loadIcon( "audio-volume-muted" ) ); break;
243 case '1': setIcon( loadIcon( "audio-volume-low" ) ); break;
244 case '2': setIcon( loadIcon( "audio-volume-medium" ) ); break;
245 case '3': setIcon( loadIcon( "audio-volume-high" ) ); break;
249 _oldPixmapType
= newPixmapType
;
253 void KMixDockWidget::moveVolumePopoup()
255 ViewDockAreaPopup
* dockAreaPopup
= qobject_cast
<ViewDockAreaPopup
*>(parent());
256 if ( dockAreaPopup
== 0 ) {
257 // If the associated parent os the MainWindow (and not the ViewDockAreaPopup), we return.
261 int h
= dockAreaPopup
->height();
262 int x
= geometry().x() - geometry().width()/2 - dockAreaPopup
->width()/2;
263 int y
= geometry().y() - h
;
265 // kDebug() << "h="<<h<< " x="<<x << " y="<<y<< "gx="<< geometry().x() << "gy="<< geometry().y();
268 y
= y
+ h
+ geometry().height();
270 dockAreaPopup
->move(x
, y
); // so that the mouse is outside of the widget
272 // Now handle Multihead displays. And also make sure that the dialog is not
273 // moved out-of-the screen on the right (see Bug 101742).
274 const QDesktopWidget
* vdesktop
= QApplication::desktop();
275 const QRect
& vScreenSize
= vdesktop
->screenGeometry(dockAreaPopup
);
276 //const QRect screenGeometry(const QWidget *widget) const
277 if ( (x
+dockAreaPopup
->width()) > (vScreenSize
.width() + vScreenSize
.x()) ) {
278 // move horizontally, so that it is completely visible
279 dockAreaPopup
->move(vScreenSize
.width() + vScreenSize
.x() - dockAreaPopup
->width() -1 , y
);
280 } // horizontally out-of bound
281 else if ( x
< vScreenSize
.x() ) {
282 dockAreaPopup
->move(vScreenSize
.x(), y
);
284 // the above stuff could also be implemented vertically
286 // dockAreaPopup->show();
287 KWindowSystem::setState(dockAreaPopup
->winId(), NET::StaysOnTop
| NET::SkipTaskbar
| NET::SkipPager
);
292 bool KMixDockWidget::event( QEvent
* event
)
294 if (event
->type() == QEvent::Wheel
) {
295 trayWheelEvent((QWheelEvent
*)event
);
299 // else if (event->type() == QEvent::ToolTip ) {
300 // trayToolTipEvent((QHelpEvent*)event);
309 // KMixDockWidget::trayToolTipEvent(QHelpEvent *e ) {
310 // kDebug(67100) << "trayToolTipEvent" ;
315 KMixDockWidget::trayWheelEvent(QWheelEvent
*e
)
317 MixDevice
*md
= Mixer::getGlobalMasterMD();
320 Volume vol
= md
->playbackVolume();
321 if ( md
->playbackVolume().hasVolume() )
322 vol
= md
->playbackVolume();
324 vol
= md
->captureVolume();
326 int inc
= vol
.maxVolume() / 20;
328 if ( inc
< 1 ) inc
= 1;
330 for ( int i
= 0; i
< vol
.count(); i
++ ) {
331 int newVal
= vol
[i
] + (inc
* (e
->delta() / 120));
332 if( newVal
< 0 ) newVal
= 0;
333 vol
.setVolume( (Volume::ChannelID
)i
, newVal
< vol
.maxVolume() ? newVal
: vol
.maxVolume() );
337 if ( _playBeepOnVolumeChange ) {
338 _audioPlayer->setCurrentSource("KDE_Beep_Digital_1.ogg");
339 _audioPlayer->play();
342 if ( md
->playbackVolume().hasVolume() )
343 md
->playbackVolume().setVolume(vol
);
345 md
->captureVolume().setVolume(vol
);
346 m_mixer
->commitVolumeChange(md
);
347 // refresh the toolTip (Qt removes it on a MouseWheel event)
348 // Mhhh, it doesn't work. Qt does not show it again.
350 // Simulate a mouse move to make Qt show the tooltip again
351 //QApplication::postEvent( this, new QMouseEvent( QEvent::MouseMove, QCursor::pos(), Qt::NoButton, Qt::NoButton ) );
357 KMixDockWidget::dockMute()
359 MixDevice
*md
= Mixer::getGlobalMasterMD();
361 md
->setMuted( !md
->isMuted() );
362 md
->mixer()->commitVolumeChange( md
);
367 void KMixDockWidget::kmixSystrayAction(QSystemTrayIcon::ActivationReason reason
)
369 if ( reason
== QSystemTrayIcon::MiddleClick
) {
372 else if ( reason
== QSystemTrayIcon::Trigger
) {
373 // Move the popup to a suitable place
380 KMixDockWidget::contextMenuAboutToShow( KMenu
* /* menu */ )
382 QAction
* showAction
= actionCollection()->action("minimizeRestore");
383 if ( parentWidget() && showAction
)
385 if ( parentWidget()->isVisible() )
387 showAction
->setText( i18n("Hide Mixer Window") );
391 showAction
->setText( i18n("Show Mixer Window") );
395 // Enable/Disable "Muted" menu item
396 ViewDockAreaPopup
* dockAreaPopup
= qobject_cast
<ViewDockAreaPopup
*>(parent());
397 if ( dockAreaPopup
!= 0 ) {
398 MixDevice
* md
= dockAreaPopup
->dockDevice();
399 KToggleAction
*dockMuteAction
= static_cast<KToggleAction
*>(actionCollection()->action("dock_mute"));
400 //kDebug(67100) << "---> md=" << md << "dockMuteAction=" << dockMuteAction << "isMuted=" << md->isMuted();
401 if ( md
!= 0 && dockMuteAction
!= 0 ) {
402 dockMuteAction
->setChecked( md
->isMuted() );
407 #include "kmixdockwidget.moc"