~ubuntu-branches/ubuntu/natty/kdemultimedia/natty-proposed

« back to all changes in this revision

Viewing changes to kmix/gui/kmixdockwidget.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Debian Qt/KDE Maintainers
  • Date: 2011-05-26 02:41:36 UTC
  • mfrom: (0.2.3 upstream)
  • mto: This revision was merged to the branch mainline in revision 108.
  • Revision ID: james.westby@ubuntu.com-20110526024136-jjwsigfy402jhupm
Tags: upstream-4.6.3
ImportĀ upstreamĀ versionĀ 4.6.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * KMix -- KDE's full featured mini mixer
 
3
 *
 
4
 *
 
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>
 
8
 *
 
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.
 
13
 *
 
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.
 
18
 *
 
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.
 
22
 */
 
23
 
 
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
 
 
41
#ifdef Q_WS_X11
 
42
#include <fixx11h.h>
 
43
#endif
 
44
 
 
45
#include <Phonon/MediaObject>
 
46
 
 
47
#include "gui/dialogselectmaster.h"
 
48
#include "apps/kmix.h"
 
49
#include "gui/kmixdockwidget.h"
 
50
#include "core/mixer.h"
 
51
#include "gui/mixdevicewidget.h"
 
52
#include "core/mixertoolbox.h"
 
53
#include "gui/viewdockareapopup.h"
 
54
 
 
55
KMixDockWidget::KMixDockWidget(KMixWindow* parent, bool volumePopup)
 
56
    : KStatusNotifierItem(parent)
 
57
  //  : KStatusNotifierItem(0)
 
58
    , _audioPlayer(0L)
 
59
    , _playBeepOnVolumeChange(false) // disabled due to triggering a "Bug"
 
60
    , _oldToolTipValue(-1)
 
61
    , _oldPixmapType('-')
 
62
    , _volumePopup(volumePopup)
 
63
    , _kmixMainWindow(parent)
 
64
    , _contextMenuWasOpen(false)
 
65
{
 
66
    setToolTipIconByName("kmix");
 
67
    setCategory(Hardware);
 
68
    setStatus(Active);
 
69
    m_mixer = Mixer::getGlobalMasterMixer();  // ugly, but we'll live with that for now
 
70
    createMasterVolWidget();
 
71
    createActions();
 
72
    connect(this, SIGNAL(scrollRequested(int,Qt::Orientation)), this, SLOT(trayWheelEvent(int)));
 
73
    connect(this, SIGNAL(secondaryActivateRequested(QPoint)), this, SLOT(dockMute()));
 
74
    connect(this, SIGNAL(activateRequested(bool, QPoint)), this, SLOT(activateMenuOrWindow(bool, QPoint)));
 
75
    connect(contextMenu(), SIGNAL(aboutToShow()), this, SLOT(contextMenuAboutToShow()));
 
76
 
 
77
#ifdef _GNU_SOURCE
 
78
// TODO minimizeRestore usage is currently VERY broken
 
79
#warning minimizeRestore usage is currently VERY broken in KMIx. This must be fixed before doing a release.
 
80
#endif
 
81
 
 
82
    if (_volumePopup) {
 
83
        kDebug() << "Construct the ViewDockAreaPopup and actions";
 
84
        _referenceWidget = new KMenu(parent);
 
85
        ViewDockAreaPopup* _referenceWidget2 = new ViewDockAreaPopup(_referenceWidget, "dockArea", Mixer::getGlobalMasterMixer(), 0, (GUIProfile*)0, parent);
 
86
        _referenceWidget2->createDeviceWidgets();
 
87
 
 
88
        _volWA = new QWidgetAction(_referenceWidget);
 
89
        _volWA->setDefaultWidget(_referenceWidget2);
 
90
        _referenceWidget->addAction(_volWA);
 
91
 
 
92
        //setAssociatedWidget(_referenceWidget);
 
93
        //setAssociatedWidget(_referenceWidget);  // If you use the popup, associate that instead of the MainWindow
 
94
    }
 
95
    else {
 
96
        _volWA = 0;
 
97
        _referenceWidget = 0;
 
98
    }
 
99
}
 
100
 
 
101
 
 
102
KMixDockWidget::~KMixDockWidget()
 
