kde-extraapps/kmix/gui/kmixdockwidget.cpp

389 lines
13 KiB
C++
Raw Normal View History

/*
* 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) 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 "gui/kmixdockwidget.h"
#include <kaction.h>
#include <klocale.h>
#include <kmenu.h>
#include <kdebug.h>
#include <kwindowsystem.h>
#include <kactioncollection.h>
#include <ktoggleaction.h>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDesktopWidget>
#include <QApplication>
#include <QTextDocument>
#include "apps/kmix.h"
#include "core/ControlManager.h"
#include "core/mixer.h"
#include "core/mixertoolbox.h"
#include "gui/dialogselectmaster.h"
#include "gui/mixdevicewidget.h"
#include "gui/viewdockareapopup.h"
//#define FEATURE_UNITY_POPUP true
KMixDockWidget::KMixDockWidget(KMixWindow* parent)
: KStatusNotifierItem(parent)
, _oldToolTipValue(-1)
, _oldPixmapType('-')
, _kmixMainWindow(parent)
{
setToolTipIconByName("kmix");
setTitle(i18n( "Volume Control"));
setCategory(Hardware);
setStatus(Active);
// TODO Unity / Gnome only support one type of activation (left-click == right-click)
// So we should show here the ViewDockAreaPopup instead of the menu:
//bool onlyOneMouseButtonAction = onlyHaveOneMouseButtonAction();
createMenuActions();
connect(this, SIGNAL(scrollRequested(int,Qt::Orientation)), this, SLOT(trayWheelEvent(int,Qt::Orientation)));
connect(this, SIGNAL(secondaryActivateRequested(QPoint)), this, SLOT(dockMute()));
// For bizarre reasons, we wrap the ViewDockAreaPopup in a KMenu. Must relate to how KStatusNotifierItem works.
_dockAreaPopupMenuWrapper = new KMenu(parent);
_volWA = new QWidgetAction(_dockAreaPopupMenuWrapper);
_dockView = new ViewDockAreaPopup(_dockAreaPopupMenuWrapper, "dockArea", 0, QString("no-guiprofile-yet-in-dock"), parent);
_volWA->setDefaultWidget(_dockView);
_dockAreaPopupMenuWrapper->addAction(_volWA);
connect(contextMenu(), SIGNAL(aboutToShow()), this, SLOT(contextMenuAboutToShow()));
ControlManager::instance().addListener(
QString(), // All mixers (as the Global master Mixer might change)
(ControlChangeType::Type) (ControlChangeType::Volume | ControlChangeType::MasterChanged), this,
QString("KMixDockWidget"));
// Refresh in all cases. When there is no Golbal Master we still need
// to initialize correctly (e.g. for showin 0% or hiding it)
refreshVolumeLevels();
}
KMixDockWidget::~KMixDockWidget()
{
ControlManager::instance().removeListener(this);
// Note: deleting _volWA also deletes its associated ViewDockAreaPopup (_referenceWidget) and prevents the
// action to be left with a dangling pointer.
// cesken: I adapted the patch from https://bugs.kde.org/show_bug.cgi?id=220621#c27 to branch /branches/work/kmix
delete _volWA;
}
void KMixDockWidget::controlsChange(int changeType)
{
ControlChangeType::Type type = ControlChangeType::fromInt(changeType);
switch (type )
{
case ControlChangeType::MasterChanged:
// Notify the main window, as it might need to update the visibiliy of the dock icon.
// _kmixMainWindow->updateDocking();
// _kmixMainWindow->saveConfig();
refreshVolumeLevels();
actionCollection()->action(QLatin1String("select_master"))->setEnabled(Mixer::getGlobalMasterMixer() != 0);
break;
case ControlChangeType::Volume:
refreshVolumeLevels();
break;
default:
ControlManager::warnUnexpectedChangeType(type, this);
}
}
/**
* Updates all visual parts of the volume, namely tooltip and pixmap
*/
void KMixDockWidget::refreshVolumeLevels()
{
setVolumeTip();
updatePixmap();
}
/**
* Creates the right-click menu
*/
void KMixDockWidget::createMenuActions()
{
QMenu *menu = contextMenu();
if ( menu == 0)
return; // We do not use a menu
std::shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD();
if ( md.get() != 0 && md->hasMuteSwitch() ) {
// Put "Mute" selector in context menu
KToggleAction *action = actionCollection()->add<KToggleAction>( "dock_mute" );
updateDockMuteAction(action);
action->setText( i18n( "M&ute" ) );
connect(action, SIGNAL(triggered(bool)), SLOT(dockMute()));
menu->addAction( action );
}
// Put "Select Master Channel" dialog in context menu
QAction *action = actionCollection()->addAction( "select_master" );
action->setText( i18n("Select Master Channel...") );
action->setEnabled(Mixer::getGlobalMasterMixer() != 0);
connect(action, SIGNAL(triggered(bool)), _kmixMainWindow, SLOT(slotSelectMaster()));
menu->addAction( action );
//Context menu entry to access media settings
menu->addAction(_kmixMainWindow->actionCollection()->action("launch_kdesoundsetup"));
}
void
KMixDockWidget::setVolumeTip()
{
std::shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD();
QString tip;
int virtualToolTipValue = 0;
if ( md.get() == 0 )
{
tip = i18n("Mixer cannot be found"); // !! text could be reworked
virtualToolTipValue = -2;
}
else
{
// Playback volume will be used for the DockIcon if available.
// This heuristic is "good enough" for the DockIcon for now.
int val = md->getUserfriendlyVolumeLevel();
tip += "<font size=\"+1\">" + i18n( "Volume at %1%", val ) + "</font>";
if ( md->isMuted() )
tip += i18n( " (Muted)" );
tip += QString( "<br/><font size=\"-1\">%1<br/>%2</font>" )
.arg( Qt::escape(md->mixer()->readableName()) ).arg( Qt::escape(md->readableName()) );
// create a new "virtual" value. With that we see "volume changes" as well as "muted changes"
virtualToolTipValue = val;
if ( md->isMuted() )
virtualToolTipValue += 10000;
}
// The actual updating is only done when the "toolTipValue" was changed (to avoid flicker)
if ( virtualToolTipValue != _oldToolTipValue )
{
// changed (or completely new tooltip)
setToolTipTitle(tip);
}
_oldToolTipValue = virtualToolTipValue;
}
void KMixDockWidget::updatePixmap()
{
std::shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD();
char newPixmapType;
if ( !md )
{
// no such control => error
newPixmapType = 'e';
}
else
{
int percentage = md->getUserfriendlyVolumeLevel();
if ( percentage <= 0 ) newPixmapType = '0'; // Hint: also muted, and also negative-values
else if ( percentage < 25 ) newPixmapType = '1';
else if ( percentage < 75 ) newPixmapType = '2';
else newPixmapType = '3';
}
if ( newPixmapType != _oldPixmapType ) {
// Pixmap must be changed => do so
switch ( newPixmapType ) {
case 'e': setIconByName( "kmixdocked_error" ); break;
case 'm':
case '0': setIconByName( "audio-volume-muted" ); break;
case '1': setIconByName( "audio-volume-low" ); break;
case '2': setIconByName( "audio-volume-medium" ); break;
case '3': setIconByName( "audio-volume-high" ); break;
}
}
_oldPixmapType = newPixmapType;
}
/**
* Called whenever the icon gets "activated". Unusally whn its clicked.
* @overload
* @param pos
*/
void KMixDockWidget::activate(const QPoint &pos)
{
QWidget* dockAreaPopup = _dockAreaPopupMenuWrapper; // TODO Refactor to use _referenceWidget directly
if (dockAreaPopup->isVisible())
{
dockAreaPopup->hide();
return;
}
_dockAreaPopupMenuWrapper->removeAction(_volWA);
delete _volWA;
_volWA = new QWidgetAction(_dockAreaPopupMenuWrapper);
_dockView = new ViewDockAreaPopup(_dockAreaPopupMenuWrapper, "dockArea", 0, QString("no-guiprofile-yet-in-dock"),
_kmixMainWindow);
_volWA->setDefaultWidget(_dockView);
_dockAreaPopupMenuWrapper->addAction(_volWA);
//_dockView->show(); // TODO cesken check: this should be automatic
// Showing, to hopefully get the geometry manager started. We need width and height below. Also
// vdesktop->availableGeometry(dockAreaPopup) needs to know on which screen the widget will be shown.
// dockAreaPopup->show();
_dockView->adjustSize();
dockAreaPopup->adjustSize();
int x = pos.x() - dockAreaPopup->width() / 2;
if (x < 0)
x = pos.x();
int y = pos.y() - dockAreaPopup->height() / 2;
if (y < 0)
y = pos.y();
// Now handle Multihead displays. And also make sure that the dialog is not
// moved out-of-the screen on the right (see Bug 101742).
const QDesktopWidget* vdesktop = QApplication::desktop();
const QRect& vScreenSize = vdesktop->availableGeometry(dockAreaPopup);
if ((x + dockAreaPopup->width()) > (vScreenSize.width() + vScreenSize.x()))
{
// move horizontally, so that it is completely visible
x = vScreenSize.width() + vScreenSize.x() - dockAreaPopup->width() - 1;
kDebug()
<< "Multihead: (case 1) moving to" << vScreenSize.x() << "," << vScreenSize.y();
}
else if (x < vScreenSize.x())
{
// horizontally out-of bound
x = vScreenSize.x();
kDebug() << "Multihead: (case 2) moving to" << vScreenSize.x() << "," << vScreenSize.y();
}
if ((y + dockAreaPopup->height()) > (vScreenSize.height() + vScreenSize.y()))
{
// move horizontally, so that it is completely visible
y = vScreenSize.height() + vScreenSize.y() - dockAreaPopup->height() - 1;
kDebug() << "Multihead: (case 3) moving to" << vScreenSize.x() << "," << vScreenSize.y();
}
else if (y < vScreenSize.y())
{
// horizontally out-of bound
y = vScreenSize.y();
kDebug() << "Multihead: (case 4) moving to" << vScreenSize.x() << "," << vScreenSize.y();
}
KWindowSystem::setType(dockAreaPopup->winId(), NET::Dock);
KWindowSystem::setState(dockAreaPopup->winId(), NET::KeepAbove | NET::SkipTaskbar | NET::SkipPager);
dockAreaPopup->show();
dockAreaPopup->move(x, y);
}
void
KMixDockWidget::trayWheelEvent(int delta,Qt::Orientation wheelOrientation)
{
std::shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD();
if ( md.get() == 0 )
return;
Volume &vol = ( md->playbackVolume().hasVolume() ) ? md->playbackVolume() : md->captureVolume();
// bko313579 Do not use "delta", as that is setting more related to documents (Editor, Browser). KMix should
// simply always use its own VOLUME_STEP_DIVISOR as a base for percentage change.
bool decrease = delta < 0;
if (wheelOrientation == Qt::Horizontal) // Reverse horizontal scroll: bko228780
decrease = !decrease;
long cv = vol.volumeStep(decrease);
bool isInactive = vol.isCapture() ? !md->isRecSource() : md->isMuted();
kDebug() << "Operating on capture=" << vol.isCapture() << ", isInactive=" << isInactive;
if ( cv > 0 && isInactive)
{
// increasing from muted state: unmute and start with a low volume level
if ( vol.isCapture())
md->setRecSource(true);
else
md->setMuted(false);
vol.setAllVolumes(cv);
}
else
vol.changeAllVolumes(cv);
md->mixer()->commitVolumeChange(md);
refreshVolumeLevels();
}
void
KMixDockWidget::dockMute()
{
std::shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD();
if ( md )
{
md->toggleMute();
md->mixer()->commitVolumeChange( md );
refreshVolumeLevels();
}
}
/**
* Returns whether the running Desktop only supports one Mouse Button
* Hint: Unity / Gnome only support one type of activation (left-click == right-click).
*/
bool KMixDockWidget::onlyHaveOneMouseButtonAction()
{
QDBusConnection connection = QDBusConnection::sessionBus();
bool unityIsRunnig = (connection.interface()->isServiceRegistered("com.canonical.Unity.Panel.Service"));
// Possibly implement other detectors, like for Gnome 3 or Gnome 2
return unityIsRunnig;
}
void KMixDockWidget::contextMenuAboutToShow()
{
// Enable/Disable "Muted" menu item
KToggleAction *dockMuteAction = static_cast<KToggleAction*>(actionCollection()->action("dock_mute"));
updateDockMuteAction(dockMuteAction);
}
void KMixDockWidget::updateDockMuteAction ( KToggleAction* dockMuteAction )
{
std::shared_ptr<MixDevice> md = Mixer::getGlobalMasterMD();
if ( md && dockMuteAction != 0 )
{
Volume& vol = md->playbackVolume().hasVolume() ? md->playbackVolume() : md->captureVolume();
bool isInactive = vol.isCapture() ? !md->isRecSource() : md->isMuted();
bool hasSwitch = vol.isCapture() ? vol.hasSwitch() : md->hasMuteSwitch();
dockMuteAction->setEnabled( hasSwitch );
dockMuteAction->setChecked( isInactive );
}
}
2015-02-27 11:02:43 +00:00
#include "moc_kmixdockwidget.cpp"