You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdemultimedia/kmix/kmixdockwidget.cpp

414 lines
12 KiB

/*
* KMix -- KDE's full featured mini mixer
*
*
* Copyright (C) 2000 Stefan Schimanski <1Stein@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 <kaction.h>
#include <klocale.h>
#include <kapplication.h>
#include <kpanelapplet.h>
#include <kpopupmenu.h>
#include <kglobalsettings.h>
#include <kdialog.h>
#include <kaudioplayer.h>
#include <kiconloader.h>
#include <kdebug.h>
#include <kwin.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 "kwin.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)
{
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, TQT_SIGNAL(quitSelected()), kapp, TQT_SLOT(quitExtended()));
}
KMixDockWidget::~KMixDockWidget()
{
delete _audioPlayer;
delete _dockAreaPopup;
}
void KMixDockWidget::createActions()
{
// Put "Mute" selector in context menu
(void)new KToggleAction( i18n( "M&ute" ), 0, this, TQT_SLOT( dockMute() ),
actionCollection(), "dock_mute" );
KAction *a = actionCollection()->action( "dock_mute" );
KPopupMenu *popupMenu = contextMenu();
if ( a ) a->plug( popupMenu );
// Put "Select Master Channel" dialog in context menu
if ( m_mixer != 0 ) {
(void)new KAction( i18n("Select Master Channel..."), 0, this, TQT_SLOT(selectMaster()),
actionCollection(), "select_master");
KAction *a2 = actionCollection()->action( "select_master" );
if (a2) a2->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
_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, TQT_SIGNAL(newVolumeLevels()), _dockAreaPopup, TQT_SLOT(refreshVolumeLevels()) );
connect( m_mixer, TQT_SIGNAL(newVolumeLevels()), this, TQT_SLOT(setVolumeTip() ) );
connect( m_mixer, TQT_SIGNAL(newVolumeLevels()), this, TQT_SLOT(slotUpdatePixmap() ) );
}
void KMixDockWidget::slotUpdatePixmap()
{
updatePixmap(false);
}
void KMixDockWidget::selectMaster()
{
DialogSelectMaster* dsm = new DialogSelectMaster(m_mixer);
connect ( dsm, TQT_SIGNAL(newMasterSelected(int, TQString&)), TQT_SLOT( handleNewMaster(int,TQString&)) );
dsm->show();
// !! The dialog is modal. Does it delete itself?
}
void KMixDockWidget::handleNewMaster(int soundcard_id, TQString& channel_id) // !! @todo rework parameters
{
//kdDebug(67100) << "KMixDockWidget::handleNewMaster() soundcard_id=" << soundcard_id << " , channel_id=" << channel_id << endl;
Mixer *mixer = Mixer::mixers().at(soundcard_id);
if ( mixer == 0 ) {
kdError(67100) << "KMixDockWidget::createPage(): Invalid Mixer (soundcard_id=" << soundcard_id << ")" << endl;
return; // can not happen
}
m_mixer = mixer;
Mixer::setMasterCard(mixer->id()); // We must save this information "somewhere".
Mixer::setMasterCardDevice( channel_id );
createMasterVolWidget();
}
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 = -1;
if ( md->maxVolume() != 0 ) {
val = (md->getVolume().getAvgVolume(Volume::MMAIN)*100 )/( md->maxVolume() );
}
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
{
newPixmapType = 'd';
}
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;
switch ( newPixmapType ) {
case 'e': origpixmap = loadSizedIcon( "kmixdocked_error", width() ); break;
case 'm': origpixmap = loadSizedIcon( "kmixdocked_mute" , width() ); break;
case 'd': origpixmap = loadSizedIcon( "kmixdocked" , width() ); break;
}
newIcon = origpixmap;
newIcon = newIcon.smoothScale(width(), height());
scaledpixmap = newIcon;
setPixmap(scaledpixmap);
_oldPixmapType = newPixmapType;
}
}
void KMixDockWidget::resizeEvent ( TQResizeEvent * )
{
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() == 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() == 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 (Qt removes it on a MouseWheel event)
// Mhhh, it doesn't work. Qt does not show it again.
setVolumeTip();
// Simulate a mouse move to make Qt show the tooltip again
TQApplication::postEvent( this, new TQMouseEvent( TQEvent::MouseMove, TQCursor::pos(), Qt::NoButton, Qt::NoButton ) );
}
}
void
KMixDockWidget::dockMute()
{
MixDevice *md = 0;
if ( _dockAreaPopup != 0 )
{
md = _dockAreaPopup->dockDevice();
if ( md != 0 ) {
md->setMuted( !md->isMuted() );
m_mixer->commitVolumeChange( md );
}
}
}
void
KMixDockWidget::contextMenuAboutToShow( KPopupMenu* /* menu */ )
{
KAction* 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();
KToggleAction *dockMuteAction = static_cast<KToggleAction*>(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"