• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mcallegari / qlcplus / 7252848206

18 Dec 2023 07:26PM UTC coverage: 32.067% (+0.001%) from 32.066%
7252848206

push

github

mcallegari
Code style review #1427

199 of 628 new or added lines in 101 files covered. (31.69%)

8 existing lines in 2 files now uncovered.

15169 of 47304 relevant lines covered (32.07%)

23733.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/ui/src/inputoutputpatcheditor.cpp
1
/*
2
  Q Light Controller
3
  inputoutputpatcheditor.cpp
4

5
  Copyright (C) Massimo Callegari
6

7
  Licensed under the Apache License, Version 2.0 (the "License");
8
  you may not use this file except in compliance with the License.
9
  You may obtain a copy of the License at
10

11
      http://www.apache.org/licenses/LICENSE-2.0.txt
12

13
  Unless required by applicable law or agreed to in writing, software
14
  distributed under the License is distributed on an "AS IS" BASIS,
15
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
  See the License for the specific language governing permissions and
17
  limitations under the License.
18
*/
19

20
#include <QTreeWidgetItem>
21
#include <QButtonGroup>
22
#include <QTreeWidget>
23
#include <QToolButton>
24
#include <QMessageBox>
25
#include <QFileDialog>
26
#include <QSettings>
27
#include <QComboBox>
28
#include <QGroupBox>
29
#include <QVariant>
30
#include <QDebug>
31
#include <QFile>
32
#include <QDir>
33

34
#include "qlcinputprofile.h"
35
#include "qlcioplugin.h"
36
#include "qlcfile.h"
37

38
#include "inputoutputpatcheditor.h"
39
#include "inputprofileeditor.h"
40
#include "audioplugincache.h"
41
#include "inputoutputmap.h"
42
#include "outputpatch.h"
43
#include "inputpatch.h"
44
#include "doc.h"
45

46
/* Plugin column structure */
47
#define KMapColumnPluginName    0
48
#define KMapColumnDeviceName    1
49
#define KMapColumnHasInput      2
50
#define KMapColumnHasOutput     3
51
#define KMapColumnHasFeedback   4
52
#define KMapColumnInputLine     5
53
#define KMapColumnOutputLine    6
54

55
#define KAudioColumnDeviceName  0
56
#define KAudioColumnHasInput    1
57
#define KAudioColumnHasOutput   2
58
#define KAudioColumnPrivate     3
59

60
/* Profile column structure */
61
#define KProfileColumnName 0
62
#define KProfileColumnType 1
63

64
InputOutputPatchEditor::InputOutputPatchEditor(QWidget* parent, quint32 universe, InputOutputMap *ioMap, Doc *doc)
×
65
    : QWidget(parent)
66
    , m_ioMap(ioMap)
67
    , m_doc(doc)
68
    , m_universe(universe)
69
    , m_currentInputPluginName(KInputNone)
70
    , m_currentInput(QLCIOPlugin::invalidLine())
×
71
    , m_currentOutputPluginName(KOutputNone)
72
    , m_currentOutput(QLCIOPlugin::invalidLine())
×
73
    , m_currentProfileName(KInputNone)
74
    , m_currentFeedbackPluginName(KOutputNone)
75
    , m_currentFeedback(QLCIOPlugin::invalidLine())
×
76
    , m_inputCapture(NULL)
×
77
{
78
    Q_ASSERT(universe < m_ioMap->universesCount());
×
79
    Q_ASSERT(ioMap != NULL);
×
80

81
    setupUi(this);
×
82

83
    m_infoBrowser->setOpenExternalLinks(true);
×
84
    m_infoBrowser->setFixedHeight(250);
×
85

86
    InputPatch* inputPatch = m_ioMap->inputPatch(universe);
×
87
    OutputPatch* outputPatch = m_ioMap->outputPatch(universe);
×
88
    OutputPatch* feedbackPatch = m_ioMap->feedbackPatch(universe);
×
89

90
    /* Copy these so they can be applied if the user cancels */
91
    if (inputPatch != NULL)
×
92
    {
93
        m_currentInputPluginName = inputPatch->pluginName();
×
94
        m_currentInput = inputPatch->input();
×
95
        m_currentProfileName = inputPatch->profileName();
×
96
    }
97

98
    if (outputPatch != NULL)
×
99
    {
100
        m_currentOutputPluginName = outputPatch->pluginName();
×
101
        m_currentOutput = outputPatch->output();
×
102
    }
103

104
    if (feedbackPatch != NULL)
×
105
    {
106
        m_currentFeedbackPluginName = feedbackPatch->pluginName();
×
107
        m_currentFeedback = feedbackPatch->output();
×
108
    }
109

110
    m_mapTree->setSortingEnabled(true);
×
111
    m_mapTree->sortByColumn(KMapColumnPluginName, Qt::AscendingOrder);
×
112

113
    /* Setup UI controls */
114
    setupMappingPage();
×
115
    setupProfilePage();
×
116

117
    QSettings settings;
×
118
    QVariant var = settings.value(SETTINGS_HOTPLUG);
×
119
    if (var.isValid() && var.toBool() == true)
×
120
        m_hotplugButton->setChecked(true);
×
121

122
    connect(m_hotplugButton, SIGNAL(toggled(bool)),
×
123
            this, SLOT(slotHotpluggingChanged(bool)));
124

125
    initAudioTab();
×
126

127
    /* Listen to itemChanged() signals to catch check state changes */
128
    connect(m_audioMapTree, SIGNAL(itemChanged(QTreeWidgetItem*, int)),
×
129
            this, SLOT(slotAudioDeviceItemChanged(QTreeWidgetItem*, int)));
130
    connect(m_srateCombo, SIGNAL(currentIndexChanged(int)),
×
131
            this, SLOT(slotSampleRateIndexChanged(int)));
132
    connect(m_chansCombo, SIGNAL(currentIndexChanged(int)),
×
133
            this, SLOT(slotAudioChannelsChanged(int)));
134
    connect(m_audioPreviewButton, SIGNAL(toggled(bool)),
×
135
            this, SLOT(slotAudioInputPreview(bool)));
136

137
    /* Select the top-most "None" item */
138
    m_mapTree->setCurrentItem(m_mapTree->topLevelItem(0));
×
139

140
    /* Listen to plugin configuration changes */
141
    connect(m_ioMap, SIGNAL(pluginConfigurationChanged(const QString&, bool)),
×
142
            this, SLOT(slotPluginConfigurationChanged(const QString&, bool)));
143
}
×
144

