mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-24 02:42:52 +00:00
439 lines
14 KiB
C++
439 lines
14 KiB
C++
/*
|
|
* KMix -- KDE's full featured mini mixer
|
|
*
|
|
*
|
|
* Copyright (C) 1996-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.
|
|
*/
|
|
|
|
//#define TEST_MIXDEVICE_COMPOSITE
|
|
#undef TEST_MIXDEVICE_COMPOSITE
|
|
|
|
#ifdef TEST_MIXDEVICE_COMPOSITE
|
|
#warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
#warning !!! MIXDEVICE COMPOSITE TESTING IS ACTIVATED !!!
|
|
#warning !!! THIS IS PRE-ALPHA CODE! !!!
|
|
#warning !!! DO NOT SHIP KMIX IN THIS STATE !!!
|
|
#warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
#endif
|
|
|
|
#include "gui/viewsliders.h"
|
|
|
|
// KMix
|
|
#include "core/ControlManager.h"
|
|
#include "core/GlobalConfig.h"
|
|
#include "core/mixdevicecomposite.h"
|
|
#include "core/mixer.h"
|
|
#include "gui/guiprofile.h"
|
|
#include "gui/mdwenum.h"
|
|
#include "gui/mdwslider.h"
|
|
#include "gui/verticaltext.h"
|
|
|
|
|
|
// KDE
|
|
#include <kdebug.h>
|
|
#include <KIcon>
|
|
#include <KLocale>
|
|
|
|
// Qt
|
|
#include <QPushButton>
|
|
#include <QLabel>
|
|
#include <QLayoutItem>
|
|
#include <QWidget>
|
|
#include <QVBoxLayout>
|
|
#include <QHBoxLayout>
|
|
|
|
/**
|
|
* Generic View implementation. This can hold now all kinds of controls (not just Sliders, as
|
|
* the class name suggests).
|
|
*/
|
|
ViewSliders::ViewSliders(QWidget* parent, QString id, Mixer* mixer, ViewBase::ViewFlags vflags, QString guiProfileId,
|
|
KActionCollection *actColl) :
|
|
ViewBase(parent, id, Qt::FramelessWindowHint, vflags, guiProfileId, actColl), _layoutEnum(0)
|
|
{
|
|
addMixer(mixer);
|
|
|
|
_configureViewButton = 0;
|
|
_layoutMDW = 0;
|
|
_layoutSliders = 0;
|
|
_layoutEnum = 0;
|
|
emptyStreamHint = 0;
|
|
|
|
createDeviceWidgets();
|
|
|
|
ControlManager::instance().addListener(mixer->id(),
|
|
(ControlChangeType::Type) (ControlChangeType::GUI | ControlChangeType::ControlList | ControlChangeType::Volume),
|
|
this, QString("ViewSliders.%1").arg(mixer->id()));
|
|
|
|
}
|
|
|
|
ViewSliders::~ViewSliders()
|
|
{
|
|
ControlManager::instance().removeListener(this);
|
|
delete _layoutMDW;
|
|
// qDeleteAll(_separators);
|
|
}
|
|
|
|
void ViewSliders::controlsChange(int changeType)
|
|
{
|
|
ControlChangeType::Type type = ControlChangeType::fromInt(changeType);
|
|
switch (type)
|
|
{
|
|
case ControlChangeType::ControlList:
|
|
createDeviceWidgets();
|
|
break;
|
|
case ControlChangeType::GUI:
|
|
updateGuiOptions();
|
|
break;
|
|
|
|
case ControlChangeType::Volume:
|
|
if (GlobalConfig::instance().data.debugVolume)
|
|
kDebug()
|
|
<< "NOW I WILL REFRESH VOLUME LEVELS. I AM " << id();
|
|
refreshVolumeLevels();
|
|
break;
|
|
|
|
default:
|
|
ControlManager::warnUnexpectedChangeType(type, this);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
QWidget* ViewSliders::add(std::shared_ptr<MixDevice> md)
|
|
{
|
|
MixDeviceWidget *mdw;
|
|
Qt::Orientation orientation = GlobalConfig::instance().data.getToplevelOrientation();
|
|
|
|
if ( md->isEnum() )
|
|
{
|
|
mdw = new MDWEnum(
|
|
md, // MixDevice (parameter)
|
|
orientation, // Orientation
|
|
this, // parent
|
|
this // View widget
|
|
, md->controlProfile()
|
|
);
|
|
// if ( _layoutEnum == 0 ) {
|
|
// // lazily creation of Layout for the first enum
|
|
// _layoutEnum = new QVBoxLayout();
|
|
// _layoutMDW->addLayout( _layoutEnum );
|
|
// }
|
|
_layoutEnum->addWidget(mdw);
|
|
} // an enum
|
|
else
|
|
{
|
|
mdw = new MDWSlider(
|
|
md, // MixDevice (parameter)
|
|
true, // Show Mute LED
|
|
true, // Show Record LED
|
|
false, // Include Mixer Name
|
|
false, // Small
|
|
orientation, // Orientation
|
|
this, // parent
|
|
this
|
|
, md->controlProfile()
|
|
); // View widget
|
|
_layoutSliders->addWidget(mdw);
|
|
}
|
|
|
|
return mdw;
|
|
}
|
|
|
|
|
|
void ViewSliders::_setMixSet()
|
|
{
|
|
resetMdws();
|
|
|
|
delete emptyStreamHint;
|
|
emptyStreamHint = 0;
|
|
|
|
// Our _layoutSliders now should only contain spacer widgets from the addSpacing() calls in add() above.
|
|
// We need to trash those too otherwise all sliders gradually migrate away from the edge :p
|
|
if (_layoutSliders != 0)
|
|
{
|
|
QLayoutItem *li;
|
|
while ((li = _layoutSliders->takeAt(0)))
|
|
delete li;
|
|
// delete _layoutSliders;
|
|
_layoutSliders = 0;
|
|
}
|
|
|
|
|
|
delete _configureViewButton;
|
|
_configureViewButton = 0;
|
|
|
|
if (_layoutEnum != 0)
|
|
{
|
|
QLayoutItem *li2;
|
|
while ((li2 = _layoutEnum->takeAt(0)) != 0)
|
|
delete li2;
|
|
// delete _layoutEnum;
|
|
_layoutEnum = 0;
|
|
}
|
|
|
|
delete _layoutMDW;
|
|
_layoutMDW = 0;
|
|
|
|
// We will be recreating our sliders, so make sure we trash all the separators too.
|
|
//qDeleteAll(_separators);
|
|
_separators.clear();
|
|
|
|
if (GlobalConfig::instance().data.getToplevelOrientation() == Qt::Horizontal)
|
|
{
|
|
// Horizontal slider => put them vertically
|
|
_layoutMDW = new QVBoxLayout(this);
|
|
_layoutMDW->setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
|
_layoutSliders = new QVBoxLayout();
|
|
_layoutSliders->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
|
|
}
|
|
else
|
|
{
|
|
// Vertical slider => put them horizontally
|
|
_layoutMDW = new QHBoxLayout(this);
|
|
_layoutMDW->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
|
|
_layoutSliders = new QHBoxLayout();
|
|
_layoutSliders->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
|
|
}
|
|
_layoutSliders->setContentsMargins(0, 0, 0, 0);
|
|
_layoutSliders->setSpacing(0);
|
|
_layoutMDW->setContentsMargins(0, 0, 0, 0);
|
|
_layoutMDW->setSpacing(0);
|
|
_layoutMDW->addItem(_layoutSliders);
|
|
|
|
_layoutEnum = new QVBoxLayout();
|
|
_layoutMDW->addLayout(_layoutEnum);
|
|
|
|
// Hint: This text comparison is not a clean solution, but one that will work for quite a while.
|
|
QString viewId(id());
|
|
if (viewId.contains(".Capture_Streams."))
|
|
emptyStreamHint = new QLabel(i18n("Nothing is capturing audio."));
|
|
else if (viewId.contains(".Playback_Streams."))
|
|
emptyStreamHint = new QLabel(i18n("Nothing is playing audio."));
|
|
else if (viewId.contains(".Capture_Devices."))
|
|
emptyStreamHint = new QLabel(i18n("No capture devices."));
|
|
else if (viewId.contains(".Playback_Devices."))
|
|
emptyStreamHint = new QLabel(i18n("No playback devices."));
|
|
else
|
|
emptyStreamHint = new QLabel(i18n("Nothing is playing audio.")); // Fallback. Assume Playback stream
|
|
|
|
emptyStreamHint->setAlignment(Qt::AlignCenter);
|
|
emptyStreamHint->setWordWrap(true);
|
|
emptyStreamHint->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
|
|
_layoutMDW->addWidget(emptyStreamHint);
|
|
|
|
#ifdef TEST_MIXDEVICE_COMPOSITE
|
|
QList<std::shared_ptr<MixDevice> > mds; // For temporary test
|
|
#endif
|
|
|
|
// This method iterates the controls from the Profile
|
|
// Each control is checked, whether it is also contained in the mixset, and
|
|
// applicable for this kind of View. If yes, the control is accepted and inserted.
|
|
|
|
GUIProfile* guiprof = guiProfile();
|
|
if (guiprof != 0)
|
|
{
|
|
foreach (Mixer* mixer , _mixers ){
|
|
const MixSet& mixset = mixer->getMixSet();
|
|
|
|
foreach ( ProfControl* control, guiprof->getControls() )
|
|
{
|
|
//ProfControl* control = *it;
|
|
// The TabName of the control matches this View name (!! attention: Better use some ID, due to i18n() )
|
|
QRegExp idRegexp(control->id);
|
|
//bool isExactRegexp = control->id.startsWith('^') && control->id.endsWith('$'); // for optimizing
|
|
//isExactRegexp &= ( ! control->id.contains(".*") ); // For now. Might be removed in the future, as it cannot be done properly !!!
|
|
//kDebug(67100) << "ViewSliders::setMixSet(): Check GUIProfile id==" << control->id << "\n";
|
|
// The following for-loop could be simplified by using a std::find_if
|
|
for ( int i=0; i<mixset.count(); i++ )
|
|
{
|
|
std::shared_ptr<MixDevice> md = mixset[i];
|
|
|
|
if ( md->id().contains(idRegexp) )
|
|
{
|
|
// Match found (by name)
|
|
if ( _mixSet.contains( md ) )
|
|
{
|
|
continue; // dup check
|
|
}
|
|
|
|
// Now check whether subcontrols match
|
|
bool subcontrolPlaybackWanted = (control->useSubcontrolPlayback() && ( md->playbackVolume().hasVolume() || md->hasMuteSwitch()) );
|
|
bool subcontrolCaptureWanted = (control->useSubcontrolCapture() && ( md->captureVolume() .hasVolume() || md->captureVolume() .hasSwitch()) );
|
|
bool subcontrolEnumWanted = (control->useSubcontrolEnum() && md->isEnum());
|
|
bool subcontrolWanted = subcontrolPlaybackWanted | subcontrolCaptureWanted | subcontrolEnumWanted;
|
|
|
|
if ( !subcontrolWanted )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
md->setControlProfile(control);
|
|
if ( !control->name.isNull() )
|
|
{
|
|
// Apply the custom name from the profile
|
|
md->setReadableName(control->name);// @todo: This is the wrong place. It only applies to controls in THIS type of view
|
|
}
|
|
if ( !control->getSwitchtype().isNull() )
|
|
{
|
|
if ( control->getSwitchtype() == "On" )
|
|
md->playbackVolume().setSwitchType(Volume::OnSwitch);
|
|
else if ( control->getSwitchtype() == "Off" )
|
|
md->playbackVolume().setSwitchType(Volume::OffSwitch);
|
|
}
|
|
_mixSet.append(md);
|
|
|
|
#ifdef TEST_MIXDEVICE_COMPOSITE
|
|
if ( md->id() == "Front:0" || md->id() == "Surround:0")
|
|
{ mds.append(md);} // For temporary test
|
|
#endif
|
|
// We use no "break;" ,as multiple devices could match the regexp (e.g. "^.*$")
|
|
} // name matches
|
|
} // loop for finding a suitable MixDevice
|
|
} // iteration over all controls from the Profile
|
|
} // if there is a profile
|
|
} // Iteration over all Mixers
|
|
|
|
emptyStreamHint->setVisible(_mixSet.isEmpty() && isDynamic()); // show a hint why a tab is empty (dynamic controls!!!)
|
|
// visibleControls() == 0 could be used for the !isDynamic() case
|
|
|
|
#ifdef TEST_MIXDEVICE_COMPOSITE
|
|
// @todo: This is currently hardcoded, and instead must be read as usual from the Profile
|
|
MixDeviceComposite *mdc = new MixDeviceComposite(_mixer, "Composite_Test", mds, "A Composite Control #1", MixDevice::KMIX_COMPOSITE);
|
|
Volume::ChannelMask chn = Volume::MMAIN;
|
|
Volume* vol = new Volume( chn, 0, 100, true, true);
|
|
mdc->addPlaybackVolume(*vol);
|
|
QString ctlId("Composite_Test");
|
|
QString ctlMatchAll("*");
|
|
ProfControl* pctl = new ProfControl(ctlId, ctlMatchAll);
|
|
mdc->setControlProfile(pctl);
|
|
_mixSet->append(mdc);
|
|
#endif
|
|
}
|
|
|
|
|
|
void ViewSliders::constructionFinished() {
|
|
configurationUpdate();
|
|
if ( !isDynamic() )
|
|
{
|
|
_configureViewButton = createConfigureViewButton();
|
|
_layoutEnum->addStretch();
|
|
_layoutEnum->addWidget(_configureViewButton);
|
|
}
|
|
|
|
updateGuiOptions();
|
|
}
|
|
|
|
|
|
void ViewSliders::configurationUpdate()
|
|
{
|
|
// Adjust height of top part by setting it to the maximum of all mdw's
|
|
bool haveCaptureLEDs = false;
|
|
int labelExtent = 0;
|
|
bool haveMuteButtons = false;
|
|
|
|
// Find out whether any MDWSlider has Switches. If one has, then we need "extents"
|
|
for (int i = 0; i < _mdws.count(); i++)
|
|
{
|
|
MDWSlider* mdw = ::qobject_cast<MDWSlider*>(_mdws[i]);
|
|
if (mdw && mdw->isVisibleTo(this))
|
|
{
|
|
if (mdw->labelExtentHint() > labelExtent)
|
|
labelExtent = mdw->labelExtentHint();
|
|
haveCaptureLEDs = haveCaptureLEDs || mdw->hasCaptureLED();
|
|
haveMuteButtons = haveMuteButtons || mdw->hasMuteButton();
|
|
}
|
|
|
|
if (haveCaptureLEDs && haveMuteButtons)
|
|
break; // We know all we want. Lets break.
|
|
}
|
|
//kDebug(67100) << "topPartExtent is " << topPartExtent;
|
|
bool firstVisibleControlFound = false;
|
|
for ( int i=0; i<_mdws.count(); i++ )
|
|
{
|
|
MixDeviceWidget* mdw = ::qobject_cast<MixDeviceWidget*>(_mdws[i]);
|
|
MDWSlider* mdwSlider = ::qobject_cast<MDWSlider*>(_mdws[i]);
|
|
if ( mdw )
|
|
{
|
|
// This is a bit hacky. Using "simple" can be wrong on the very first start of KMix (but usually it is not!)
|
|
ProfControl* matchingControl = findMdw(mdw->mixDevice()->id(), QString("simple"));
|
|
mdw->setVisible(matchingControl != 0);
|
|
|
|
if ( mdwSlider )
|
|
{
|
|
// additional options for sliders
|
|
mdwSlider->setLabelExtent(labelExtent);
|
|
mdwSlider->setMuteButtonSpace(haveMuteButtons);
|
|
mdwSlider->setCaptureLEDSpace(haveCaptureLEDs);
|
|
}
|
|
bool thisControlIsVisible = mdw->isVisibleTo(this);
|
|
bool showSeparator = ( firstVisibleControlFound && thisControlIsVisible);
|
|
if ( _separators.contains( mdw->mixDevice()->id() ))
|
|
{
|
|
QFrame* sep = _separators[mdw->mixDevice()->id()];
|
|
sep->setVisible(showSeparator);
|
|
}
|
|
if ( thisControlIsVisible )
|
|
firstVisibleControlFound=true;
|
|
}
|
|
} // for all MDW's
|
|
|
|
_layoutMDW->activate();
|
|
}
|
|
|
|
void ViewSliders::refreshVolumeLevels()
|
|
{
|
|
for (int i = 0; i < _mdws.count(); i++)
|
|
{
|
|
QWidget *mdwx = _mdws[i];
|
|
|
|
MixDeviceWidget* mdw = ::qobject_cast<MixDeviceWidget*>(mdwx);
|
|
if (mdw != 0)
|
|
{ // sanity check
|
|
|
|
#ifdef TEST_MIXDEVICE_COMPOSITE
|
|
// --- start --- The following 4 code lines should be moved to a more
|
|
// generic place, as it only works in this View. But it
|
|
// should also work in the ViewDockareaPopup and everywhere else.
|
|
MixDeviceComposite* mdc = ::qobject_cast<MixDeviceComposite*>(mdw->mixDevice());
|
|
if (mdc != 0)
|
|
{
|
|
mdc->update();
|
|
}
|
|
// --- end ---
|
|
#endif
|
|
|
|
if (GlobalConfig::instance().data.debugVolume)
|
|
{
|
|
bool debugMe = (mdw->mixDevice()->id() == "PCM:0");
|
|
if (debugMe)
|
|
kDebug()
|
|
<< "Old PCM:0 playback state" << mdw->mixDevice()->isMuted() << ", vol="
|
|
<< mdw->mixDevice()->playbackVolume().getAvgVolumePercent(Volume::MALL);
|
|
}
|
|
|
|
mdw->update();
|
|
}
|
|
else
|
|
{
|
|
kError(67100) << "ViewSliders::refreshVolumeLevels(): mdw is not a MixDeviceWidget\n";
|
|
// no slider. Cannot happen in theory => skip it
|
|
}
|
|
}
|
|
}
|
|
|
|
#include "moc_viewsliders.cpp"
|