103
{
 
104
    delete _audioPlayer;
 
105
    // Note: deleting _volWA also deletes its associated ViewDockAreaPopup (_referenceWidget) and prevents the
 
106
    //       action to be left with a dangling pointer.
 
107
    //       cesken: I adapted the patch from https://bugs.kde.org/show_bug.cgi?id=220621#c27 to branch /branches/work/kmix 
 
108
    delete _volWA;
 
109
 
 
110
}
 
111
 
 
112
void KMixDockWidget::createActions()
 
113
{
 
114
   QMenu *menu = contextMenu();
 
115
 
 
116
   MixDevice* md = Mixer::getGlobalMasterMD();
 
117
  if ( md != 0 && md->playbackVolume().hasSwitch() ) {
 
118
    // Put "Mute" selector in context menu
 
119
    KToggleAction *action = actionCollection()->add<KToggleAction>( "dock_mute" );
 
120
    action->setText( i18n( "M&ute" ) );
 
121
    connect(action, SIGNAL(triggered(bool) ), SLOT( dockMute() ));
 
122
    menu->addAction( action );
 
123
}
 
124
 
 
125
  // Put "Select Master Channel" dialog in context menu
 
126
  if ( m_mixer != 0 ) {
 
127
  QAction *action = actionCollection()->addAction( "select_master" );
 
128
  action->setText( i18n("Select Master Channel...") );
 
129
  connect(action, SIGNAL(triggered(bool) ), SLOT(selectMaster()));
 
130
  menu->addAction( action );
 
131
  }
 
132
 
 
133
   // Setup volume preview
 
134
  if ( _playBeepOnVolumeChange ) {
 
135
    _audioPlayer = Phonon::createPlayer(Phonon::MusicCategory);
 
136
    _audioPlayer->setParent(this);
 
137
  }
 
138
}
 
139
 
 
140
 
 
141
void
 
142
KMixDockWidget::createMasterVolWidget()
 
143
{
 
144
     // Reset flags, so that the dock icon will be reconstructed
 
145
     _oldToolTipValue = -1;
 
146
     _oldPixmapType   = '-';
 
147
 
 
148
    if (Mixer::getGlobalMasterMD() == 0) {
 
149
        // In case that there is no mixer installed, there will be no controlChanged() signal's
 
150
        // Thus we prepare the dock areas manually
 
151
        setVolumeTip();
 
152
        updatePixmap();
 
153
        return;
 
154
    }
 
155
    // create devices
 
156
 
 
157
    m_mixer->readSetFromHWforceUpdate();  // after changing the master device, make sure to re-read (otherwise no "changed()" signals might get sent by the Mixer
 
158
    /* With the recently introduced QSocketNotifier stuff, we can't rely on regular timer updates
 
159
       any longer. Also the readSetFromHWforceUpdate() won't be enough. As a workaround, we trigger
 
160
       all "repaints" manually here.
 
161
       The call to m_mixer->readSetFromHWforceUpdate() is most likely superfluous, even if we don't use QSocketNotifier (e.g. in backends OSS, Solaris, ...)
 
162
     */
 
163
    setVolumeTip();
 
164
    updatePixmap();
 
165
    /* We are setting up 3 connections:
 
166
     * Refreshig the _dockAreaPopup (not anymore necessary, because ViewBase already does it)
 
167
     * Refreshing the Tooltip
 
168
     * Refreshing the Icon
 
169
     */
 
170
    //    connect( m_mixer, SIGNAL(controlChanged()), _dockAreaPopup, SLOT(refreshVolumeLevels()) );
 
171
    connect( m_mixer, SIGNAL(controlChanged()), this, SLOT(setVolumeTip() ) );
 
172
    connect( m_mixer, SIGNAL(controlChanged()), this, SLOT(updatePixmap() ) );
 
173
}
 
174
 
 
175
void KMixDockWidget::selectMaster()
 
176
{
 
177
   DialogSelectMaster* dsm = new DialogSelectMaster(m_mixer);
 
178
   dsm->setAttribute(Qt::WA_DeleteOnClose, true);
 
179
   connect ( dsm, SIGNAL(newMasterSelected(QString&, QString&)), SLOT( handleNewMaster(QString&, QString&)) );
 
180
   connect ( dsm, SIGNAL(newMasterSelected(QString&, QString&)), SIGNAL( newMasterSelected()) );
 
181
   dsm->show();
 
182
}
 
183
 
 
184
 
 
185
void KMixDockWidget::handleNewMaster(QString& /*mixerID*/, QString& /*control_id*/)
 