145
InputOutputPatchEditor::~InputOutputPatchEditor()
×
146
{
147
    if (m_audioPreviewButton->isChecked())
×
148
        m_audioPreviewButton->setChecked(false);
×
149
}
×
150

151
/****************************************************************************
152
 * Mapping page
153
 ****************************************************************************/
154

155
InputPatch* InputOutputPatchEditor::patch() const
×
156
{
157
    InputPatch* p = m_ioMap->inputPatch(m_universe);
×
158
    Q_ASSERT(p != NULL);
×
159
    return p;
×
160
}
161

162
QTreeWidgetItem* InputOutputPatchEditor::currentlyMappedItem() const
×
163
{
164
    for (int i = 0; i < m_mapTree->topLevelItemCount(); i++)
×
165
    {
166
        QTreeWidgetItem* pluginItem = m_mapTree->topLevelItem(i);
×
167
        Q_ASSERT(pluginItem != NULL);
×
168

169
        if (pluginItem->text(KMapColumnPluginName) == patch()->pluginName())
×
170
        {
171
            QTreeWidgetItem* inputItem = pluginItem->child(patch()->input());
×
172
            return inputItem;
×
173
        }
174
    }
175

176
    return NULL;
×
177
}
178

179
void InputOutputPatchEditor::setupMappingPage()
×
180
{
181
    /* Fill the map tree with available plugins */
182
    fillMappingTree();
×
183

184
    /* Selection changes */
185
    connect(m_mapTree, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
×
186
            this, SLOT(slotMapCurrentItemChanged(QTreeWidgetItem*)));
187

188
    /* Configure button */
189
    connect(m_configureButton, SIGNAL(clicked()),
×
190
            this, SLOT(slotConfigureInputClicked()));
191

192
    /* Double click acts as edit button click */
193
    connect(m_mapTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
×
194
            this, SLOT(slotConfigureInputClicked()));
195
}
×
196

