dialogselectmaster.cpp 8.92 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
 * 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
Dirk Mueller's avatar
Dirk Mueller committed
19
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21
 */

22
23
#include "gui/dialogselectmaster.h"

24
#include <QLabel>
Laurent Montel's avatar
Laurent Montel committed
25
#include <QVBoxLayout>
26
#include <QListWidget>
27
#include <QComboBox>
28
#include <QToolTip>
29

30
#include <klocalizedstring.h>
31

32
#include "core/ControlManager.h"
33
#include "core/mixer.h"
34
#include "gui/kmixtoolbox.h"
35

36
37
38
39
40

// TOD: Should this be incorporated into the "Configure KMix" dialogue?

DialogSelectMaster::DialogSelectMaster(const Mixer *mixer, QWidget *parent)
    : DialogBase(parent)
41
{
42
    setWindowTitle(i18n("Select Master Channel"));
43
44
    if (Mixer::mixers().count()>0) setButtons(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
    else setButtons(QDialogButtonBox::Cancel);
45

46
47
   m_channelSelector = nullptr;
   createWidgets(mixer);				// for the specified 'mixer'
48
49
}

50

51
/**
52
 * Create basic widgets of the dialogue.
53
 */
54
void DialogSelectMaster::createWidgets(const Mixer *mixer)
55
{
56
57
58
    QWidget *mainFrame = new QWidget(this);
    setMainWidget(mainFrame);
    QVBoxLayout *layout = new QVBoxLayout(mainFrame);
59

60
61
62
63
    const QList<Mixer *> &mixers = Mixer::mixers();	// list of all mixers present

    if (mixers.count()>1)
    {
64
65
66
67
68
        int mixerIndex = 0;				// index of selected mixer

        // TODO: should "Playback Streams" (ALSA) be shown here?
        // There is no meaningful concept of a master channel for that.

69
70
        // More than one Mixer => show Combo-Box to select Mixer
        // Mixer widget line
71
        QHBoxLayout *mixerNameLayout = new QHBoxLayout();
72
        layout->addLayout( mixerNameLayout );
Laurent Montel's avatar
Laurent Montel committed
73
        mixerNameLayout->setContentsMargins(0, 0, 0, 0);
74
    
75
        QLabel *qlbl = new QLabel( i18n("Current mixer:"), mainFrame );
76
77
78
        mixerNameLayout->addWidget(qlbl);
        qlbl->setFixedHeight(qlbl->sizeHint().height());
    
79
        m_cMixer = new QComboBox(mainFrame);
Laurent Montel's avatar
Laurent Montel committed
80
        m_cMixer->setObjectName( QLatin1String( "mixerCombo" ) );
81
        m_cMixer->setFixedHeight(m_cMixer->sizeHint().height());
82
        connect(m_cMixer, QOverload<int>::of(&QComboBox::activated), this, &DialogSelectMaster::createPageByID);
83

84
        for (int i = 0; i<mixers.count(); ++i)
85
        {
86
            const Mixer *m = mixers[i];
87
88
            m_cMixer->addItem(QIcon::fromTheme(m->iconName()), m->readableName(), m->id());
            if (m->id()==mixer->id()) mixerIndex = i;
89
90
91
92
93
         }

        // Select the specified 'mixer' as the current item in the combo box.
        // If it was not found by the loop above, then select the first item.
        m_cMixer->setCurrentIndex(mixerIndex);
94
        
95
        mixerNameLayout->addWidget(m_cMixer, 1);
96
        layout->addSpacing(DialogBase::verticalSpacing());
97
    }
98

99
100
    if (mixers.count()>0)
    {
101
102
103
104
105
        QLabel *qlbl = new QLabel(xi18nc("@info", "Select the <a href=\"i\">master volume</a> channel:"), mainFrame);
        // Not a good idea to set Qt::LinksAccessibleByKeyboard, if the link is
        // clicked on or gets focus then the Return key will not activate the OK
        // button as expected.
        //qlbl->setTextInteractionFlags(Qt::LinksAccessibleByMouse|Qt::LinksAccessibleByKeyboard);
106
        layout->addWidget(qlbl);
107
108
109
110
111
112
113
114
        connect(qlbl, &QLabel::linkActivated, this, [this]() {
            QToolTip::showText(QCursor::pos(), xi18nc("@info:tooltip",
"<para>Here you can select the master sound device (if there is more than one) and its master channel.</para>"
"<para>The master channel is the one that is affected by the system tray volume control, "
"the volume up/down and mute global shortcut keys, "
"and the <interface>Mute</interface> action in the system tray popup menu.</para>"));
        });

115
116
117
        createPage(mixer);

        connect(this, &QDialog::accepted, this, &DialogSelectMaster::apply);
118
    }
119
120
121
122
123
    else
    {
	QWidget *noMixersWarning = KMixToolBox::noDevicesWarningWidget(mainFrame);
        layout->addWidget(noMixersWarning);
        layout->addStretch(1);
124
    }
125
126
}

127

128
/**
129
130
131
 * Create the channel list for the specified mixer
 *
 * @p mixerId the ID (index) of the mixer for which the list should be created.
132
 */
133
void DialogSelectMaster::createPageByID(int mixerId)
134
{
135
136
    // TODO: would Mixer::mixers().value(mixerId) work just as well?
    // TODO: mixerId should really be named mixerIndex!
137
138
139
    const QString mixer_id = m_cMixer->itemData(mixerId).toString();
    const Mixer *mixer = Mixer::findMixer(mixer_id);
    if (mixer!=nullptr) createPage(mixer);
140
141
}

142

143
/**
144
145
146
 * Create the channel list for the specified mixer
 *
 * @p mixer the mixer for which the list should be created
147
 */
148
void DialogSelectMaster::createPage(const Mixer *mixer)
149
{
150
151
152
153
    /** --- Reset page -----------------------------------------------
     * In case the user selected a new Mixer via m_cMixer, we need
     * to remove the stuff created on the last call.
     */
154
155
156
	// delete the list widget.
	// This should automatically remove all contained items.
	delete m_channelSelector;
157
    
158
    /** Reset page end -------------------------------------------------- */
159
    
160
161
162
        QWidget *mainFrame = mainWidget();
        QVBoxLayout *layout = qobject_cast<QVBoxLayout *>(mainFrame->layout());
        Q_ASSERT(layout!=nullptr);
163

164
	m_channelSelector = new QListWidget(mainFrame);
165
#ifndef QT_NO_ACCESSIBILITY
166
        m_channelSelector->setAccessibleName( i18n("Select Master Channel") );
167
#endif
168
169
170
	m_channelSelector->setSelectionMode(QAbstractItemView::SingleSelection);
	m_channelSelector->setDragEnabled(false);
	m_channelSelector->setAlternatingRowColors(true);
171
	layout->addWidget(m_channelSelector);
172

173
174
        const MixSet &mixset = mixer->getMixSet();
	const MasterControl mc = mixer->getGlobalMasterPreferred(false);
175
	QString masterKey = mc.getControl();
176
	if (!masterKey.isEmpty() && !mixset.get(masterKey))
177
	{
178
179
		const shared_ptr<MixDevice> master = mixer->getLocalMasterMD();
		if (master.get()!=nullptr) masterKey = master->id();
180
181
182
	}

	int msetCount = 0;
183
	for (int i = 0; i<mixset.count(); ++i)
184
        {
185
            const shared_ptr<MixDevice> md = mixset[i];
186
187
            if (md->playbackVolume().hasVolume()) ++msetCount;
        }
188

189
	if (msetCount>0 && !mixer->isDynamic())
190
	{
191
            QString mdName = i18n("Automatic (%1 recommendation)", mixer->getDriverName());
192
193
194
            auto *item = new QListWidgetItem(QIcon::fromTheme("audio-volume-high"), mdName, m_channelSelector);
            item->setData(Qt::UserRole, QString());	// ID here: see apply(), empty String => Automatic
            if (masterKey.isEmpty()) m_channelSelector->setCurrentItem(item);
195
196
	}

197
198
	// Populate the list view with the MixDevice's having a playback volume
        // (excludes pure capture controls and pure enum's)
199
	for (int i = 0; i<mixset.count(); ++i)
200
        {
201
            const shared_ptr<MixDevice> md = mixset[i];
202
203
204
205
206
207
208
209
210
            if (md->playbackVolume().hasVolume())
            {
                const QString mdName = md->readableName();
                auto *item = new QListWidgetItem(QIcon::fromTheme(md->iconName()), mdName, m_channelSelector);
                item->setData(Qt::UserRole, md->id());  // ID here: see apply()
                if (md->id() == masterKey)
                {					// select the current master
                    m_channelSelector->setCurrentItem(item);
                }
211
            }
212
        }
213

214
        setButtonEnabled(QDialogButtonBox::Ok, m_channelSelector->count()>0);
215
216
217
218
219
}


void DialogSelectMaster::apply()
{
220
221
222
223
224
225
226
    const QList<Mixer *> &mixers = Mixer::mixers();	// list of all mixers present
    Mixer *mixer = nullptr;				// selected mixer found

    if (mixers.count()==1)
    {
        // only one mixer => no combo box => take first entry
        mixer = mixers.first();
227
    }
228
229
230
231
232
    else if (mixers.count()>1)
    {
        // use the mixer that is currently selected in the combo box
        const int idx = m_cMixer->currentIndex();
        if (idx!=-1) mixer = mixers.at(idx);
233
    }
234

235
236
    if (mixer==nullptr) return;				// user must have unplugged everything

237
238
    QList<QListWidgetItem *> items = m_channelSelector->selectedItems();
    if (items.count()==1)
239
    {
240
241
    	QListWidgetItem *item = items.first();
    	QString control_id = item->data(Qt::UserRole).toString();
242
        mixer->setLocalMasterMD(control_id);
243
244
245
        Mixer::setGlobalMaster(mixer->id(), control_id, true);
        ControlManager::instance().announce(mixer->id(), ControlManager::MasterChanged, QString("Select Master Dialog"));
    }
246
}