186
{
 
187
   /* When a new master is selected, we will need to destroy *this instance of KMixDockWidget.
 
188
      Reason: This widget is a KSystemTrayIcon, and needs an associated QWidget. This is in
 
189
      our case the ViewDockAreaPopup instance. As that is recreated, we need to recrate ourself as well.
 
190
      The only chance to do so is outside, in fact it is done by KMixWindow::updateDocking(). This needs to
 
191
      use deleteLater(), as we are executing in a SLOT currently.
 
192
    */
 
193
   _kmixMainWindow->updateDocking();
 
194
/*
 
195
    Mixer *mixer = MixerToolBox::instance()->find(mixerID);
 
196
    if ( mixer == 0 ) {
 
197
        kError(67100) << "KMixDockWidget::createPage(): Invalid Mixer (mixerID=" << mixerID << ")" << endl;
 
198
        return; // can not happen
 
199
   }
 
200
   m_mixer = mixer;
 
201
   //Mixer::setGlobalMaster(mixer->id(), control_id); // We must save this information "somewhere".
 
202
   createMasterVolWidget();
 
203
*/
 
204
}
 
205
 
 
206
 
 
207
void
 
208
KMixDockWidget::setVolumeTip()
 
209
{
 
210
    MixDevice *md = Mixer::getGlobalMasterMD();
 
211
    QString tip = "";
 
212
    int newToolTipValue = 0;
 
213
 
 
214
    if ( md == 0 )
 
215
    {
 
216
        tip = i18n("Mixer cannot be found"); // !! text could be reworked
 
217
        newToolTipValue = -2;
 
218
    }
 
219
    else
 
220
    {
 
221
        // Playback volume will be used for the DockIcon if available.
 
222
        // This heuristic is "good enough" for the DockIcon for now.
 
223
        long val = 0;
 
224
        Volume& vol       = md->playbackVolume();
 
225
        if (! vol.hasVolume() ) {
 
226
           vol = md->captureVolume();
 
227
        }
 
228
        if ( vol.hasVolume() && vol.maxVolume() != 0 ) {
 
229
            val = (vol.getAvgVolume(Volume::MMAIN)*100 )/( vol.maxVolume() );
 
230
        }
 
231
 
 
232
        // create a new "virtual" value. With that we see "volume changes" as well as "muted changes"
 
233
        newToolTipValue = val;
 
234
        if ( md->isMuted() ) newToolTipValue += 10000;
 
235
        if ( _oldToolTipValue != newToolTipValue ) {
 
236
            tip = i18n( "Volume at %1%", val );
 
237
            if ( md->playbackVolume().hasSwitch() && md->isMuted() ) {
 
238
                tip += i18n( " (Muted)" );
 
239
            }
 
240
        }
 
241
    }
 
242
 
 
243
    // The actual updating is only done when the "toolTipValue" was changed
 
244
    if ( newToolTipValue != _oldToolTipValue ) {
 
245
        // changed (or completely new tooltip)
 
246
        setToolTipTitle(tip);
 
247
    }
 
248
    _oldToolTipValue = newToolTipValue;
 
249
}
 
250
 
 
251
void
 
252
KMixDockWidget::updatePixmap()
 
253
{
 
254
    MixDevice *md = Mixer::getGlobalMasterMD();
 
255
 
 
256
    char newPixmapType;
 
257
    if ( md == 0 )
 
258
    {
 
259
        newPixmapType = 'e';
 
260
    }
 
261
    else if ( md->playbackVolume().hasSwitch() && md->isMuted() )
 
262
    {
 
263
        newPixmapType = 'm';
 
264
    }
 
265
    else
 
266
    {
 
267
        Volume& vol = md->playbackVolume();
 
268
        if (! vol.hasVolume() ) {
 
269
            vol = md->captureVolume();
 
270
        }
 
271
        long absoluteVolume    = vol.getAvgVolume(Volume::MALL);
 
272
        int percentage         = vol.percentage(absoluteVolume);
 
273
        if      ( percentage < 25 ) newPixmapType = '1';  // Hint: also negative-values
 
274
        else if ( percentage < 75 ) newPixmapType = '2';
 
275
        else                        newPixmapType = '3';
 
276
   }
 
277
 
 
278
 
 
279
   if ( newPixmapType != _oldPixmapType ) {
 
280
      // Pixmap must be changed => do so
 
281
      switch ( newPixmapType ) {
 
282
         case 'e': setIconByName( "kmixdocked_error" ); break;
 
283
         case 'm': setIconByName( "audio-volume-muted"  ); break;
 
284
         case '1': setIconByName( "audio-volume-low"  ); break;
 
285
         case '2': setIconByName( "audio-volume-medium" ); break;
 
286
         case '3': setIconByName( "audio-volume-high" ); break;
 
287
      }
 
288
   }
 
289
 
 
290
   _oldPixmapType = newPixmapType;
 
291
}
 