197
void InputOutputPatchEditor::fillMappingTree()
×
198
{
199
    /* Disable check state change tracking when the tree is filled */
200
    disconnect(m_mapTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
×
201
               this, SLOT(slotMapItemChanged(QTreeWidgetItem*, int)));
202

203
    m_mapTree->clear();
×
204

205
    qDebug() << "[InputOutputPatchEditor] Fill tree for universe: " << m_universe;
×
206

207
    // Build a complete list of Input/Output plugins
208
    QStringList IOplugins = m_ioMap->inputPluginNames();
×
209
    foreach (QString out, m_ioMap->outputPluginNames())
×
210
        if (IOplugins.contains(out) == false)
×
211
            IOplugins.append(out);
×
212

213
    // cycle through available plugins
214
    foreach (QString pluginName, IOplugins)
×
215
    {
216
        QStringList inputs = m_ioMap->pluginInputs(pluginName);
×
217
        QStringList outputs = m_ioMap->pluginOutputs(pluginName);
×
218
        bool hasFeedback = m_ioMap->pluginSupportsFeedback(pluginName);
×
219
        QLCIOPlugin *plugin = m_doc->ioPluginCache()->plugin(pluginName);
×
220
        int lineNumber = 1;
×
221

222
        // 1st case: this plugin has no input or output
223
        if (inputs.length() == 0 && outputs.length() == 0)
×
224
        {
225
            QTreeWidgetItem* pitem = new QTreeWidgetItem(m_mapTree);
×
226
            pitem->setText(KMapColumnPluginName, pluginName);
×
227
            pitem->setText(KMapColumnDeviceName, KInputNone);
×
228
            pitem->setText(KMapColumnInputLine, QString("%1").arg(QLCIOPlugin::invalidLine()));
×
229
            pitem->setText(KMapColumnOutputLine, QString("%1").arg(QLCIOPlugin::invalidLine()));
×
230
        }
231
        else
232
        {
233
            // 2nd case: plugin with an input and maybe an output
234
            for (quint32 inputId = 0; inputId < quint32(inputs.length()); inputId++)
×
235
            {
236
                quint32 uni = m_ioMap->inputMapping(pluginName, inputId);
×
237
                QString inputName = inputs.at(inputId);
×
238
                //qDebug() << "Plugin: " << pluginName << ", deviceName: " << inputName << ", input: " << inputId << ", universe:" << uni;
239

240
                if (uni == InputOutputMap::invalidUniverse() ||
×
241
                   (uni == m_universe || plugin->capabilities() & QLCIOPlugin::Infinite))
×
242
                {
243
                    QTreeWidgetItem* pitem = new QTreeWidgetItem(m_mapTree);
×
244
                    pitem->setText(KMapColumnPluginName, pluginName);
×
245
                    pitem->setText(KMapColumnDeviceName, QString("%1: %2").arg(lineNumber++).arg(inputName));
×
246
                    pitem->setFlags(pitem->flags() | Qt::ItemIsUserCheckable);
×
247
                    if (m_currentInputPluginName == pluginName && m_currentInput == inputId)
×
248
                        pitem->setCheckState(KMapColumnHasInput, Qt::Checked);
×
249
                    else
250
                        pitem->setCheckState(KMapColumnHasInput, Qt::Unchecked);
×
251
                    pitem->setTextAlignment(KMapColumnHasInput, Qt::AlignHCenter);
×
252
                    pitem->setText(KMapColumnInputLine, QString("%1").arg(inputId));
×
253
                    pitem->setText(KMapColumnOutputLine, QString("%1").arg(QLCIOPlugin::invalidLine()));
×
254

255
                    // check if this plugin has also an output
256
                    int outLine = outputs.indexOf(inputName);
×
257
                    if (outLine != -1)
×
258
                    {
259
                        quint32 outUni = m_ioMap->outputMapping(pluginName, outLine);
×
260
                        //qDebug() << "Device" << inputName << "has also an output on line" << outLine;
261

262
                        if (outUni == InputOutputMap::invalidUniverse() ||
×
263
                           (outUni == m_universe || plugin->capabilities() & QLCIOPlugin::Infinite))
×
264
                        {
265
                            if (m_currentOutputPluginName == pluginName && m_currentOutput == quint32(outLine))
×
266
                                pitem->setCheckState(KMapColumnHasOutput, Qt::Checked);
×
267
                            else
268
                                pitem->setCheckState(KMapColumnHasOutput, Qt::Unchecked);
×
269
                            pitem->setText(KMapColumnOutputLine, QString("%1").arg(outLine));
×
270
                            // add feedback
271
                            if (hasFeedback)
×
272
                            {
273
                                if (m_currentFeedbackPluginName == pluginName && m_currentFeedback == quint32(outLine))
×
274
                                    pitem->setCheckState(KMapColumnHasFeedback, Qt::Checked);
×
275
                                else
276
                                    pitem->setCheckState(KMapColumnHasFeedback, Qt::Unchecked);
×
277
                            }
278
                        }
279
                    }
280
                }
281
                else // input is mapped to different universe, let's check if outputs are available
282
                {
283
                    // check if this plugin has also an output
284
                    int outLine = outputs.indexOf(inputName);
×
285
                    if (outLine != -1)
×
286
                    {
287
                        quint32 outUni = m_ioMap->outputMapping(pluginName, outLine);
×
288
                        //qDebug() << "Device" << inputName << "has also an output on line" << outLine;
289

290
                        if (outUni == InputOutputMap::invalidUniverse() ||
×
291
                           (outUni == m_universe || plugin->capabilities() & QLCIOPlugin::Infinite))
×
292
                        {
293
                            qDebug() << "Plugin: " << pluginName << ", output: " << outLine << ", universe:" << outUni;
×
294
                            QTreeWidgetItem* pitem = new QTreeWidgetItem(m_mapTree);
×
295
                            pitem->setText(KMapColumnPluginName, pluginName);
×
296
                            pitem->setText(KMapColumnDeviceName, QString("%1: %2").arg(lineNumber++).arg(inputName));
×
297
                            pitem->setFlags(pitem->flags() | Qt::ItemIsUserCheckable);
×
298
                            if (m_currentOutputPluginName == pluginName && m_currentOutput == quint32(outLine))
×
299
                                pitem->setCheckState(KMapColumnHasOutput, Qt::Checked);
×
300
                            else
301
                                pitem->setCheckState(KMapColumnHasOutput, Qt::Unchecked);
×
302
                            // add feedback
303
                            if (hasFeedback)
×
304
                            {
305
                                if (m_currentFeedbackPluginName == pluginName && m_currentFeedback == quint32(outLine))
×
306
                                    pitem->setCheckState(KMapColumnHasFeedback, Qt::Checked);
×
307
                                else
308
                                    pitem->setCheckState(KMapColumnHasFeedback, Qt::Unchecked);
×
309
                            }
310
                            pitem->setText(KMapColumnOutputLine, QString("%1").arg(outLine));
×
311
                            pitem->setText(KMapColumnInputLine, QString("%1").arg(QLCIOPlugin::invalidLine()));
×
312
                        }
313
                    }
314
                }
315
            }
316
            // 3rd case: output only plugins
317
            for (quint32 outputId = 0; outputId < quint32(outputs.length()); outputId++)
×
318
            {
319
                QString outputName = outputs.at(outputId);
×
320
                if (inputs.contains(outputName) == false)
×
321
                {
322
                    quint32 outUni = m_ioMap->outputMapping(pluginName, outputId);
×
323
                    if (outUni == InputOutputMap::invalidUniverse() ||
×
324
                        (outUni == m_universe || plugin->capabilities() & QLCIOPlugin::Infinite))
×
325
                    {
326
                        //qDebug() << "Plugin: " << pluginName << ", deviceName: " << outputName << ", output: " << outputId << ", universe:" << outUni;
327
                        QTreeWidgetItem* pitem = new QTreeWidgetItem(m_mapTree);
×
328
                        pitem->setText(KMapColumnPluginName, pluginName);
×
329
                        pitem->setText(KMapColumnDeviceName, QString("%1: %2").arg(lineNumber++).arg(outputName));
×
330
                        pitem->setFlags(pitem->flags() | Qt::ItemIsUserCheckable);
×
331
                        if (m_currentOutputPluginName == pluginName && m_currentOutput == outputId)
×
332
                            pitem->setCheckState(KMapColumnHasOutput, Qt::Checked);
×
333
                        else
334
                            pitem->setCheckState(KMapColumnHasOutput, Qt::Unchecked);
×
335
                        // add feedback
336
                        if (hasFeedback)
×
337
                        {
338
                            if (m_currentFeedbackPluginName == pluginName && m_currentFeedback == outputId)
×
339
                                pitem->setCheckState(KMapColumnHasFeedback, Qt::Checked);
×
340
                            else
341
                                pitem->setCheckState(KMapColumnHasFeedback, Qt::Unchecked);
×
342
                        }
343
                        pitem->setText(KMapColumnOutputLine, QString("%1").arg(outputId));
×
344
                        pitem->setText(KMapColumnInputLine, QString("%1").arg(QLCIOPlugin::invalidLine()));
×
345
                    }
346
                }
347
            }
348
        }
349
    }
350

351
    m_mapTree->resizeColumnToContents(KMapColumnPluginName);
×
352
    m_mapTree->resizeColumnToContents(KMapColumnDeviceName);
×
353

354
    /* Enable check state change tracking after the tree has been filled */
355
    connect(m_mapTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
×
356
            this, SLOT(slotMapItemChanged(QTreeWidgetItem*,int)));
357
}
×
358

