/* * KMix -- KDE's full featured mini mixer * * * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> * Copyright (C) 2001 Preston Brown * Copyright (C) 2003 Sven Leiber * Copyright (C) 2004 Christian Esken * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dialogselectmaster.h" #include "mixer.h" #include "mixdevicewidget.h" #include "kmixdockwidget.h" #include "twin.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 TDEToggleAction( i18n( "M&ute" ), 0, TQT_TQOBJECT(this), TQT_SLOT( dockMute() ), actionCollection(), "dock_mute" ); TDEAction *a = actionCollection()->action( "dock_mute" ); TDEPopupMenu *popupMenu = contextMenu(); if ( a ) a->plug( popupMenu ); // Put "Select Master Channel" dialog in context menu if ( m_mixer != 0 ) { (void)new TDEAction( i18n("Select Master Channel..."), 0, TQT_TQOBJECT(this), TQT_SLOT(selectMaster()), actionCollection(), "select_master"); TDEAction *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()), TQT_TQOBJECT(this), TQT_SLOT(setVolumeTip() ) ); connect( m_mixer, TQT_SIGNAL(newVolumeLevels()), TQT_TQOBJECT(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 = isShown() ? loadSizedIcon( "kmixdocked_error", width() ) : loadIcon( "kmixdocked_error"); break; case 'm': origpixmap = isShown() ? loadSizedIcon( "kmixdocked_mute" , width() ) : loadIcon( "kmixdocked_mute"); break; case 'd': origpixmap = isShown() ? loadSizedIcon( "kmixdocked" , width() ) : loadIcon( "kmixdocked "); break; } newIcon = origpixmap; if (isShown()) { newIcon = newIcon.smoothScale(width(), height()); } scaledpixmap = newIcon; setPixmap(scaledpixmap); _oldPixmapType = newPixmapType; } } void KMixDockWidget::resizeEvent ( TQResizeEvent * ) { updatePixmap(true); } void KMixDockWidget::showEvent ( TQShowEvent *se ) { updatePixmap(true); KSystemTray::showEvent(se); } 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() == Qt::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() == Qt::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 (TQt removes it on a MouseWheel event) // Mhhh, it doesn't work. TQt does not show it again. setVolumeTip(); // Simulate a mouse move to make TQt 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 ); updatePixmap(false); } } } void KMixDockWidget::contextMenuAboutToShow( TDEPopupMenu* /* menu */ ) { TDEAction* 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(); TDEToggleAction *dockMuteAction = static_cast(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"