292
 
 
293
 
 
294
 
 
295
void KMixDockWidget::activateMenuOrWindow(bool val, const QPoint &pos)
 
296
{
 
297
    kDebug() << "activateMenuOrWindow: " << val << "," << pos;
 
298
}
 
299
 
 
300
 
 
301
void KMixDockWidget::activate(const QPoint &pos)
 
302
{
 
303
    kDebug() << "Activate at " << pos;
 
304
 
 
305
    bool showHideMainWindow = false;
 
306
    showHideMainWindow |= (_referenceWidget == 0);
 
307
    showHideMainWindow |= (pos.x() == 0  && pos.y() == 0);  // HACK. When the action comes from the context menu, the pos is (0,0)
 
308
 
 
309
    if ( showHideMainWindow ) {
 
310
        // Use default KStatusNotifierItem behavior if we are not using the dockAreaPopup
 
311
        kDebug() << "Use default KStatusNotifierItem behavior";
 
312
        setAssociatedWidget(_kmixMainWindow);
 
313
        KStatusNotifierItem::activate(pos);
 
314
        return;
 
315
    }
 
316
 
 
317
    KMenu* dockAreaPopup =_referenceWidget; // TODO Refactor to use _referenceWidget directly
 
318
    kDebug() << "Skip default KStatusNotifierItkdebem behavior";
 
319
    if ( dockAreaPopup->isVisible() ) {
 
320
        dockAreaPopup->hide();
 
321
        kDebug() << "dap is visible => hide and return";
 
322
        return;
 
323
    }
 
324
 
 
325
//    if (dockAreaPopup->isVisible()) {
 
326
//        contextMenu()->hide();
 
327
//        setAssociatedWidget(_kmixMainWindow);
 
328
//        KStatusNotifierItem::activate(pos);
 
329
//        kDebug() << "cm is visible => setAssociatedWidget(_kmixMainWindow)";
 
330
//        return;
 
331
//    }
 
332
    if ( false ) {}
 
333
    else {
 
334
        setAssociatedWidget(_referenceWidget);
 
335
        kDebug() << "cm is NOT visible => setAssociatedWidget(_referenceWidget)";
 
336
 
 
337
        dockAreaPopup->adjustSize();
 
338
        int h = dockAreaPopup->height();
 
339
        int x = pos.x() - dockAreaPopup->width()/2;
 
340
        int y = pos.y() - h;
 
341
 
 
342
        // kDebug() << "h="<<h<< " x="<<x << " y="<<y<< "gx="<< geometry().x() << "gy="<< geometry().y();
 
343
 
 
344
        if ( y < 0 ) {
 
345
            y = pos.y();
 
346
        }
 
347
 
 
348
        dockAreaPopup->move(x, y);  // so that the mouse is outside of the widget
 
349
        kDebug() << "moving to" << dockAreaPopup->size() << x << y;
 
350
 
 
351
        dockAreaPopup->show();
 
352
 
 
353
        // Now handle Multihead displays. And also make sure that the dialog is not
 
354
        // moved out-of-the screen on the right (see Bug 101742).
 
355
        const QDesktopWidget* vdesktop = QApplication::desktop();
 
356
        const QRect& vScreenSize = vdesktop->screenGeometry(dockAreaPopup);
 
357
        //const QRect screenGeometry(const QWidget *widget) const
 
358
        if ( (x+dockAreaPopup->width()) > (vScreenSize.width() + vScreenSize.x()) ) {
 
359
            // move horizontally, so that it is completely visible
 
360
            dockAreaPopup->move(vScreenSize.width() + vScreenSize.x() - dockAreaPopup->width() -1 , y);
 
361
            kDebug() << "Multihead: (case 1) moving to" << vScreenSize.x() << "," << vScreenSize.y();
 
362
        }
 
363
        else if ( x < vScreenSize.x() ) {
 
364
            // horizontally out-of bound
 
365
            dockAreaPopup->move(vScreenSize.x(), y);
 
366
            kDebug() << "Multihead: (case 2) moving to" << vScreenSize.x() << "," << vScreenSize.y();
 
367
        }
 
368
        // the above stuff could also be implemented vertically
 
369
 
 
370
        KWindowSystem::setState( dockAreaPopup->winId(), NET::StaysOnTop | NET::SkipTaskbar | NET::SkipPager );
 
371
    }
 
372
}
 
