Final polisihing for KDE4:
[kdemultimedia.git] / kmix / kmixdockwidget.cpp
blob7ecf5878d573a8b46f678d659ab1555bd50a1695
1 /*
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.
24 #include <kaction.h>
25 #include <klocale.h>
26 #include <kapplication.h>
27 #include <kmenu.h>
28 #include <kurl.h>
29 #include <kglobalsettings.h>
30 #include <kdialog.h>
31 #include <kiconloader.h>
32 #include <kdebug.h>
33 #include <kwindowsystem.h>
34 #include <kactioncollection.h>
35 #include <ktoggleaction.h>
36 #include <qapplication.h>
37 #include <qcursor.h>
38 #include <QDesktopWidget>
39 #include <QMouseEvent>
40 #include <fixx11h.h>
41 #include <Phonon/MediaObject>
43 #include "dialogselectmaster.h"
44 #include "kmix.h"
45 #include "kmixdockwidget.h"
46 #include "mixer.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 ),
53 _audioPlayer(0L),
54 _playBeepOnVolumeChange(false), // disabled due to triggering a "Bug"
55 _oldToolTipValue(-1),
56 _oldPixmapType('-'),
57 _volumePopup(volumePopup)
58 , _kmixMainWindow(parent)
60 m_mixer = Mixer::getGlobalMasterMixer(); // ugly, but we'll live with that for now
61 createMasterVolWidget();
62 createActions();
63 //connect(this, SIGNAL(quitSelected()), kapp, SLOT(quitExtended()));
66 KMixDockWidget::~KMixDockWidget()
68 delete _audioPlayer;
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
84 if ( m_mixer != 0 ) {
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);
100 void
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
110 setVolumeTip();
111 updatePixmap();
112 return;
114 // create devices
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, ...)
122 setVolumeTip();
123 updatePixmap();
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&)) );
139 dsm->show();
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);
155 if ( mixer == 0 ) {
156 kError(67100) << "KMixDockWidget::createPage(): Invalid Mixer (mixerID=" << mixerID << ")" << endl;
157 return; // can not happen
159 m_mixer = mixer;
160 //Mixer::setGlobalMaster(mixer->id(), control_id); // We must save this information "somewhere".
161 createMasterVolWidget();
166 void
167 KMixDockWidget::setVolumeTip()
169 MixDevice *md = Mixer::getGlobalMasterMD();
170 QString tip = "";
171 int newToolTipValue = 0;
173 if ( md == 0 )
175 tip = i18n("Mixer cannot be found"); // !! text could be reworked
176 newToolTipValue = -2;
178 else
180 // Playback volume will be used for the DockIcon if available.
181 // This heuristic is "good enough" for the DockIcon for now.
182 long val = 0;
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;
210 void
211 KMixDockWidget::updatePixmap()
213 MixDevice *md = Mixer::getGlobalMasterMD();
215 char newPixmapType;
216 if ( md == 0 )
218 newPixmapType = 'e';
220 else if ( md->playbackVolume().hasSwitch() && md->isMuted() )
222 newPixmapType = 'm';
224 else
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.
258 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();
267 if ( y < 0 )
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 );
289 return;
292 bool KMixDockWidget::event( QEvent * event )
294 if (event->type() == QEvent::Wheel ) {
295 trayWheelEvent((QWheelEvent*)event);
296 event->accept();
297 return true;
299 // else if (event->type() == QEvent::ToolTip ) {
300 // trayToolTipEvent((QHelpEvent*)event);
301 // event->accept();
302 // return true;
303 // }
304 return false;
308 // void
309 // KMixDockWidget::trayToolTipEvent(QHelpEvent *e ) {
310 // kDebug(67100) << "trayToolTipEvent" ;
311 // setVolumeTip();
312 // }
314 void
315 KMixDockWidget::trayWheelEvent(QWheelEvent *e )
317 MixDevice *md = Mixer::getGlobalMasterMD();
318 if ( md != 0 )
320 Volume vol = md->playbackVolume();
321 if ( md->playbackVolume().hasVolume() )
322 vol = md->playbackVolume();
323 else
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);
344 else
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.
349 setVolumeTip();
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 ) );
356 void
357 KMixDockWidget::dockMute()
359 MixDevice *md = Mixer::getGlobalMasterMD();
360 if ( md != 0 ) {
361 md->setMuted( !md->isMuted() );
362 md->mixer()->commitVolumeChange( md );
367 void KMixDockWidget::kmixSystrayAction(QSystemTrayIcon::ActivationReason reason)
369 if ( reason == QSystemTrayIcon::MiddleClick ) {
370 dockMute();
372 else if ( reason == QSystemTrayIcon::Trigger ) {
373 // Move the popup to a suitable place
374 if (_volumePopup)
375 moveVolumePopoup();
379 void
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") );
389 else
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"