Commit ba21e699 authored by Jonathan Marten's avatar Jonathan Marten
Browse files

Fix naming and management of global shortcuts

Correctly separate the internal action name and the user readable text,
so that they show up with readable names in the Global Shortcuts KCM.

Only create volume global shortcuts if the control has adjustable
(playback or capture) volume.

Do not create any shortcuts for a virtual or dynamic control, and do not
show the shortcuts configuration dialogue if it would be empty.

Replace protected MixDeviceWidget data members with access functions.
parent 7080e7d1
......@@ -48,18 +48,12 @@ MDWEnum::MDWEnum(shared_ptr<MixDevice> md, MixDeviceWidget::MDWFlags flags, View
_label(nullptr),
_enumCombo(nullptr)
{
// create actions (on _mdwActions, see MixDeviceWidget)
// KStandardAction::showMenubar() is in MixDeviceWidget now
KToggleAction *action = _mdwActions->add<KToggleAction>( "hide" );
KToggleAction *action = channelActions()->add<KToggleAction>("hide");
action->setText( i18n("&Hide") );
connect(action, SIGNAL(triggered(bool)), SLOT(setDisabled(bool)));
QAction *c = _mdwActions->addAction( "keys" );
c->setText( i18n("C&onfigure Shortcuts...") );
connect(c, SIGNAL(triggered(bool)), SLOT(defineKeys()));
// create widgets
createWidgets();
createShortcutsAction();
}
......
......@@ -41,7 +41,6 @@ public:
MDWEnum(shared_ptr<MixDevice> md, MixDeviceWidget::MDWFlags flags, ViewBase *view, ProfControl *pctl = nullptr);
virtual ~MDWEnum() = default;
void addActionToPopup( QAction *action );
QSizePolicy sizePolicy() const;
int labelExtentHint() const override;
void setLabelExtent(int extent) override;
......
......@@ -75,7 +75,8 @@ MDWSlider::MDWSlider(shared_ptr<MixDevice> md, MixDeviceWidget::MDWFlags flags,
createActions();
createWidgets();
createShortcutActions();
createGlobalActions();
createShortcutsAction();
// Yes, this looks odd - monitor all events sent to myself by myself?
// But it's so that wheel events over the MDWSlider background can be
......@@ -96,27 +97,27 @@ MDWSlider::~MDWSlider()
void MDWSlider::createActions()
{
// create actions (on _mdwActions, see MixDeviceWidget)
KToggleAction *taction = _mdwActions->add<KToggleAction>( "stereo" );
// create actions (in channelActions(), see MixDeviceWidget)
KToggleAction *taction = channelActions()->add<KToggleAction>( "stereo" );
taction->setText( i18n("Split Channels") );
connect( taction, SIGNAL(triggered(bool)), SLOT(toggleStereoLinked()) );
// QAction *action;
// if ( ! mixDevice()->mixer()->isDynamic() ) {
// action = _mdwActions->add<KToggleAction>( "hide" );
// action = channelActions()->add<KToggleAction>( "hide" );
// action->setText( i18n("&Hide") );
// connect( action, SIGNAL(triggered(bool)), SLOT(setDisabled(bool)) );
// }
if( mixDevice()->hasMuteSwitch() )
{
taction = _mdwActions->add<KToggleAction>( "mute" );
taction = channelActions()->add<KToggleAction>( "mute" );
taction->setText( i18n("Mute") );
connect( taction, SIGNAL(toggled(bool)), SLOT(toggleMuted()) );
}
if( mixDevice()->captureVolume().hasSwitch() ) {
taction = _mdwActions->add<KToggleAction>( "recsrc" );
taction = channelActions()->add<KToggleAction>( "recsrc" );
taction->setText( i18n("Capture") );
connect( taction, SIGNAL(toggled(bool)), SLOT(toggleRecsrc()) );
}
......@@ -125,64 +126,76 @@ void MDWSlider::createActions()
m_moveMenu = new QMenu( i18n("Use Device"), this);
connect( m_moveMenu, SIGNAL(aboutToShow()), SLOT(showMoveMenu()) );
}
QAction* qaction = _mdwActions->addAction( "keys" );
qaction->setText( i18n("Channel Shortcuts...") );
connect( qaction, SIGNAL(triggered(bool)), SLOT(defineKeys()) );
}
void MDWSlider::addGlobalShortcut(QAction* qaction, const QString& label, bool dynamicControl)
{
QString finalLabel(label);
finalLabel += " - " + mixDevice()->readableName() + ", " + mixDevice()->mixer()->readableName();
qaction->setText(label);
if (!dynamicControl)
void MDWSlider::createGlobalActions()
{
const shared_ptr<MixDevice> md = mixDevice();
const Mixer *mixer = md->mixer();
// I don't understand the former logic here. This variable and the comment
// in MDWSlider::addGlobalShortcut() say that there are no shortcuts
// assigned for a virtual or dynamic control - which makes sense. However,
// for such a control an action was created but its triggered() signal was
// never connected to anything - so even if the action was accessible from
// the GUI it would have done nothing.
//
// Possibly what was intended was that the shortcut should be local to KMix.
// However, that doesn't make sense either - in MixDeviceWidget::defineKeys()
// (which is now MixDeviceWidget::configureShortcuts()), only the global
// shortcuts are allowed to be edited.
//
// In order to keep things simple, do not assign any shortcuts for a
// virtual/dynamic control.
if (mixer->isDynamic()) return;
// The following actions are created for the desktop "Global Shortcuts" settings.
// It would appear that the discussion and caution below was only required
// for KDE4 and has been fixed in KF5.
//
// Note that global shortcuts are saved with the name as set with QAction::setText(),
// instead of their internal action name which is the QObject::objectName().
// This is a bug according to the kde-core-devel thread "Global shortcuts are saved with
// their text-name and not..." at https://marc.info/?t=119901891400001&r=1&w=2
//
// Work around this by setting a text that is unique, but still readable for the user.
// A suffix string to identify the control in the global shortcut configuration.
// This should not be I18N'ed so that the assignment is independent of the
// display language.
const QString actionSuffix = QString("%1@%2").arg(md->readableName(), mixer->readableName());
// -3- MUTE VOLUME SHORTCUT -----------------------------------------
QAction *act = new QAction(i18nc("1=device name, 2=control name",
"Mute %2 on %1",
mixer->readableName(), md->readableName()), this);
globalActions()->addAction(QString("toggle-mute-")+actionSuffix, act);
KGlobalAccel::setGlobalShortcut(act, QKeySequence());
connect(act, &QAction::triggered, this, &MDWSlider::toggleMuted);
// Only create volume actions if there are either any playback or capture
// sliders present. It is assumed that there cannot be both.
if (m_slidersPlayback.count()!=0 || m_slidersCapture.count()!=0)
{
// virtual / dynamic controls won't get shortcuts
// #ifdef __GNUC__
// #warning GLOBAL SHORTCUTS ARE NOW ASSIGNED TO ALL CONTROLS, as enableGlobalShortcut(), has not been committed
// #endif
// b->enableGlobalShortcut();
// enableGlobalShortcut() is not there => use workaround
KGlobalAccel::setGlobalShortcut(qaction, QKeySequence());
// -1- INCREASE VOLUME SHORTCUT -----------------------------------------
act = new QAction(i18nc("1=device name, 2=control name",
"Increase volume of %2 on %1",
mixer->readableName(), md->readableName()), this);
globalActions()->addAction(QString("increase-volume-")+actionSuffix, act);
KGlobalAccel::setGlobalShortcut(act, QKeySequence());
connect(act, &QAction::triggered, this, &MDWSlider::increaseVolume);
// -2- DECREASE VOLUME SHORTCUT -----------------------------------------
act = new QAction(i18nc("1=device name, 2=control name",
"Decrease volume of %2 on %1",
mixer->readableName(), md->readableName()), this);
globalActions()->addAction(QString("decrease-volume-")+actionSuffix, act);
KGlobalAccel::setGlobalShortcut(act, QKeySequence());
connect(act, &QAction::triggered, this, &MDWSlider::decreaseVolume);
}
}
void MDWSlider::createShortcutActions()
{
bool dynamicControl = mixDevice()->mixer()->isDynamic();
// The following actions are for the "Configure Shortcuts" dialog
/* PLEASE NOTE THAT global shortcuts are saved with the name as set with setName(), instead of their action name.
This is a bug according to the thread "Global shortcuts are saved with their text-name and not their action-name - Bug?" on kcd.
I work around this by using a text with setText() that is unique, but still readable to the user.
*/
QString actionSuffix = QString(" - %1, %2").arg( mixDevice()->readableName(), mixDevice()->mixer()->readableName() );
QAction *bi, *bd, *bm;
// -1- INCREASE VOLUME SHORTCUT -----------------------------------------
bi = _mdwPopupActions->addAction( QString("Increase volume %1").arg( actionSuffix ) );
QString increaseVolumeName = i18n( "Increase Volume" );
addGlobalShortcut(bi, increaseVolumeName, dynamicControl);
if ( ! dynamicControl )
connect( bi, SIGNAL(triggered(bool)), SLOT(increaseVolume()) );
// -2- DECREASE VOLUME SHORTCUT -----------------------------------------
bd = _mdwPopupActions->addAction( QString("Decrease volume %1").arg( actionSuffix ) );
QString decreaseVolumeName = i18n( "Decrease Volume" );
addGlobalShortcut(bd, decreaseVolumeName, dynamicControl);
if ( ! dynamicControl )
connect(bd, SIGNAL(triggered(bool)), SLOT(decreaseVolume()));
// -3- MUTE VOLUME SHORTCUT -----------------------------------------
bm = _mdwPopupActions->addAction( QString("Toggle mute %1").arg( actionSuffix ) );
QString muteVolumeName = i18n( "Toggle Mute" );
addGlobalShortcut(bm, muteVolumeName, dynamicControl);
if ( ! dynamicControl )
connect( bm, SIGNAL(triggered(bool)), SLOT(toggleMuted()) );
}
QSizePolicy MDWSlider::sizePolicy() const
{
......@@ -963,7 +976,7 @@ void MDWSlider::increaseVolume()
}
/**
* This slot is called on a Keyboard Shortcut event, except for the XF86Audio* shortcuts which hare handled by the
* This slot is called on a Keyboard Shortcut event, except for the XF86Audio* shortcuts which are handled by the
* KMixWindow class. So for 99.9% of all users, this method is never called.
*/
void MDWSlider::decreaseVolume()
......@@ -1138,7 +1151,7 @@ void MDWSlider::showContextMenu(const QPoint &pos)
}
if ( m_slidersPlayback.count()>1 || m_slidersCapture.count()>1) {
KToggleAction *stereo = qobject_cast<KToggleAction *>(_mdwActions->action("stereo"));
KToggleAction *stereo = qobject_cast<KToggleAction *>(channelActions()->action("stereo"));
if (stereo!=nullptr) {
QSignalBlocker blocker(stereo);
stereo->setChecked(!isStereoLinked());
......@@ -1147,7 +1160,7 @@ void MDWSlider::showContextMenu(const QPoint &pos)
}
if ( mixDevice()->captureVolume().hasSwitch() ) {
KToggleAction *ta = qobject_cast<KToggleAction *>(_mdwActions->action("recsrc"));
KToggleAction *ta = qobject_cast<KToggleAction *>(channelActions()->action("recsrc"));
if (ta!=nullptr) {
QSignalBlocker blocker(ta);
ta->setChecked( mixDevice()->isRecSource() );
......@@ -1156,7 +1169,7 @@ void MDWSlider::showContextMenu(const QPoint &pos)
}
if ( mixDevice()->hasMuteSwitch() ) {
KToggleAction *ta = qobject_cast<KToggleAction *>(_mdwActions->action("mute"));
KToggleAction *ta = qobject_cast<KToggleAction *>(channelActions()->action("mute"));
if (ta!=nullptr) {
QSignalBlocker blocker(ta);
ta->setChecked( mixDevice()->isMuted() );
......@@ -1164,11 +1177,7 @@ void MDWSlider::showContextMenu(const QPoint &pos)
}
}
// QAction *a = _mdwActions->action( "hide" );
// if ( a )
// menu->addAction( a );
QAction *b = _mdwActions->action( "keys" );
QAction *b = channelActions()->action( "keys" );
if (b!=nullptr)
{
menu->addSeparator();
......
......@@ -50,11 +50,6 @@ public:
MDWSlider(shared_ptr<MixDevice> md, MixDeviceWidget::MDWFlags flags, ViewBase *view, ProfControl *pctl = nullptr);
virtual ~MDWSlider();
enum LabelType { LT_ALL, LT_FIRST_CAPTURE, LT_NONE };
void addActionToPopup( QAction *action );
void createActions();
void createShortcutActions();
// GUI
bool isStereoLinked() const override { return m_linked; }
void setStereoLinked( bool value ) override;
......@@ -111,6 +106,9 @@ private slots:
private:
void createWidgets();
void createActions();
void createGlobalActions();
void addSliders(QBoxLayout *volLayout, char type, Volume& vol,
QList<QAbstractSlider *>& ref_sliders, const QString &tooltipText );
......@@ -129,7 +127,6 @@ private:
void guiAddMuteButton(const QString &muteTooltipText);
void guiAddControlIcon(const QString &tooltipText);
void guiAddControlLabel(Qt::Alignment alignment, const QString &channelName);
void addGlobalShortcut(QAction* action, const QString& label, bool dynamicControl);
QSize controlButtonSize();
bool m_linked;
......
///*
// * KMix -- KDE's full featured mini mixer
// *
// *
// * 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 <qcursor.h>
//#include <QLabel>
//#include <QMenu>
//#include <QMouseEvent>
//#include <QObject>
//
////#include <klocalizedstring.h>
//#include <kconfig.h>
//#include <kaction.h>
//#include <kglobalaccel.h>
//#include <ktoggleaction.h>
//#include <kactioncollection.h>
//
//#include "mdwswitch.h"
//#include "core/mixer.h"
//#include "viewbase.h"
//#include "verticaltext.h"
//
///**
// * Class that represents a single Switch
// * The orientation (horizontal, vertical) can be configured
// */
//MDWSwitch::MDWSwitch(MixDevice* md,
// bool small, Qt::Orientation orientation,
// QWidget* parent, ViewBase* mw) :
// MixDeviceWidget(md,small,orientation,parent,mw),
// _label(0) , _labelV(0) , _switchLED(0), _layout(0)
//{
// // create actions (on _mdwActions, see MixDeviceWidget)
//
// // KStandardAction::showMenubar() is in MixDeviceWidget now
// KToggleAction *action = _mdwActions->add<KToggleAction>( "hide" );
// action->setText( i18n("&Hide") );
// connect(action, SIGNAL(triggered(bool)), SLOT(setDisabled()));
// KAction *b = _mdwActions->addAction( "keys" );
// b->setText( i18n("C&onfigure Shortcuts...") );
// connect(b, SIGNAL(triggered(bool)), SLOT(defineKeys()));
//
// // create widgets
// createWidgets();
//
// KAction *a = _mdwActions->addAction( "Toggle switch" );
// a->setText( i18n( "Toggle Switch" ) );
// connect(a, SIGNAL(triggered(bool)), SLOT(toggleSwitch()));
//
// // The accel keys are loaded in KMixerWidget::loadConfig, see kmixtoolbox.cpp
//
// installEventFilter( this ); // filter for popup
//}
//
//MDWSwitch::~MDWSwitch()
//{
//}
//
//
//void MDWSwitch::createWidgets()
//{
// if ( _orientation == Qt::Vertical ) {
// _layout = new QVBoxLayout( this );
// _layout->setAlignment(Qt::AlignHCenter);
// }
// else {
// _layout = new QHBoxLayout( this );
// _layout->setAlignment(Qt::AlignVCenter);
// }
// this->setToolTip( m_mixdevice->readableName() );
//
//
// _layout->addSpacing( 4 );
// // --- LEDS --------------------------
// if ( _orientation == Qt::Vertical ) {
// if( m_mixdevice->captureVolume().hasSwitch() )
// _switchLED = new QCheckBox( Qt::red,
// m_mixdevice->isRecSource()?KLed::On:KLed::Off,
// KLed::Sunken, KLed::Circular, this, "RecordLED" );
// else
// _switchLED = new QCheckBox( Qt::yellow, KLed::On, KLed::Sunken, KLed::Circular, this, "SwitchLED" );
// _switchLED->setFixedSize(16,16);
// _labelV = new VerticalText( this, m_mixdevice->readableName().toUtf8().data() );
//
// _layout->addWidget( _switchLED );
// _layout->addSpacing( 2 );
// _layout->addWidget( _labelV );
//
// _switchLED->installEventFilter( this );
// _labelV->installEventFilter( this );
// }
// else
// {
// if( m_mixdevice->captureVolume().hasSwitch() )
// _switchLED = new QCheckBox( Qt::red,
// m_mixdevice->isRecSource()?KLed::On:KLed::Off,
// KLed::Sunken, KLed::Circular, this, "RecordLED" );
// else
// _switchLED = new QCheckBox( Qt::yellow, KLed::On, KLed::Sunken, KLed::Circular, this, "SwitchLED" );
// _switchLED->setFixedSize(16,16);
// _label = new QLabel(m_mixdevice->readableName(), this );
// _label->setObjectName( QLatin1String("SwitchName" ));
//
// _layout->addWidget( _switchLED );
// _layout->addSpacing( 1 );
// _layout->addWidget( _label );
// _switchLED->installEventFilter( this );
// _label->installEventFilter( this );
// }
// connect( _switchLED, SIGNAL(stateChanged(bool)), this, SLOT(toggleSwitch()) );
// _layout->addSpacing( 4 );
//}
//
//void MDWSwitch::update()
//{
// if ( _switchLED != 0 ) {
// _switchLED->blockSignals( true );
// if( m_mixdevice->captureVolume().hasSwitch() )
// _switchLED->setState( m_mixdevice->isRecSource() ? KLed::On : KLed::Off );
// else
// _switchLED->setState( m_mixdevice->isMuted() ? KLed::Off : KLed::On );
//
// _switchLED->blockSignals( false );
// }
//}
//
//void MDWSwitch::setBackgroundRole(QPalette::ColorRole m)
//{
// if ( _label != 0 ){
// _label->setBackgroundRole(m);
// }
// if ( _labelV != 0 ){
// _labelV->setBackgroundRole(m);
// }
// _switchLED->setBackgroundRole(m);
// MixDeviceWidget::setBackgroundRole(m);
//}
//
//void MDWSwitch::showContextMenu()
//{
// if( m_view == 0 )
// return;
//
// QMenu *menu = m_view->getPopup();
//
// QPoint pos = QCursor::pos();
// menu->popup( pos );
//}
//
//
//QSizePolicy MDWSwitch::sizePolicy() const
//{
// if ( _orientation == Qt::Vertical ) {
// return QSizePolicy( QSizePolicy::Fixed, QSizePolicy::MinimumExpanding );
// }
// else {
// return QSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
// }
//}
//
///**
// This slot is called, when a user has clicked the mute button. Also it is called by any other
// associated KAction like the context menu.
//*/
//void MDWSwitch::toggleSwitch() {
// if( m_mixdevice->captureVolume().hasSwitch() )
// setSwitch( !m_mixdevice->isRecSource() );
// else
// setSwitch( !m_mixdevice->isMuted() );
//}
//
//void MDWSwitch::setSwitch(bool value)
//{
// if ( m_mixdevice->playbackVolume().hasSwitch() ) {
// if ( m_mixdevice->captureVolume().hasSwitch() ) {
// m_mixdevice->mixer()->setRecordSource( m_mixdevice->id(), value );
// }
// else {
// m_mixdevice->setMuted( value );
// m_mixdevice->mixer()->commitVolumeChange( m_mixdevice );
// }
// }
//}
//
//void MDWSwitch::setDisabled()
//{
// setDisabled( true );
//}
//
//void MDWSwitch::setDisabled( bool value ) {
// if ( m_disabled!=value)
// {
// value ? hide() : show();
// m_disabled = value;
// }
//}
//
///**
// * An event filter for the various QWidgets. We watch for Mouse press Events, so
// * that we can popup the context menu.
// */
//bool MDWSwitch::eventFilter( QObject* obj, QEvent* e )
//{
// if (e->type() == QEvent::MouseButtonPress) {
// QMouseEvent *qme = static_cast<QMouseEvent*>(e);
// if (qme->button() == Qt::RightButton) {
// showContextMenu();
// return true;
// }
// }
// return QWidget::eventFilter(obj,e);
//}
//
////-*-C++-*-
///*
// * KMix -- KDE's full featured mini mixer
// *
// *
// * Copyright Chrisitan 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.
// */
//
//#ifndef MDWSWITCH_H
//#define MDWSWITCH_H
//
//#include <QWidget>
//#include "core/volume.h"
//#include <qpixmap.h>
//
//class QBoxLayout;
//class QLabel;
//
//class QCheckBox;
//class KAction;
//
//class MixDevice;
//class VerticalText;
//class Mixer;
//class ViewBase;
//
//#include "gui/mixdevicewidget.h"
//
//class MDWSwitch : public MixDeviceWidget
//{
// Q_OBJECT
//
//public:
// MDWSwitch( MixDevice* md,
// bool small, Qt::Orientation orientation,
// QWidget* parent = 0, ViewBase* mw = 0);
// ~MDWSwitch();
//
// void addActionToPopup( KAction *action );
// QSizePolicy sizePolicy() const;
// void setBackgroundRole(QPalette::ColorRole m);
// bool eventFilter( QObject* obj, QEvent* e );
//
//public slots:
// // GUI hide and show
// void setDisabled();
// void setDisabled(bool);
//
// // Switch on/off
// void toggleSwitch();
// void setSwitch(bool value);
//
// void update();
// virtual void showContextMenu();
//
//private:
// void createWidgets();
//
// QLabel *_label;
// VerticalText *_labelV;
// QCheckBox *_switchLED;
// QBoxLayout *_layout;
//};
//
//#endif
......@@ -23,13 +23,11 @@
#include <kactioncollection.h>
#include <kconfig.h>
#include <kglobalaccel.h>
#include <kshortcutsdialog.h>
#include <klocalizedstring.h>
#include <qcursor.h>
#include <QMenu>
#include <QMouseEvent>
#include <qpixmap.h>
#include <qmenu.h>
#include <qevent.h>
#include "core/mixer.h"
#include "core/mixertoolbox.h"
......@@ -60,8 +58,8 @@ MixDeviceWidget::MixDeviceWidget(shared_ptr<MixDevice> md, MDWFlags flags, ViewB
if (m_pctl==nullptr) m_pctl = md->controlProfile();
Q_ASSERT(m_pctl!=nullptr);
_mdwActions = new KActionCollection( this );
_mdwPopupActions = new KActionCollection( this );
m_channelActions = new KActionCollection(this);
m_globalActions = new KActionCollection(this);
m_shortcutsDialog = nullptr;
QString name (md->id());
......@@ -76,26 +74,36 @@ MixDeviceWidget::MixDeviceWidget(shared_ptr<MixDevice> md, MDWFlags flags, ViewB
}
}
void MixDeviceWidget::addActionToPopup( QAction *action )
void MixDeviceWidget::createShortcutsAction()
{
_mdwActions->addAction( action->objectName(), action );
if (m_globalActions->isEmpty()) return; // no shortcuts to define
QAction* act = m_channelActions->addAction("keys");
act->setText(i18n("Channel Shortcuts..."));
connect(act, &QAction::triggered, this, &MixDeviceWidget::configureShortcuts);
}
void MixDeviceWidget::defineKeys()
void MixDeviceWidget::configureShortcuts()