373
 
 
374
// void
 
375
// KMixDockWidget::trayToolTipEvent(QHelpEvent *e ) {
 
376
//    kDebug(67100) << "trayToolTipEvent" ;
 
377
//    setVolumeTip();
 
378
// }
 
379
 
 
380
void
 
381
KMixDockWidget::trayWheelEvent(int delta)
 
382
{
 
383
  MixDevice *md = Mixer::getGlobalMasterMD();
 
384
  if ( md != 0 )
 
385
  {
 
386
      Volume vol = md->playbackVolume();
 
387
      if ( md->playbackVolume().hasVolume() )
 
388
         vol = md->playbackVolume();
 
389
      else
 
390
         vol = md->captureVolume();
 
391
 
 
392
      int inc = vol.maxVolume() / 20;
 
393
 
 
394
    if ( inc < 1 ) inc = 1;
 
395
 
 
396
    for ( int i = 0; i < vol.count(); i++ ) {
 
397
        int newVal = vol[i] + (inc * (delta / 120));
 
398
        if( newVal < 0 ) newVal = 0;
 
399
        vol.setVolume( (Volume::ChannelID)i, newVal < vol.maxVolume() ? newVal : vol.maxVolume() );
 
400
    }
 
401
 
 
402
    if ( _playBeepOnVolumeChange ) {
 
403
        QString fileName("KDE_Beep_Digital_1.ogg");
 
404
        _audioPlayer->setCurrentSource(fileName);
 
405
        _audioPlayer->play();
 
406
    }
 
407
      if ( md->playbackVolume().hasVolume() )
 
408
         md->playbackVolume().setVolume(vol);
 
409
      else
 
410
         md->captureVolume().setVolume(vol);
 
411
    m_mixer->commitVolumeChange(md);
 
412
    setVolumeTip();
 
413
  }
 
414
}
 
415
 
 
416
 
 
417
void
 
418
KMixDockWidget::dockMute()
 
419
{
 
420
   MixDevice *md = Mixer::getGlobalMasterMD();
 
421
   if ( md != 0 ) {
 
422
      md->setMuted( !md->isMuted() );
 
423
      md->mixer()->commitVolumeChange( md );
 
424
   }
 
425
}
 
426
 
 
427
void
 
428
KMixDockWidget::contextMenuAboutToShow()
 
429
{
 
430
   // KStatusNotifierItem::contextMenuAboutToShow();
 
431
    /*
 
432
    kDebug() << "<<< mm 1";
 
433
    QAction* showAction = actionCollection()->action("minimizeRestore");
 
434
    kDebug() << "<<< mm 2";
 
435
    if ( parent() && showAction )
 
436
    {
 
437
        if ( ((QWidget*)parent())->isVisible() ) // TODO isVisible() is not good enough here
 
438
        {
 
439
            showAction->setText( i18n("Hide Mixer Window") );
 
440
        }
 
441
        else
 
442
        {
 
443
            showAction->setText( i18n("Show Mixer Window") );
 
444
        }
 
445
        kDebug() << "<<< mm 3";
 
446
        //disconnect(showAction, 0, 0, 0);
 
447
        //connect(showAction, SIGNAL(triggered(bool), this, hideOrShowMainWindow() );
 
448
    }
 
449
    */
 
450
 
 
451
    // Enable/Disable "Muted" menu item
 
452
    MixDevice* md = Mixer::getGlobalMasterMD();
 
453
    KToggleAction *dockMuteAction = static_cast<KToggleAction*>(actionCollection()->action("dock_mute"));
 
454
    kDebug(67100) << "---> md=" << md << "dockMuteAction=" << dockMuteAction << "isMuted=" << md->isMuted();
 
455
    if ( md != 0 && dockMuteAction != 0 ) {
 
456
        bool hasSwitch = md->playbackVolume().hasSwitch();
 
457
        dockMuteAction->setEnabled( hasSwitch );
 
458
        dockMuteAction->setChecked( hasSwitch && md->isMuted() );
 
459
    }
 
460
    _contextMenuWasOpen = true;
 
461
}
 
462
 
 
463
#include "kmixdockwidget.moc"