359
void InputOutputPatchEditor::slotMapCurrentItemChanged(QTreeWidgetItem* item)
×
360
{
361
    QString info;
×
362
    bool configurable;
363

364
    if (item == NULL)
×
365
    {
366
        info = m_ioMap->inputPluginStatus(QString(), 0);
×
367
        info += m_ioMap->outputPluginStatus(QString(), 0);
×
368
        configurable = false;
×
369
    }
370
    else
371
    {
372
        QString plugin;
×
373
        quint32 input;
374
        quint32 output;
375

376
        /* Input node selected */
377
        plugin = item->text(KMapColumnPluginName);
×
378
        input = item->text(KMapColumnInputLine).toUInt();
×
379
        output = item->text(KMapColumnOutputLine).toUInt();
×
380

381
        info = m_ioMap->pluginDescription(plugin);
×
382

383
        //if (input != QLCIOPlugin::invalidLine())
384
            info += m_ioMap->inputPluginStatus(plugin, input);
×
385
        //if (output != QLCIOPlugin::invalidLine())
386
            info += m_ioMap->outputPluginStatus(plugin, output);
×
387
        configurable = m_ioMap->canConfigurePlugin(plugin);
×
388
    }
389

390
    /* Display information for the selected plugin or input */
391
    m_infoBrowser->setText(info);
×
392

393
    /* Enable configuration if plugin supports it */
394
    m_configureButton->setEnabled(configurable);
×
395
}
×
396

397
void InputOutputPatchEditor::slotMapItemChanged(QTreeWidgetItem* item, int col)
×
398
{
399
    if (item == NULL)
×
400
        return;
×
401

402
    /* Temporarily disable this signal to prevent an endless loop */
403
    disconnect(m_mapTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
×
404
               this, SLOT(slotMapItemChanged(QTreeWidgetItem*, int)));
405

406
    if (item->checkState(col) == Qt::Checked)
×
407
    {
408
        /* Set all other items unchecked... */
409
        QTreeWidgetItemIterator it(m_mapTree);
×
410
        while ((*it) != NULL)
×
411
        {
412
            /* Don't touch the item that was just checked */
413
            if (*it != item && (*it)->checkState(col))
×
414
            {
415
                /* Set all the rest of the nodes unchecked */
416
                (*it)->setCheckState(col, Qt::Unchecked);
×
417
            }
418
            ++it;
×
419
        }
420
        if (col == KMapColumnHasInput)
×
421
        {
422
            /* Store the selected plugin name & input */
423
            m_currentInputPluginName = item->text(KMapColumnPluginName);
×
424
            m_currentInput = item->text(KMapColumnInputLine).toInt();
×
425

426
            /* Apply the patch immediately so that input data can be used in the
427
               input profile editor */
428
            if (m_ioMap->setInputPatch(m_universe, m_currentInputPluginName, "",
×
429
                                       m_currentInput, m_currentProfileName) == false)
×
430
                showPluginMappingError();
×
431
        }
432
        else if (col == KMapColumnHasOutput)
×
433
        {
434
            if (item->checkState(KMapColumnHasFeedback) == Qt::Checked)
×
435
            {
436
                item->setCheckState(KMapColumnHasOutput, Qt::Unchecked);
×
437
                QMessageBox::warning(this, tr("Error"),
×
438
                                     tr("Output line already assigned"));
×
439
            }
440
            else
441
            {
442
                /* Store the selected plugin name & line */
443
                m_currentOutputPluginName = item->text(KMapColumnPluginName);
×
444
                m_currentOutput = item->text(KMapColumnOutputLine).toUInt();
×
445

446
                /* Apply the patch immediately */
447
                if (m_ioMap->setOutputPatch(m_universe, m_currentOutputPluginName, "",
×
448
                                            m_currentOutput, false) == false)
×
449
                    showPluginMappingError();
×
450
            }
451
        }
452
        else if (col == KMapColumnHasFeedback)
×
453
        {
454
            if (item->checkState(KMapColumnHasOutput) == Qt::Checked)
×
455
            {
456
                item->setCheckState(KMapColumnHasFeedback, Qt::Unchecked);
×
457
                QMessageBox::warning(this, tr("Error"),
×
458
                                     tr("Output line already assigned"));
×
459
            }
460
            else
461
            {
462
                m_currentFeedbackPluginName = item->text(KMapColumnPluginName);
×
463
                m_currentFeedback = item->text(KMapColumnOutputLine).toUInt();
×
464

465
                /* Apply the patch immediately */
466
                if (m_ioMap->setOutputPatch(m_universe, m_currentFeedbackPluginName, "",
×
467
                                            m_currentFeedback, true) == false)
×
468
                    showPluginMappingError();
×
469
            }
470
        }
471
    }
472
    else
473
    {
474
        /* Unchecked action. Set the patch to none */
475
        if (col == KMapColumnHasInput)
×
476
        {
477
            m_currentInputPluginName = KInputNone;
×
478
            m_currentInput = QLCIOPlugin::invalidLine();
×
479

480
            if (m_ioMap->setInputPatch(m_universe, m_currentInputPluginName, "", m_currentInput) == false)
×
481
                showPluginMappingError();
×
482
        }
483
        else if (col == KMapColumnHasOutput)
×
484
        {
485
            m_currentOutputPluginName = KInputNone;
×
486
            m_currentOutput = QLCIOPlugin::invalidLine();
×
487

488
            /* Apply the patch immediately */
489
            if (m_ioMap->setOutputPatch(m_universe, m_currentOutputPluginName, "", m_currentOutput, false) == false)
×
490
                showPluginMappingError();
×
491
        }
492
        else if (col == KMapColumnHasFeedback)
×
493
        {
494
            m_currentFeedbackPluginName = KInputNone;
×
495
            m_currentFeedback = QLCIOPlugin::invalidLine();
×
496

497
            /* Apply the patch immediately */
498
            if (m_ioMap->setOutputPatch(m_universe, m_currentFeedbackPluginName, "", m_currentFeedback, true) == false)
×
499
                showPluginMappingError();
×
500
        }
501
    }
502

503
    slotMapCurrentItemChanged(item);
×
504

505
    /* Start listening to this signal once again */
506
    connect(m_mapTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
×
507
            this, SLOT(slotMapItemChanged(QTreeWidgetItem*, int)));
508

509
    emit mappingChanged();
×
510
}
511

