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>
45
#include <Phonon/MediaObject>
47
#include "dialogselectmaster.h"
49
#include "kmixdockwidget.h"
51
#include "mixdevicewidget.h"
52
#include "mixertoolbox.h"
53
#include "viewdockareapopup.h"
55
KMixDockWidget::KMixDockWidget(KMixWindow* parent, QWidget *referenceWidget, bool volumePopup)
56
: KStatusNotifierItem(parent)
57
, _referenceWidget(referenceWidget)
59
, _playBeepOnVolumeChange(false) // disabled due to triggering a "Bug"
60
, _oldToolTipValue(-1)
62
, _volumePopup(volumePopup)
63
, _kmixMainWindow(parent)
65
setToolTipIconByName("kmix");
66
setCategory(Hardware);
68
m_mixer = Mixer::getGlobalMasterMixer(); // ugly, but we'll live with that for now
69
createMasterVolWidget();
71
connect(this, SIGNAL(scrollRequested(int,Qt::Orientation)), this, SLOT(trayWheelEvent(int)));
72
connect(this, SIGNAL(secondaryActivateRequested(QPoint)), this, SLOT(dockMute()));
73
connect(contextMenu(), SIGNAL(aboutToShow()), this, SLOT(contextMenuAboutToShow()));
75
// TODO remove this, as in a KMenu it does not need window tricks
76
// ViewDockAreaPopup* dockAreaPopup = qobject_cast<ViewDockAreaPopup*>(referenceWidget);
77
// if ( dockAreaPopup ) {
78
// KWindowSystem::setState( dockAreaPopup->winId(), NET::StaysOnTop | NET::SkipTaskbar | NET::SkipPager );
82
KMixDockWidget::~KMixDockWidget()
87
void KMixDockWidget::createActions()
89
QMenu *menu = contextMenu();
91
MixDevice* md = Mixer::getGlobalMasterMD();
92
if ( md != 0 && md->playbackVolume().hasSwitch() ) {
93
// Put "Mute" selector in context menu
94
KToggleAction *action = actionCollection()->add<KToggleAction>( "dock_mute" );
95
action->setText( i18n( "M&ute" ) );
96
connect(action, SIGNAL(triggered(bool) ), SLOT( dockMute() ));
97
menu->addAction( action );
100
// Put "Select Master Channel" dialog in context menu
101
if ( m_mixer != 0 ) {
102
QAction *action = actionCollection()->addAction( "select_master" );
103
action->setText( i18n("Select Master Channel...") );
104
connect(action, SIGNAL(triggered(bool) ), SLOT(selectMaster()));
105
menu->addAction( action );
108
// Setup volume preview
109
if ( _playBeepOnVolumeChange ) {
110
_audioPlayer = Phonon::createPlayer(Phonon::MusicCategory);
111
_audioPlayer->setParent(this);
117
KMixDockWidget::createMasterVolWidget()
119
// Reset flags, so that the dock icon will be reconstructed
120
_oldToolTipValue = -1;
121
_oldPixmapType = '-';
123
if (Mixer::getGlobalMasterMD() == 0) {
124
// In case that there is no mixer installed, there will be no controlChanged() signal's
125
// Thus we prepare the dock areas manually
132
m_mixer->readSetFromHWforceUpdate(); // after changing the master device, make sure to re-read (otherwise no "changed()" signals might get sent by the Mixer
133
/* With the recently introduced QSocketNotifier stuff, we can't rely on regular timer updates
134
any longer. Also the readSetFromHWforceUpdate() won't be enough. As a workaround, we trigger
135
all "repaints" manually here.
136
The call to m_mixer->readSetFromHWforceUpdate() is most likely superfluous, even if we don't use QSocketNotifier (e.g. in backends OSS, Solaris, ...)
140
/* We are setting up 3 connections:
141
* Refreshig the _dockAreaPopup (not anymore necessary, because ViewBase already does it)
142
* Refreshing the Tooltip
143
* Refreshing the Icon
145
// connect( m_mixer, SIGNAL(controlChanged()), _dockAreaPopup, SLOT(refreshVolumeLevels()) );
146
connect( m_mixer, SIGNAL(controlChanged()), this, SLOT(setVolumeTip() ) );
147
connect( m_mixer, SIGNAL(controlChanged()), this, SLOT(updatePixmap() ) );
150
void KMixDockWidget::selectMaster()
152
DialogSelectMaster* dsm = new DialogSelectMaster(m_mixer);
153
connect ( dsm, SIGNAL(newMasterSelected(QString&, QString&)), SLOT( handleNewMaster(QString&, QString&)) );
154
connect ( dsm, SIGNAL(newMasterSelected(QString&, QString&)), SIGNAL( newMasterSelected()) );
156
// !! The dialog is modal. Does it delete itself?
160
void KMixDockWidget::handleNewMaster(QString& /*mixerID*/, QString& /*control_id*/)
162
/* When a new master is selected, we will need to destroy *this instance of KMixDockWidget.
163
Reason: This widget is a KSystemTrayIcon, and needs an associated QWidget. This is in
164
our case the ViewDockAreaPopup instance. As that is recreated, we need to recrate ourself as well.
165
The only chance to do so is outside, in fact it is done by KMixWindow::updateDocking(). This needs to
166
use deleteLater(), as we are executing in a SLOT currently.
168
_kmixMainWindow->updateDocking();
170
Mixer *mixer = MixerToolBox::instance()->find(mixerID);
172
kError(67100) << "KMixDockWidget::createPage(): Invalid Mixer (mixerID=" << mixerID << ")" << endl;
173
return; // can not happen
176
//Mixer::setGlobalMaster(mixer->id(), control_id); // We must save this information "somewhere".
177
createMasterVolWidget();
183
KMixDockWidget::setVolumeTip()
185
MixDevice *md = Mixer::getGlobalMasterMD();
187
int newToolTipValue = 0;
191
tip = i18n("Mixer cannot be found"); // !! text could be reworked
192
newToolTipValue = -2;
196
// Playback volume will be used for the DockIcon if available.
197
// This heuristic is "good enough" for the DockIcon for now.
199
Volume& vol = md->playbackVolume();
200
if (! vol.hasVolume() ) {
201
vol = md->captureVolume();
203
if ( vol.hasVolume() && vol.maxVolume() != 0 ) {
204
val = (vol.getAvgVolume(Volume::MMAIN)*100 )/( vol.maxVolume() );
207
// create a new "virtual" value. With that we see "volume changes" as well as "muted changes"
208
newToolTipValue = val;
209
if ( md->isMuted() ) newToolTipValue += 10000;
210
if ( _oldToolTipValue != newToolTipValue ) {
211
tip = i18n( "Volume at %1%", val );
212
if ( md->playbackVolume().hasSwitch() && md->isMuted() ) {
213
tip += i18n( " (Muted)" );
218
// The actual updating is only done when the "toolTipValue" was changed
219
if ( newToolTipValue != _oldToolTipValue ) {
220
// changed (or completely new tooltip)
221
setToolTipTitle(tip);
223
_oldToolTipValue = newToolTipValue;
227
KMixDockWidget::updatePixmap()
229
MixDevice *md = Mixer::getGlobalMasterMD();
236
else if ( md->playbackVolume().hasSwitch() && md->isMuted() )
242
Volume& vol = md->playbackVolume();
243
if (! vol.hasVolume() ) {
244
vol = md->captureVolume();
246
long absoluteVolume = vol.getAvgVolume(Volume::MALL);
247
int percentage = vol.percentage(absoluteVolume);
248
if ( percentage < 25 ) newPixmapType = '1'; // Hint: also negative-values
249
else if ( percentage < 75 ) newPixmapType = '2';
250
else newPixmapType = '3';
254
if ( newPixmapType != _oldPixmapType ) {
255
// Pixmap must be changed => do so
256
switch ( newPixmapType ) {
257
case 'e': setIconByName( "kmixdocked_error" ); break;
258
case 'm': setIconByName( "audio-volume-muted" ); break;
259
case '1': setIconByName( "audio-volume-low" ); break;
260
case '2': setIconByName( "audio-volume-medium" ); break;
261
case '3': setIconByName( "audio-volume-high" ); break;
265
_oldPixmapType = newPixmapType;
270
void KMixDockWidget::activate(const QPoint &pos)
272
KMenu* dockAreaPopup = qobject_cast<KMenu*>(_referenceWidget);
273
if ( !dockAreaPopup ) {
274
// Use default KStatusNotifierItem behavior if we are not using the
276
KStatusNotifierItem::activate(pos);
279
if ( dockAreaPopup->isVisible() ) {
280
dockAreaPopup->hide();
284
if (contextMenu()->isVisible()) {
285
contextMenu()->hide();
288
dockAreaPopup->adjustSize();
289
int h = dockAreaPopup->height();
290
int x = pos.x() - dockAreaPopup->width()/2;
293
// kDebug() << "h="<<h<< " x="<<x << " y="<<y<< "gx="<< geometry().x() << "gy="<< geometry().y();
299
dockAreaPopup->move(x, y); // so that the mouse is outside of the widget
300
kDebug() << "moving to" << dockAreaPopup->size() << x << y;
302
dockAreaPopup->show();
304
// Now handle Multihead displays. And also make sure that the dialog is not
305
// moved out-of-the screen on the right (see Bug 101742).
306
const QDesktopWidget* vdesktop = QApplication::desktop();
307
const QRect& vScreenSize = vdesktop->screenGeometry(dockAreaPopup);
308
//const QRect screenGeometry(const QWidget *widget) const
309
if ( (x+dockAreaPopup->width()) > (vScreenSize.width() + vScreenSize.x()) ) {
310
// move horizontally, so that it is completely visible
311
dockAreaPopup->move(vScreenSize.width() + vScreenSize.x() - dockAreaPopup->width() -1 , y);
313
else if ( x < vScreenSize.x() ) {
314
// horizontally out-of bound
315
dockAreaPopup->move(vScreenSize.x(), y);
317
// the above stuff could also be implemented vertically
319
KWindowSystem::setState( dockAreaPopup->winId(), NET::StaysOnTop | NET::SkipTaskbar | NET::SkipPager );
323
// KMixDockWidget::trayToolTipEvent(QHelpEvent *e ) {
324
// kDebug(67100) << "trayToolTipEvent" ;
329
KMixDockWidget::trayWheelEvent(int delta)
331
MixDevice *md = Mixer::getGlobalMasterMD();
334
Volume vol = md->playbackVolume();
335
if ( md->playbackVolume().hasVolume() )
336
vol = md->playbackVolume();
338
vol = md->captureVolume();
340
int inc = vol.maxVolume() / 20;
342
if ( inc < 1 ) inc = 1;
344
for ( int i = 0; i < vol.count(); i++ ) {
345
int newVal = vol[i] + (inc * (delta / 120));
346
if( newVal < 0 ) newVal = 0;
347
vol.setVolume( (Volume::ChannelID)i, newVal < vol.maxVolume() ? newVal : vol.maxVolume() );
350
if ( _playBeepOnVolumeChange ) {
351
QString fileName("KDE_Beep_Digital_1.ogg");
352
_audioPlayer->setCurrentSource(fileName);
353
_audioPlayer->play();
355
if ( md->playbackVolume().hasVolume() )
356
md->playbackVolume().setVolume(vol);
358
md->captureVolume().setVolume(vol);
359
m_mixer->commitVolumeChange(md);
365
KMixDockWidget::dockMute()
367
MixDevice *md = Mixer::getGlobalMasterMD();
369
md->setMuted( !md->isMuted() );
370
md->mixer()->commitVolumeChange( md );
375
KMixDockWidget::contextMenuAboutToShow()
377
QAction* showAction = actionCollection()->action("minimizeRestore");
378
if ( associatedWidget() && showAction )
380
if ( associatedWidget()->isVisible() )
382
showAction->setText( i18n("Hide Mixer Window") );
386
showAction->setText( i18n("Show Mixer Window") );
390
// Enable/Disable "Muted" menu item
391
MixDevice* md = Mixer::getGlobalMasterMD();
392
KToggleAction *dockMuteAction = static_cast<KToggleAction*>(actionCollection()->action("dock_mute"));
393
//kDebug(67100) << "---> md=" << md << "dockMuteAction=" << dockMuteAction << "isMuted=" << md->isMuted();
394
if ( md != 0 && dockMuteAction != 0 ) {
395
bool hasSwitch = md->playbackVolume().hasSwitch();
396
dockMuteAction->setEnabled( hasSwitch );
397
dockMuteAction->setChecked( hasSwitch && md->isMuted() );
401
#include "kmixdockwidget.moc"