512
void InputOutputPatchEditor::slotConfigureInputClicked()
×
513
{
514
    QTreeWidgetItem* item;
515
    QString plugin;
×
516

517
    /* Find out the currently selected plugin */
518
    item = m_mapTree->currentItem();
×
519
    if (item == NULL)
×
520
        return;
×
521
    else
522
        plugin = item->text(KMapColumnPluginName);
×
523

524
    /* Configure the plugin. Changes in plugin outputs are handled with
525
       slotPluginConfigurationChanged(). */
526
    m_ioMap->configurePlugin(plugin);
×
527
}
528

529
void InputOutputPatchEditor::slotPluginConfigurationChanged(const QString& pluginName, bool success)
×
530
{
531
    if (success == false)
×
532
    {
533
        showPluginMappingError();
×
534
        return;
×
535
    }
536

537
    QTreeWidgetItem* item = pluginItem(pluginName);
×
538
    if (item == NULL)
×
539
        return;
×
540

541
    fillMappingTree();
×
542
}
543

544
void InputOutputPatchEditor::slotHotpluggingChanged(bool checked)
×
545
{
546
    QSettings settings;
×
547
    settings.setValue(SETTINGS_HOTPLUG, checked);
×
548
}
×
549

550
QTreeWidgetItem* InputOutputPatchEditor::pluginItem(const QString& pluginName)
×
551
{
552
    for (int i = 0; i < m_mapTree->topLevelItemCount(); i++)
×
553
    {
554
        QTreeWidgetItem* item = m_mapTree->topLevelItem(i);
×
555
        if (item->text(KMapColumnPluginName) == pluginName)
×
556
            return item;
×
557
    }
558

559
    return NULL;
×
560
}
561

562
void InputOutputPatchEditor::showPluginMappingError()
×
563
{
564
    QMessageBox::critical(this, tr("Error"),
×
565
                          tr("An error occurred while trying to open the selected device line.\n"
×
566
                             "This can be caused either by a wrong system configuration or "
567
                             "an unsupported input/output mode.\n"
568
                             "Please refer to the plugins documentation to troubleshoot this."),
569
                          QMessageBox::Close);
570
}
×
571

572
/****************************************************************************
573
 * Profile tree
574
 ****************************************************************************/
575

576
void InputOutputPatchEditor::setupProfilePage()
×
577
{
578
    connect(m_addProfileButton, SIGNAL(clicked()),
×
579
            this, SLOT(slotAddProfileClicked()));
580
    connect(m_removeProfileButton, SIGNAL(clicked()),
×
581
            this, SLOT(slotRemoveProfileClicked()));
582
    connect(m_editProfileButton, SIGNAL(clicked()),
×
583
            this, SLOT(slotEditProfileClicked()));
584

585
    /* Fill the profile tree with available profile names */
586
    fillProfileTree();
×
587

588
    /* Listen to itemChanged() signals to catch check state changes */
589
    connect(m_profileTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
×
590
            this, SLOT(slotProfileItemChanged(QTreeWidgetItem*)));
591

592
    /* Double click acts as edit button click */
593
    connect(m_profileTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
×
594
            this, SLOT(slotEditProfileClicked()));
595
}
×
596

597
void InputOutputPatchEditor::fillProfileTree()
×
598
{
599
    QTreeWidgetItem* item;
600

601
    m_profileTree->clear();
×
602

603
    /* Add an option for having no profile at all */
604
    item = new QTreeWidgetItem(m_profileTree);
×
605
    updateProfileItem(KInputNone, item);
×
606

607
    /* Insert available input profiles to the tree */
608
    QStringListIterator it(m_ioMap->profileNames());
×
609
    while (it.hasNext() == true)
×
610
    {
611
        item = new QTreeWidgetItem(m_profileTree);
×
612
        updateProfileItem(it.next(), item);
×
613
    }
614
    m_profileTree->resizeColumnToContents(KProfileColumnName);
×
615
}
×
616

617
void InputOutputPatchEditor::updateProfileItem(const QString& name, QTreeWidgetItem* item)
×
618
{
619
    Q_ASSERT(item != NULL);
×
620

621
    item->setText(KProfileColumnName, name);
×
622
    QLCInputProfile * prof = m_ioMap->profile(name);
×
623
    if (prof)
×
624
    {
625
        item->setText(KProfileColumnType, QLCInputProfile::typeToString(prof->type()));
×
626
    }
627

628
    item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
×
629
    if (m_currentProfileName == name)
×
630
        item->setCheckState(KProfileColumnName, Qt::Checked);
×
631
    else
632
        item->setCheckState(KProfileColumnName, Qt::Unchecked);
×
633
}
×
634

635
QString InputOutputPatchEditor::fullProfilePath(const QString& manufacturer,
×
636
                                          const QString& model) const
637
{
638
    QDir dir(InputOutputMap::userProfileDirectory());
×
639
    QString path = QString("%1/%2-%3%4").arg(dir.absolutePath())
×
640
                                        .arg(manufacturer).arg(model)
×
641
                                        .arg(KExtInputProfile);
×
642

643
    return path;
×
644
}
645

646
void InputOutputPatchEditor::slotProfileItemChanged(QTreeWidgetItem* item)
×
647
{
648
    if (item->checkState(KProfileColumnName) == Qt::Checked)
×
649
    {
650
        /* Temporarily disable this signal to prevent an endless loop */
651
        disconnect(m_profileTree,
×
652
                   SIGNAL(itemChanged(QTreeWidgetItem*,int)),
653
                   this,
654
                   SLOT(slotProfileItemChanged(QTreeWidgetItem*)));
655

656
        /* Set all other items unchecked... */
657
        QTreeWidgetItemIterator it(m_profileTree);
×
658
        while (*it != NULL)
×
659
        {
660
            /* ...except the one that was just checked */
661
            if (*it != item)
×
662
                (*it)->setCheckState(KProfileColumnName,
×
663
                                     Qt::Unchecked);
664
            ++it;
×
665
        }
666

667
        /* Start listening to this signal once again */
668
        connect(m_profileTree,
×
669
                SIGNAL(itemChanged(QTreeWidgetItem*,int)),
670
                this,
671
                SLOT(slotProfileItemChanged(QTreeWidgetItem*)));
672
    }
673
    else
674
    {
675
        /* Don't allow unchecking an item by clicking it. Only allow
676
           changing the check state by checking another item. */
677
        item->setCheckState(KProfileColumnName, Qt::Checked);
×
678
    }
679

680
    /* Store the selected profile name */
681
    m_currentProfileName = item->text(KProfileColumnName);
×
682

683
    /* Apply the patch immediately */
684
    //if (m_ioMap->setInputPatch(m_universe, m_currentInputPluginName, m_currentInput, m_currentProfileName) == false)
685
    if (m_ioMap->setInputProfile(m_universe, m_currentProfileName) == false)
×
686
        showPluginMappingError();
×
687

688
    emit mappingChanged();
×
689
}
×
690

691
void InputOutputPatchEditor::slotAddProfileClicked()
×
692
{
693
    /* Create a new input profile and start editing it */
694
    InputProfileEditor ite(this, NULL, m_ioMap);
×
695
edit:
×
696
    if (ite.exec() == QDialog::Accepted)
×
697
    {
698
        /* Remove spaces from these */
699
        QString manufacturer = ite.profile()->manufacturer().remove(QChar(' '));
×
700
        QString model = ite.profile()->model().remove(QChar(' '));
×
701
        QString path = fullProfilePath(manufacturer, model);
×
702

703
        /* If another profile with the same name exists, ask permission to overwrite */
704
        if (QFile::exists(path) == true && path != ite.profile()->path())
×
705
        {
706
            int r = QMessageBox::warning(this, tr("Existing Input Profile"),
×
707
                    tr("An input profile at %1 already exists. Do you wish to overwrite it?").arg(path),
×
708
                    QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
×
709
                    QMessageBox::No);
710
            if (r == QMessageBox::Cancel)
×
711
            {
712
                goto edit;
×
713
            }
714
            else if (r == QMessageBox::No)
×
715
            {
716
                path = QFileDialog::getSaveFileName(this, tr("Save Input Profile"),
×
717
                                                    path, tr("Input Profiles (*.qxi)"));
×
718
                if (path.isEmpty() == true)
×
719
                    goto edit;
×
720
            }
721
        }
722

723
        /* Create a new non-const copy of the profile and
724
           reparent it to the input map */
725
        QLCInputProfile* profile = ite.profile()->createCopy();
×
726

727
        /* Save it to a file, go back to edit if save failed */
728
        if (profile->saveXML(path) == false)
×
729
        {
730
            QMessageBox::warning(this, tr("Saving failed"),
×
731
                                 tr("Unable to save the profile to %1")
×
732
                                 .arg(QDir::toNativeSeparators(path)));
×
733
            delete profile;
×
734
            goto edit;
×
735
        }
736
        else
737
        {
738
            /* Add the new profile to input map */
739
            m_ioMap->addProfile(profile);
×
740

741
            /* Add the new profile to our tree widget */
742
            QTreeWidgetItem* item;
743
            item = new QTreeWidgetItem(m_profileTree);
×
744
            updateProfileItem(profile->name(), item);
×
745
        }
746
    }
747
}
×
748

749
void InputOutputPatchEditor::slotRemoveProfileClicked()
×
750
{
751
    QLCInputProfile* profile;
752
    QTreeWidgetItem* item;
753
    QString name;
×
754
    int r;
755

756
    /* Find out the currently selected item */
757
    item = m_profileTree->currentItem();
×
758
    if (item == NULL)
×
759
        return;
×
760

761
    /* Get the currently selected profile object by its name */
762
    name = item->text(KProfileColumnName);
×
763
    profile = m_ioMap->profile(name);
×
764
    if (profile == NULL)
×
765
        return;
×
766

767
    /* Ask for user confirmation */
768
    r = QMessageBox::question(this, tr("Delete profile"),
×
769
                              tr("Do you wish to permanently delete profile \"%1\"?")
×
770
                              .arg(profile->name()),
×
771
                              QMessageBox::Yes, QMessageBox::No);
772
    if (r == QMessageBox::Yes)
×
773
    {
774
        /* Attempt to delete the file first */
775
        QFile file(profile->path());
×
776
        if (file.remove() == true)
×
777
        {
778
            if (item->checkState(KProfileColumnName) == Qt::Checked)
×
779
            {
780
                /* The currently assigned profile is removed,
781
                   so select "None" next. */
782
                QTreeWidgetItem* none;
783
                none = m_profileTree->topLevelItem(0);
×
784
                Q_ASSERT(none != NULL);
×
785
                none->setCheckState(KProfileColumnName,
×
786
                                    Qt::Checked);
787
            }
788

789
            /* Successful deletion. Remove the profile from
790
               input map and our tree widget */
791
            m_ioMap->removeProfile(name);
×
792
            delete item;
×
793
        }
794
        else
795
        {
796
            /* Annoy the user even more after deletion failure */
797
            QMessageBox::warning(this, tr("File deletion failed"),
×
798
                                 tr("Unable to delete file %1")
×
799
                                 .arg(file.errorString()));
×
800
        }
801
    }
802
}
803

804
void InputOutputPatchEditor::slotEditProfileClicked()
×
805
{
806
    QLCInputProfile* profile;
807
    QTreeWidgetItem* item;
808
    QString name;
×
809

810
    /* Get the currently selected item and bail out if nothing or "None"
811
       is selected */
812
    item = m_profileTree->currentItem();
×
813
    if (item == NULL || item->text(KProfileColumnName) == KInputNone)
×
814
        return;
×
815

816
    /* Get the currently selected profile by its name */
817
    name = item->text(KProfileColumnName);
×
818
    profile = m_ioMap->profile(name);
×
819
    if (profile == NULL)
×
820
        return;
×
821

822
    /* Edit the profile and update the item if OK was pressed */
823
    InputProfileEditor ite(this, profile, m_ioMap);
×
824
edit:
×
825
    if (ite.exec() == QDialog::Rejected)
×
826
        return;
×
827

828
    /* Copy the channel's contents from the editor's copy to
829
       the actual object (with QLCInputProfile::operator=()). */
830
    *profile = *ite.profile();
×
831

832
    /* Remove spaces from these */
833
    QString manufacturer = ite.profile()->manufacturer().remove(QChar(' '));
×
834
    QString model = ite.profile()->model().remove(QChar(' '));
×
835
    QString path = fullProfilePath(manufacturer, model);
×
836

837
    /* If another profile with the same name exists, ask permission to overwrite */
838
    if (QFile::exists(path) == true && path != ite.profile()->path())
×
839
    {
840
        int r = QMessageBox::warning(this, tr("Existing Input Profile"),
×
841
                tr("An input profile at %1 already exists. Do you wish to overwrite it?").arg(path),
×
842
                QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
×
843
                QMessageBox::No);
844
        if (r == QMessageBox::Cancel)
×
845
        {
846
            goto edit;
×
847
        }
848
        else if (r == QMessageBox::No)
×
849
        {
850
            path = QFileDialog::getSaveFileName(this, tr("Save Input Profile"),
×
851
                                                path, tr("Input Profiles (*.qxi)"));
×
852
            if (path.isEmpty() == true)
×
853
                goto edit;
×
854
        }
855
    }
856

857
    /* Save the profile */
858
    if (profile->saveXML(path) == true)
×
859
    {
860
        /* Get the profile's name from the profile itself
861
           since it may have changed making local variable
862
           "name" invalid */
863
        updateProfileItem(profile->name(), item);
×
864
    }
865
    else
866
    {
867
        QMessageBox::warning(this, tr("Saving failed"),
×
868
                             tr("Unable to save %1 to %2")
×
869
                             .arg(profile->name())
×
870
                             .arg(QDir::toNativeSeparators(path)));
×
871
        goto edit;
×
872
    }
873
}
874

875
/****************************************************************************
876
 * Audio tree
877
 ****************************************************************************/
878

879
void InputOutputPatchEditor::initAudioTab()
×
880
{
881
    QList<AudioDeviceInfo> devList = m_doc->audioPluginCache()->audioDevicesList();
×
882
    m_audioMapTree->clear();
×
883
    QSettings settings;
×
884
    QString inputName, outputName;
×
885
    bool inputFound = false, outputFound = false;
×
886

887
    QTreeWidgetItem* defItem = new QTreeWidgetItem(m_audioMapTree);
×
888
    defItem->setText(KAudioColumnDeviceName, tr("Default device"));
×
889
    defItem->setText(KAudioColumnPrivate, "__qlcplusdefault__");
×
890

891
    QVariant var = settings.value(SETTINGS_AUDIO_INPUT_DEVICE);
×
892
    if (var.isValid() == true)
×
893
        inputName = var.toString();
×
894

895
    var = settings.value(SETTINGS_AUDIO_OUTPUT_DEVICE);
×
896
    if (var.isValid() == true)
×
897
        outputName = var.toString();
×
898

NEW
899
    foreach (AudioDeviceInfo info, devList)
×
900
    {
901
        QTreeWidgetItem* item = new QTreeWidgetItem(m_audioMapTree);
×
902
        item->setText(KAudioColumnDeviceName, info.deviceName);
×
903
        item->setText(KAudioColumnPrivate, info.privateName);
×
904

905
        if (info.capabilities & AUDIO_CAP_INPUT)
×
906
        {
907
            if (info.privateName == inputName)
×
908
            {
909
                item->setCheckState(KAudioColumnHasInput, Qt::Checked);
×
910
                inputFound = true;
×
911
            }
912
            else
913
                item->setCheckState(KAudioColumnHasInput, Qt::Unchecked);
×
914
        }
915
        if (info.capabilities & AUDIO_CAP_OUTPUT)
×
916
        {
917
            if (info.privateName == outputName)
×
918
            {
919
                item->setCheckState(KAudioColumnHasOutput, Qt::Checked);
×
920
                outputFound = true;
×
921
            }
922
            else
923
                item->setCheckState(KAudioColumnHasOutput, Qt::Unchecked);
×
924
        }
925
    }
926

927
    if (inputFound == true)
×
928
        defItem->setCheckState(KAudioColumnHasInput, Qt::Unchecked);
×
929
    else
930
        defItem->setCheckState(KAudioColumnHasInput, Qt::Checked);
×
931

932
    if (outputFound == true)
×
933
        defItem->setCheckState(KAudioColumnHasOutput, Qt::Unchecked);
×
934
    else
935
        defItem->setCheckState(KAudioColumnHasOutput, Qt::Checked);
×
936

937
    m_audioMapTree->resizeColumnToContents(KAudioColumnDeviceName);
×
938

939
    var = settings.value(SETTINGS_AUDIO_INPUT_SRATE);
×
940
    if (var.isValid())
×
941
    {
942
        int srate = var.toInt();
×
943
        for (int i = 0; i < m_srateCombo->count(); i++)
×
944
        {
945
            if (m_srateCombo->itemText(i).toInt() == srate)
×
946
            {
947
                m_srateCombo->blockSignals(true);
×
948
                m_srateCombo->setCurrentIndex(i);
×
949
                m_srateCombo->blockSignals(false);
×
950
                break;
×
951
            }
952
        }
953
    }
954

955
    var = settings.value(SETTINGS_AUDIO_INPUT_CHANNELS);
×
956
    if (var.isValid())
×
957
    {
958
        int channels = var.toInt();
×
959
        if (channels == 2)
×
960
        {
961
            m_chansCombo->blockSignals(true);
×
962
            m_chansCombo->setCurrentIndex(1);
×
963
            m_chansCombo->blockSignals(false);
×
964
        }
965
    }
966
}
×
967

968
void InputOutputPatchEditor::slotAudioDeviceItemChanged(QTreeWidgetItem *item, int col)
×
969
{
970
    if (item == NULL)
×
971
        return;
×
972

973
    /* Temporarily disable this signal to prevent an endless loop */
974
    disconnect(m_audioMapTree, SIGNAL(itemChanged(QTreeWidgetItem*, int)),
×
975
               this, SLOT(slotAudioDeviceItemChanged(QTreeWidgetItem*, int)));
976

977
    QSettings settings;
×
978

979
    if (item->checkState(col) == Qt::Checked)
×
980
    {
981
        /* Set all other items unchecked... */
982
        QTreeWidgetItemIterator it(m_audioMapTree);
×
983
        while ((*it) != NULL)
×
984
        {
985
            /* Don't touch the item that was just checked */
986
            if (*it != item && (*it)->checkState(col))
×
987
            {
988
                /* Set all the rest of the nodes unchecked */
989
                (*it)->setCheckState(col, Qt::Unchecked);
×
990
            }
991
            ++it;
×
992
        }
993

994
        if (col == KAudioColumnHasInput)
×
995
        {
996
            if (item == m_audioMapTree->topLevelItem(0))
×
997
                settings.remove(SETTINGS_AUDIO_INPUT_DEVICE);
×
998
            else
999
                settings.setValue(SETTINGS_AUDIO_INPUT_DEVICE, QVariant(item->text(KAudioColumnPrivate)));
×
1000
            emit audioInputDeviceChanged();
×
1001
        }
1002
        else if (col == KAudioColumnHasOutput)
×
1003
        {
1004
            if (item == m_audioMapTree->topLevelItem(0))
×
1005
                settings.remove(SETTINGS_AUDIO_OUTPUT_DEVICE);
×
1006
            else
1007
                settings.setValue(SETTINGS_AUDIO_OUTPUT_DEVICE, QVariant(item->text(KAudioColumnPrivate)));
×
1008
        }
1009
    }
1010
    else
1011
    {
1012
        QTreeWidgetItem* defItem = m_audioMapTree->topLevelItem(0);
×
1013

1014
        if (col == KAudioColumnHasInput)
×
1015
        {
1016
            settings.remove(SETTINGS_AUDIO_INPUT_DEVICE);
×
1017
            defItem->setCheckState(KAudioColumnHasInput, Qt::Checked);
×
1018
            emit audioInputDeviceChanged();
×
1019
        }
1020
        else if (col == KAudioColumnHasOutput)
×
1021
        {
1022
            settings.remove(SETTINGS_AUDIO_OUTPUT_DEVICE);
×
1023
            defItem->setCheckState(KAudioColumnHasOutput, Qt::Checked);
×
1024
        }
1025
    }
1026

1027
    /* Start listening to this signal once again */
1028
    connect(m_audioMapTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
×
1029
            this, SLOT(slotAudioDeviceItemChanged(QTreeWidgetItem*,int)));
1030
}
1031

1032
void InputOutputPatchEditor::slotSampleRateIndexChanged(int index)
×
1033
{
1034
    QSettings settings;
×
1035
    int selectedRate = m_srateCombo->itemText(index).toInt();
×
1036
    if (selectedRate == AUDIO_DEFAULT_SAMPLE_RATE)
×
1037
        settings.remove(SETTINGS_AUDIO_INPUT_SRATE);
×
1038
    else
1039
        settings.setValue(SETTINGS_AUDIO_INPUT_SRATE, selectedRate);
×
1040
    if (m_audioPreviewButton->isChecked())
×
1041
        m_audioPreviewButton->setChecked(false);
×
1042
    emit audioInputDeviceChanged();
×
1043
}
×
1044

1045
void InputOutputPatchEditor::slotAudioChannelsChanged(int index)
×
1046
{
1047
    QSettings settings;
×
1048
    int channels = (index == 0) ? 1: 2;
×
1049
    if (channels == AUDIO_DEFAULT_CHANNELS)
×
1050
        settings.remove(SETTINGS_AUDIO_INPUT_CHANNELS);
×
1051
    else
1052
        settings.setValue(SETTINGS_AUDIO_INPUT_CHANNELS, channels);
×
1053
    if (m_audioPreviewButton->isChecked())
×
1054
        m_audioPreviewButton->setChecked(false);
×
1055
    emit audioInputDeviceChanged();
×
1056
}
×
1057

1058
void InputOutputPatchEditor::slotAudioInputPreview(bool enable)
×
1059
{
1060
    QSharedPointer<AudioCapture> capture(m_doc->audioInputCapture());
×
1061
    m_inputCapture = capture.data();
×
1062

1063
    if (enable == true)
×
1064
    {
1065
        connect(m_inputCapture, SIGNAL(dataProcessed(double*,int,double,quint32)),
×
1066
                this, SLOT(slotAudioUpdateLevel(double*,int,double,quint32)));
1067
        m_inputCapture->registerBandsNumber(FREQ_SUBBANDS_DEFAULT_NUMBER);
×
1068
    }
1069
    else
1070
    {
1071
        m_inputCapture->unregisterBandsNumber(FREQ_SUBBANDS_DEFAULT_NUMBER);
×
1072
        disconnect(m_inputCapture, SIGNAL(dataProcessed(double*,int,double,quint32)),
×
1073
                   this, SLOT(slotAudioUpdateLevel(double*,int,double,quint32)));
1074
    }
1075
}
×
1076

1077
void InputOutputPatchEditor::slotAudioUpdateLevel(double *spectrumBands, int size, double maxMagnitude, quint32 power)
×
1078
{
1079
    Q_UNUSED(spectrumBands)
1080
    Q_UNUSED(size)
1081
    Q_UNUSED(maxMagnitude)
1082

1083
    m_levelProgress->setValue(power);
×
1084
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc