• 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/fixtureremap.cpp
1
/*
2
  Q Light Controller Plus
3
  fixtureremap.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 <QXmlStreamReader>
21
#include <QProgressDialog>
22
#include <QFileDialog>
23
#include <QMessageBox>
24
#include <QScrollBar>
25
#include <QDebug>
26
#include <QDir>
27

28
#include "monitorproperties.h"
29
#include "vcaudiotriggers.h"
30
#include "virtualconsole.h"
31
#include "qlcfixturemode.h"
32
#include "qlcfixturedef.h"
33
#include "channelsgroup.h"
34
#include "fixtureremap.h"
35
#include "remapwidget.h"
36
#include "qlcchannel.h"
37
#include "addfixture.h"
38
#include "efxfixture.h"
39
#include "scenevalue.h"
40
#include "chaserstep.h"
41
#include "audiobar.h"
42
#include "vcslider.h"
43
#include "sequence.h"
44
#include "qlcfile.h"
45
#include "vcxypad.h"
46
#include "vcframe.h"
47
#include "chaser.h"
48
#include "scene.h"
49
#include "efx.h"
50
#include "doc.h"
51
#include "app.h"
52

53
#define KColumnName         0
54
#define KColumnAddress      1
55
#define KColumnUniverse     2
56
#define KColumnID           3
57
#define KColumnChIdx        4
58

59
FixtureRemap::FixtureRemap(Doc *doc, QWidget *parent)
×
60
    : QDialog(parent)
61
    , m_doc(doc)
×
62
{
63
    Q_ASSERT(doc != NULL);
×
64

65
    setupUi(this);
×
66

67

68
    connect(m_importButton, SIGNAL(clicked()),
×
69
            this, SLOT(slotImportFixtures()));
70
    connect(m_addButton, SIGNAL(clicked()),
×
71
            this, SLOT(slotAddTargetFixture()));
72
    connect(m_removeButton, SIGNAL(clicked()),
×
73
            this, SLOT(slotRemoveTargetFixture()));
74
    connect(m_cloneButton, SIGNAL(clicked()),
×
75
            this, SLOT(slotCloneSourceFixture()));
76
    connect(m_remapButton, SIGNAL(clicked()),
×
77
            this, SLOT(slotAddRemap()));
78
    connect(m_unmapButton, SIGNAL(clicked()),
×
79
            this, SLOT(slotRemoveRemap()));
80

81
    m_cloneButton->setEnabled(false);
×
82

83
    remapWidget = new RemapWidget(m_sourceTree, m_targetTree, this);
×
84
    remapWidget->show();
×
85
    m_remapLayout->addWidget(remapWidget);
×
86

87
    m_targetDoc = new Doc(this);
×
88
    /* Load user fixtures first so that they override system fixtures */
89
    m_targetDoc->fixtureDefCache()->load(QLCFixtureDefCache::userDefinitionDirectory());
×
90
    m_targetDoc->fixtureDefCache()->loadMap(QLCFixtureDefCache::systemDefinitionDirectory());
×
91

92
    /* Remove the default set of universes from the target Doc and re-fill it
93
     * with the current Doc universe list */
94
    m_targetDoc->inputOutputMap()->removeAllUniverses();
×
95

96
    int index = 0;
×
NEW
97
    foreach (Universe *uni, m_doc->inputOutputMap()->universes())
×
98
    {
99
        m_targetDoc->inputOutputMap()->addUniverse(uni->id());
×
100
        m_targetDoc->inputOutputMap()->setUniverseName(index, uni->name());
×
101
        m_targetDoc->inputOutputMap()->startUniverses();
×
102
        index++;
×
103
    }
104

105
    m_sourceTree->setIconSize(QSize(24, 24));
×
106
    m_sourceTree->setAllColumnsShowFocus(true);
×
107
    fillFixturesTree(m_doc, m_sourceTree);
×
108

109
    m_targetTree->setIconSize(QSize(24, 24));
×
110
    m_targetTree->setAllColumnsShowFocus(true);
×
111

112
    connect(m_sourceTree->verticalScrollBar(), SIGNAL(valueChanged(int)),
×
113
            this, SLOT(slotUpdateConnections()));
114
    connect(m_sourceTree, SIGNAL(clicked(QModelIndex)),
×
115
            this, SLOT(slotUpdateConnections()));
116
    connect(m_sourceTree, SIGNAL(expanded(QModelIndex)),
×
117
            this, SLOT(slotUpdateConnections()));
118
    connect(m_sourceTree, SIGNAL(collapsed(QModelIndex)),
×
119
            this, SLOT(slotUpdateConnections()));
120
    connect(m_sourceTree, SIGNAL(itemSelectionChanged()),
×
121
            this, SLOT(slotSourceSelectionChanged()));
122

123
    connect(m_targetTree->verticalScrollBar(), SIGNAL(valueChanged(int)),
×
124
            this, SLOT(slotUpdateConnections()));
125
    connect(m_targetTree, SIGNAL(clicked(QModelIndex)),
×
126
            this, SLOT(slotUpdateConnections()));
127
    connect(m_targetTree, SIGNAL(expanded(QModelIndex)),
×
128
            this, SLOT(slotUpdateConnections()));
129
    connect(m_targetTree, SIGNAL(collapsed(QModelIndex)),
×
130
            this, SLOT(slotUpdateConnections()));
131

132
    // retrieve the original project name from the QLC+ App class
133
    App *mainApp = (App *)m_doc->parent();
×
134
    QString prjName = mainApp->fileName();
×
135

136
    if (prjName.lastIndexOf(".") > 0)
×
137
        prjName.insert(prjName.lastIndexOf("."), tr(" (remapped)"));
×
138
    else
139
        prjName.append(tr(" (remapped)"));
×
140

141
    m_targetProjectLabel->setText(prjName);
×
142
}
×
143

144
FixtureRemap::~FixtureRemap()
×
145
{
146
    delete m_targetDoc;
×
147
}
×
148

149
QTreeWidgetItem *FixtureRemap::getUniverseItem(Doc *doc, quint32 universe, QTreeWidget *tree)
×
150
{
151
    QTreeWidgetItem *topItem = NULL;
×
152

153
    for (int i = 0; i < tree->topLevelItemCount(); i++)
×
154
    {
155
        QTreeWidgetItem* tItem = tree->topLevelItem(i);
×
156
        quint32 tUni = tItem->text(KColumnUniverse).toUInt();
×
157
        if (tUni == universe)
×
158
        {
159
            topItem = tItem;
×
160
            break;
×
161
        }
162
    }
163

164
    // Haven't found this universe node ? Create it.
165
    if (topItem == NULL)
×
166
    {
167
        topItem = new QTreeWidgetItem(tree);
×
168
        topItem->setText(KColumnName, doc->inputOutputMap()->universes().at(universe)->name());
×
169
        topItem->setText(KColumnUniverse, QString::number(universe));
×
170
        topItem->setText(KColumnID, QString::number(Function::invalidId()));
×
171
        topItem->setExpanded(true);
×
172
    }
173

174
    return topItem;
×
175
}
176

177
void FixtureRemap::fillFixturesTree(Doc *doc, QTreeWidget *tree)
×
178
{
NEW
179
    foreach (Fixture *fxi, doc->fixtures())
×
180
    {
181
        quint32 uni = fxi->universe();
×
182
        QTreeWidgetItem *topItem = getUniverseItem(doc, uni, tree);
×
183

184
        quint32 baseAddr = fxi->address();
×
185
        QTreeWidgetItem *fItem = new QTreeWidgetItem(topItem);
×
186
        fItem->setText(KColumnName, fxi->name());
×
187
        fItem->setIcon(KColumnName, fxi->getIconFromType());
×
188
        fItem->setText(KColumnAddress, QString("%1 - %2").arg(baseAddr + 1).arg(baseAddr + fxi->channels()));
×
189
        fItem->setText(KColumnUniverse, QString::number(uni));
×
190
        fItem->setText(KColumnID, QString::number(fxi->id()));
×
191

192
        for (quint32 c = 0; c < fxi->channels(); c++)
×
193
        {
194
            const QLCChannel* channel = fxi->channel(c);
×
195
            QTreeWidgetItem *item = new QTreeWidgetItem(fItem);
×
196
            item->setText(KColumnName, QString("%1:%2").arg(c + 1)
×
197
                          .arg(channel->name()));
×
198
            item->setIcon(KColumnName, channel->getIcon());
×
199
            item->setText(KColumnUniverse, QString::number(uni));
×
200
            item->setText(KColumnID, QString::number(fxi->id()));
×
201
            item->setText(KColumnChIdx, QString::number(c));
×
202
        }
203
    }
204

205
    tree->resizeColumnToContents(KColumnName);
×
206
}
×
207

208
QString FixtureRemap::createImportDialog()
×
209
{
210
    QString fileName;
×
211

212
    /* Create a file save dialog */
213
    QFileDialog dialog(this);
×
214
    dialog.setWindowTitle(tr("Import Fixtures List"));
×
215
    dialog.setAcceptMode(QFileDialog::AcceptOpen);
×
216

217
    /* Append file filters to the dialog */
218
    QStringList filters;
×
219
    filters << tr("Fixtures List (*%1)").arg(KExtFixtureList);
×
220
#if defined(WIN32) || defined(Q_OS_WIN)
221
    filters << tr("All Files (*.*)");
222
#else
223
    filters << tr("All Files (*)");
×
224
#endif
225
    dialog.setNameFilters(filters);
×
226

227
    /* Append useful URLs to the dialog */
228
    QList <QUrl> sidebar;
×
229
    sidebar.append(QUrl::fromLocalFile(QDir::homePath()));
×
230
    sidebar.append(QUrl::fromLocalFile(QDir::rootPath()));
×
231
    dialog.setSidebarUrls(sidebar);
×
232

233
    /* Get file name */
234
    if (dialog.exec() != QDialog::Accepted)
×
235
        return "";
×
236

237
    fileName = dialog.selectedFiles().first();
×
238
    if (fileName.isEmpty() == true)
×
239
        return "";
×
240

241
    return fileName;
×
242
}
243

244
void FixtureRemap::slotImportFixtures()
×
245
{
246
    QString fileName = createImportDialog();
×
247

248
    QMessageBox msgBox;
×
249
    msgBox.setText(tr("Do you want to automatically connect fixtures with the same name?"));
×
250
    msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
×
251
    msgBox.setDefaultButton(QMessageBox::Yes);
×
252
    bool autoConnect = msgBox.exec() == QMessageBox::Yes ? true : false;
×
253

254
    QXmlStreamReader *doc = QLCFile::getXMLReader(fileName);
×
255
    if (doc == NULL || doc->device() == NULL || doc->hasError())
×
256
    {
257
        qWarning() << Q_FUNC_INFO << "Unable to read from" << fileName;
×
258
        return;
×
259
    }
260

261
    while (!doc->atEnd())
×
262
    {
263
        if (doc->readNext() == QXmlStreamReader::DTD)
×
264
            break;
×
265
    }
266
    if (doc->hasError())
×
267
    {
268
        QLCFile::releaseXMLReader(doc);
×
269
        return;
×
270
    }
271

272
    if (doc->dtdName() == KXMLQLCFixturesList)
×
273
    {
274
        doc->readNextStartElement();
×
275
        if (doc->name() != KXMLQLCFixturesList)
×
276
        {
277
            qWarning() << Q_FUNC_INFO << "Fixture Definition node not found";
×
278
            QLCFile::releaseXMLReader(doc);
×
279
            return;
×
280
        }
281

282
        while (doc->readNextStartElement())
×
283
        {
284
            if (doc->name() == KXMLFixture)
×
285
            {
286
                Fixture* fxi = new Fixture(m_targetDoc);
×
287
                Q_ASSERT(fxi != NULL);
×
288

289
                if (fxi->loadXML(*doc, m_targetDoc, m_doc->fixtureDefCache()) == true)
×
290
                {
291
                    if (m_targetDoc->addFixture(fxi) == false)
×
292
                    {
293
                        qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name() << "cannot be created.";
×
294
                        delete fxi;
×
295
                    }
296
                }
297
                else
298
                {
299
                    qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name() << "cannot be loaded.";
×
300
                    delete fxi;
×
301
                }
302
            }
303
            else if (doc->name() == KXMLQLCFixtureGroup)
×
304
            {
305
                FixtureGroup* grp = new FixtureGroup(m_targetDoc);
×
306
                Q_ASSERT(grp != NULL);
×
307

308
                if (grp->loadXML(*doc) == true)
×
309
                {
310
                    m_targetDoc->addFixtureGroup(grp, grp->id());
×
311
                }
312
                else
313
                {
314
                    qWarning() << Q_FUNC_INFO << "FixtureGroup" << grp->name() << "cannot be loaded.";
×
315
                    delete grp;
×
316
                }
317
            }
318
            else
319
            {
320
                qWarning() << Q_FUNC_INFO << "Unknown label tag:" << doc->name().toString();
×
321
                doc->skipCurrentElement();
×
322
            }
323
        }
324
        fillFixturesTree(m_targetDoc, m_targetTree);
×
325

326
        if (autoConnect)
×
327
        {
328
            for (int tu = 0; tu < m_targetTree->topLevelItemCount(); tu++)
×
329
            {
330
                QTreeWidgetItem *tgtUniItem = m_targetTree->topLevelItem(tu);
×
331

332
                for (int ti = 0; ti < tgtUniItem->childCount(); ti++)
×
333
                {
334
                    QTreeWidgetItem *tgtItem = tgtUniItem->child(ti);
×
335

336
                    for (int su = 0; su < m_sourceTree->topLevelItemCount(); su++)
×
337
                    {
338
                        QTreeWidgetItem *srcUniItem = m_sourceTree->topLevelItem(su);
×
339

340
                        for (int si = 0; si < srcUniItem->childCount(); si++)
×
341
                        {
342
                            QTreeWidgetItem *srcItem = srcUniItem->child(si);
×
343

344
                            if (srcItem->text(KColumnName) == tgtItem->text(KColumnName))
×
345
                            {
346
                                connectFixtures(srcItem, tgtItem);
×
347
                                break;
×
348
                            }
349
                        }
350
                    }
351
                }
352
            }
353
            remapWidget->setRemapList(m_remapList);
×
354
        }
355
    }
356
    QLCFile::releaseXMLReader(doc);
×
357
}
358

359
void FixtureRemap::slotAddTargetFixture()
×
360
{
361
    AddFixture af(this, m_targetDoc);
×
362
    if (af.exec() == QDialog::Rejected)
×
363
        return;
×
364

365
    QString name = af.name();
×
366
    quint32 address = af.address();
×
367
    quint32 universe = af.universe();
×
368
    quint32 channels = af.channels();
×
369
    QLCFixtureDef* fixtureDef = af.fixtureDef();
×
370
    QLCFixtureMode* mode = af.mode();
×
371
    int gap = af.gap();
×
372

NEW
373
    for (int i = 0; i < af.amount(); i++)
×
374
    {
375
        QString modname;
×
376

377
        /* If an empty name was given use the model instead */
378
        if (name.simplified().isEmpty())
×
379
        {
380
            if (fixtureDef != NULL)
×
381
                name = fixtureDef->model();
×
382
            else
383
                name = tr("Generic Dimmer");
×
384
        }
385

386
        /* If we're adding more than one fixture,
387
           append a number to the end of the name */
388
        if (af.amount() > 1)
×
389
            modname = QString("%1 #%2").arg(name).arg(i+1);
×
390
        else
391
            modname = name;
×
392

393
        /* Create the target fixture */
394
        Fixture* fxi = new Fixture(m_targetDoc);
×
395

396
        /* Add the first fixture without gap, at the given address */
397
        fxi->setAddress(address + (i * channels) + (i * gap));
×
398
        fxi->setUniverse(universe);
×
399
        fxi->setName(modname);
×
400

401
        /* Set a fixture definition & mode if they were selected.
402
           Otherwise assign channels to a generic dimmer. */
403
        if (fixtureDef != NULL && mode != NULL)
×
404
            fxi->setFixtureDefinition(fixtureDef, mode);
×
405
        else
406
        {
407
            fixtureDef = fxi->genericDimmerDef(channels);
×
408
            mode = fxi->genericDimmerMode(fixtureDef, channels);
×
409
            fxi->setFixtureDefinition(fixtureDef, mode);
×
410
            //fxi->setChannels(channels);
411
        }
412

413
        m_targetDoc->addFixture(fxi);
×
414

415
        QTreeWidgetItem *topItem = getUniverseItem(m_targetDoc, universe, m_targetTree);
×
416

417
        quint32 baseAddr = fxi->address();
×
418
        QTreeWidgetItem *fItem = new QTreeWidgetItem(topItem);
×
419
        fItem->setText(KColumnName, fxi->name());
×
420
        fItem->setIcon(KColumnName, fxi->getIconFromType());
×
421
        fItem->setText(KColumnAddress, QString("%1 - %2").arg(baseAddr + 1).arg(baseAddr + fxi->channels()));
×
422
        fItem->setText(KColumnUniverse, QString::number(universe));
×
423
        fItem->setText(KColumnID, QString::number(fxi->id()));
×
424

425
        for (quint32 c = 0; c < fxi->channels(); c++)
×
426
        {
427
            const QLCChannel* channel = fxi->channel(c);
×
428
            QTreeWidgetItem *item = new QTreeWidgetItem(fItem);
×
429
            item->setText(KColumnName, QString("%1:%2").arg(c + 1)
×
430
                          .arg(channel->name()));
×
431
            item->setIcon(KColumnName, channel->getIcon());
×
432
            item->setText(KColumnUniverse, QString::number(universe));
×
433
            item->setText(KColumnID, QString::number(fxi->id()));
×
434
            item->setText(KColumnChIdx, QString::number(c));
×
435
        }
436
    }
437
    m_targetTree->resizeColumnToContents(KColumnName);
×
438

439
    qDebug() << "Fixtures in target doc:" << m_targetDoc->fixtures().count();
×
440
}
441

442
void FixtureRemap::slotRemoveTargetFixture()
×
443
{
444
    if (m_targetTree->selectedItems().count() == 0)
×
445
        return;
×
446

447
    QTreeWidgetItem *item = m_targetTree->selectedItems().first();
×
448
    bool ok = false;
×
449
    quint32 fxid = item->text(KColumnID).toUInt(&ok);
×
450
    if (ok == false)
×
451
        return;
×
452

453
    // Ask before deletion
454
    if (QMessageBox::question(this, tr("Delete Fixtures"),
×
455
                              tr("Do you want to delete the selected items?"),
×
456
                              QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
×
457
    {
458
        return;
×
459
    }
460

461
    int i = 0;
×
462
    QListIterator <RemapInfo> it(m_remapList);
×
463
    while (it.hasNext() == true)
×
464
    {
465
        RemapInfo info = it.next();
×
466
        quint32 tgtID = info.target->text(KColumnID).toUInt();
×
467
        if (tgtID == fxid)
×
468
            m_remapList.takeAt(i);
×
469
        else
470
            i++;
×
471
    }
472
    remapWidget->setRemapList(m_remapList);
×
473
    m_targetDoc->deleteFixture(fxid);
×
474
    for (int i = 0; i < item->childCount(); i++)
×
475
    {
476
        QTreeWidgetItem *child = item->child(i);
×
477
        delete child;
×
478
    }
479
    delete item;
×
480
    m_targetTree->resizeColumnToContents(KColumnName);
×
481

482
    qDebug() << "Fixtures in target doc:" << m_targetDoc->fixtures().count();
×
483
}
484

485
void FixtureRemap::slotCloneSourceFixture()
×
486
{
487
    if (m_sourceTree->selectedItems().count() == 0)
×
488
        return; // popup here ??
×
489

490
    QTreeWidgetItem *sItem = m_sourceTree->selectedItems().first();
×
491
    quint32 fxID = sItem->text(KColumnID).toUInt();
×
492
    Fixture *srcFix = m_doc->fixture(fxID);
×
493
    if (srcFix == NULL)
×
494
        return; // popup here ?
×
495

496
    quint32 srcAddr = srcFix->universeAddress();
×
497
    for (quint32 i = srcAddr; i < srcAddr + srcFix->channels(); i++)
×
498
    {
499
        quint32 fxCheck = m_targetDoc->fixtureForAddress(i);
×
500
        if (fxCheck != Fixture::invalidId())
×
501
        {
502
            QMessageBox::warning(this,
×
503
                                 tr("Invalid operation"),
×
504
                                 tr("You are trying to clone a fixture on an address already in use. "
×
505
                                    "Please fix the target list first."));
506
            return;
×
507
        }
508
    }
509

510
    // create a copy of the fixture and add it to the target document
511
    /* Create the target fixture */
512
    Fixture* tgtFix = new Fixture(m_targetDoc);
×
513

514
    /* Add the first fixture without gap, at the given address */
515
    tgtFix->setAddress(srcFix->address());
×
516
    tgtFix->setUniverse(srcFix->universe());
×
517
    tgtFix->setName(srcFix->name());
×
518

519
    /* Set a fixture definition & mode if they were selected.
520
       Otherwise assign channels to a generic dimmer. */
521
    if (srcFix->fixtureDef()->manufacturer() == KXMLFixtureGeneric &&
×
522
        srcFix->fixtureDef()->model() == KXMLFixtureGeneric)
×
523
            tgtFix->setChannels(srcFix->channels());
×
524
    else
525
        tgtFix->setFixtureDefinition(srcFix->fixtureDef(), srcFix->fixtureMode());
×
526

527
    m_targetDoc->addFixture(tgtFix);
×
528

529
    // create the tree element and add it to the target tree
530
    QTreeWidgetItem *topItem = getUniverseItem(m_targetDoc, tgtFix->universe(), m_targetTree);
×
531
    quint32 baseAddr = tgtFix->address();
×
532
    QTreeWidgetItem *fItem = new QTreeWidgetItem(topItem);
×
533
    fItem->setText(KColumnName, tgtFix->name());
×
534
    fItem->setIcon(KColumnName, tgtFix->getIconFromType());
×
535
    fItem->setText(KColumnAddress, QString("%1 - %2").arg(baseAddr + 1).arg(baseAddr + tgtFix->channels()));
×
536
    fItem->setText(KColumnUniverse, QString::number(tgtFix->universe()));
×
537
    fItem->setText(KColumnID, QString::number(tgtFix->id()));
×
538

539
    for (quint32 c = 0; c < tgtFix->channels(); c++)
×
540
    {
541
        const QLCChannel* channel = tgtFix->channel(c);
×
542
        QTreeWidgetItem *item = new QTreeWidgetItem(fItem);
×
543
        item->setText(KColumnName, QString("%1:%2").arg(c + 1)
×
544
                      .arg(channel->name()));
×
545
        item->setIcon(KColumnName, channel->getIcon());
×
546
        item->setText(KColumnUniverse, QString::number(tgtFix->universe()));
×
547
        item->setText(KColumnID, QString::number(tgtFix->id()));
×
548
        item->setText(KColumnChIdx, QString::number(c));
×
549
    }
550

551
    m_targetTree->resizeColumnToContents(KColumnName);
×
552

NEW
553
    foreach (QTreeWidgetItem *it, m_targetTree->selectedItems())
×
554
        it->setSelected(false);
×
555
    fItem->setSelected(true);
×
556

557
    slotAddRemap();
×
558
}
559

560
void FixtureRemap::slotAddRemap()
×
561
{
562
    if (m_sourceTree->selectedItems().count() == 0 ||
×
563
        m_targetTree->selectedItems().count() == 0)
×
564
    {
565
        QMessageBox::warning(this,
×
566
                tr("Invalid selection"),
×
567
                tr("Please select a source and a target fixture or channel to perform this operation."));
×
568
        return;
×
569
    }
570

571
    connectFixtures(m_sourceTree->selectedItems().first(),
×
572
                    m_targetTree->selectedItems().first());
×
573

574
    remapWidget->setRemapList(m_remapList);
×
575
}
576

577
void FixtureRemap::connectFixtures(QTreeWidgetItem *sourceItem, QTreeWidgetItem *targetItem)
×
578
{
579
    if (sourceItem == NULL || targetItem == NULL)
×
580
        return;
×
581

582
    RemapInfo newRemap;
583
    newRemap.source = sourceItem;
×
584
    newRemap.target = targetItem;
×
585

586
    quint32 srcFxiID = newRemap.source->text(KColumnID).toUInt();
×
587
    Fixture *srcFxi = m_doc->fixture(srcFxiID);
×
588
    quint32 tgtFxiID = newRemap.target->text(KColumnID).toUInt();
×
589
    Fixture *tgtFxi = m_targetDoc->fixture(tgtFxiID);
×
590
    if (srcFxi == NULL || tgtFxi == NULL)
×
591
    {
592
        QMessageBox::warning(this,
×
593
                tr("Invalid selection"),
×
594
                tr("Please select a source and a target fixture or channel to perform this operation."));
×
595
        return;
×
596
    }
597

598
    bool srcFxiSelected = false;
×
599
    bool tgtFxiSelected = false;
×
600

601
    bool ok = false;
×
602
    int srcIdx = newRemap.source->text(KColumnChIdx).toInt(&ok);
×
603
    if (ok == false)
×
604
        srcFxiSelected = true;
×
605
    ok = false;
×
606
    int tgtIdx = newRemap.target->text(KColumnChIdx).toInt(&ok);
×
607
    if (ok == false)
×
608
        tgtFxiSelected = true;
×
609

610
    qDebug() << "Idx:" << srcIdx << ", src:" << srcFxiSelected << ", tgt:" << tgtFxiSelected;
×
611

612
    if ((srcFxiSelected == true && tgtFxiSelected == false) ||
×
NEW
613
        (srcFxiSelected == false && tgtFxiSelected == true))
×
614
    {
615
        QMessageBox::warning(this,
×
616
                             tr("Invalid selection"),
×
617
                             tr("To perform a fixture remap, please select fixtures on both lists."));
×
618
        return;
×
619
    }
620
    else if (srcFxiSelected == true && tgtFxiSelected == true)
×
621
    {
622
        // perform a full fixture remap
623
        const QLCFixtureDef *srcFxiDef = srcFxi->fixtureDef();
×
624
        const QLCFixtureDef *tgtFxiDef = tgtFxi->fixtureDef();
×
625
        const QLCFixtureMode *srcFxiMode = srcFxi->fixtureMode();
×
626
        const QLCFixtureMode *tgtFxiMode = tgtFxi->fixtureMode();
×
627
        bool oneToOneRemap = false;
×
628

629
        if (m_remapNamesCheck->isChecked())
×
630
        {
631
            tgtFxi->setName(srcFxi->name());
×
632
            newRemap.target->setText(KColumnName, srcFxi->name());
×
633
        }
634

635
        // 1-to-1 channel remapping is required for fixtures with
636
        // the same definition and mode
637
        if (srcFxiDef != NULL && tgtFxiDef != NULL &&
×
638
            srcFxiMode != NULL && tgtFxiMode != NULL)
×
639
        {
640
            if (srcFxiDef->name() == tgtFxiDef->name() &&
×
641
                srcFxiMode->name() == tgtFxiMode->name())
×
642
                    oneToOneRemap = true;
×
643
        }
644
        // 1-to-1 channel remapping is required for
645
        // generic dimmer packs
646
        else if (srcFxiDef == NULL && tgtFxiDef == NULL &&
×
647
                 srcFxiMode == NULL && tgtFxiMode == NULL)
×
648
                    oneToOneRemap = true;
×
649

650
        if (oneToOneRemap == true)
×
651
        {
652
            tgtFxi->setForcedHTPChannels(srcFxi->forcedHTPChannels());
×
653
            tgtFxi->setForcedLTPChannels(srcFxi->forcedLTPChannels());
×
654
        }
655

656
        for (quint32 s = 0; s < srcFxi->channels(); s++)
×
657
        {
658
            if (oneToOneRemap == true)
×
659
            {
660
                if (s < tgtFxi->channels())
×
661
                {
662
                    RemapInfo matchInfo;
663
                    matchInfo.source = newRemap.source->child(s);
×
664
                    matchInfo.target = newRemap.target->child(s);
×
665
                    m_remapList.append(matchInfo);
×
666

667
                    if (srcFxi->channelCanFade(s) == false)
×
668
                        tgtFxi->setChannelCanFade(s, false);
×
669
                }
670
            }
671
            else
672
            {
673
                const QLCChannel* srcCh = srcFxi->channel(s);
×
674

675
                for (quint32 t = 0; t < tgtFxi->channels(); t++)
×
676
                {
677
                    const QLCChannel* tgtCh = tgtFxi->channel(t);
×
678

679
                    if ((tgtCh->group() == srcCh->group()) &&
×
680
                        (tgtCh->controlByte() == srcCh->controlByte()))
×
681
                    {
682
                        if (tgtCh->group() == QLCChannel::Intensity &&
×
683
                            tgtCh->colour() != srcCh->colour())
×
684
                                continue;
×
685
                        RemapInfo matchInfo;
686
                        matchInfo.source = newRemap.source->child(s);
×
687
                        matchInfo.target = newRemap.target->child(t);
×
688
                        m_remapList.append(matchInfo);
×
689

690
                        if (srcFxi->channelCanFade(s) == false)
×
691
                            tgtFxi->setChannelCanFade(t, false);
×
692
                        break;
×
693
                    }
694
                }
695
            }
696
        }
×
697
    }
698
    else
699
    {
700
        // perform a single channel remap
701
        m_remapList.append(newRemap);
×
702
        if (srcFxi->channelCanFade(srcIdx) == false)
×
703
            tgtFxi->setChannelCanFade(tgtIdx, false);
×
704
    }
705
}
706

707
void FixtureRemap::slotRemoveRemap()
×
708
{
709
    if (m_sourceTree->selectedItems().count() == 0 ||
×
710
        m_targetTree->selectedItems().count() == 0)
×
711
    {
712
        QMessageBox::warning(this,
×
713
                             tr("Invalid selection"),
×
714
                             tr("Please select a source and a target fixture or channel to perform this operation."));
×
715
        return;
×
716
    }
717

718
    RemapInfo delRemap;
719
    delRemap.source = m_sourceTree->selectedItems().first();
×
720
    delRemap.target = m_targetTree->selectedItems().first();
×
721

722
    bool tgtFxiSelected = false;
×
723
    bool fxok = false, chok = false;
×
724
    quint32 fxid = delRemap.target->text(KColumnID).toUInt(&fxok);
×
725
    delRemap.target->text(KColumnChIdx).toInt(&chok);
×
726
    if (fxok == true && chok == false)
×
727
        tgtFxiSelected = true;
×
728

729
    for (int i = 0; i < m_remapList.count(); i++)
×
730
    {
731
        RemapInfo info = m_remapList.at(i);
×
732
        // full fixture remap delete
733
        if (tgtFxiSelected == true)
×
734
        {
735
            quint32 rmpFxID = info.target->text(KColumnID).toUInt();
×
736
            if (rmpFxID == fxid)
×
737
            {
738
                m_remapList.takeAt(i);
×
739
                i--;
×
740
            }
741
        }
742
        // single channel remap delete. Source and target must match
743
        else if (info.source == delRemap.source && info.target == delRemap.target)
×
744
        {
745
            m_remapList.takeAt(i);
×
746
            i--;
×
747
        }
748
    }
749
    remapWidget->setRemapList(m_remapList);
×
750
}
751

752
void FixtureRemap::slotUpdateConnections()
×
753
{
754
    remapWidget->update();
×
755
    m_sourceTree->resizeColumnToContents(KColumnName);
×
756
    m_targetTree->resizeColumnToContents(KColumnName);
×
757
}
×
758

759
void FixtureRemap::slotSourceSelectionChanged()
×
760
{
761
    if (m_sourceTree->selectedItems().count() > 0)
×
762
    {
763
        QTreeWidgetItem *item = m_sourceTree->selectedItems().first();
×
764
        bool fxOK = false, chOK = false;
×
765
        item->text(KColumnID).toUInt(&fxOK);
×
766
        item->text(KColumnChIdx).toInt(&chOK);
×
767
        if (fxOK == true && chOK == false)
×
768
            m_cloneButton->setEnabled(true);
×
769
        else
770
            m_cloneButton->setEnabled(false);
×
771
    }
772
    else
773
        m_cloneButton->setEnabled(false);
×
774
}
×
775

776
QList<SceneValue> FixtureRemap::remapSceneValues(QList<SceneValue> funcList,
×
777
                                    QList<SceneValue> &srcList,
778
                                    QList<SceneValue> &tgtList)
779
{
780
    QList <SceneValue> newValuesList;
×
NEW
781
    foreach (SceneValue val, funcList)
×
782
    {
783
        for (int v = 0; v < srcList.count(); v++)
×
784
        {
785
            if (val == srcList.at(v))
×
786
            {
787
                SceneValue tgtVal = tgtList.at(v);
×
788
                //qDebug() << "[Scene] Remapping" << val.fxi << val.channel << " to " << tgtVal.fxi << tgtVal.channel;
789
                newValuesList.append(SceneValue(tgtVal.fxi, tgtVal.channel, val.value));
×
790
            }
791
        }
792
    }
793
    std::sort(newValuesList.begin(), newValuesList.end());
×
794
    return newValuesList;
×
795
}
796

797
void FixtureRemap::accept()
×
798
{
799
    /* **********************************************************************
800
     * 1 - create a map of SceneValues from the fixtures channel associations
801
     * ********************************************************************** */
802
    QList<SceneValue> sourceList;
×
803
    QList<SceneValue> targetList;
×
804

805
    foreach (RemapInfo info, m_remapList)
×
806
    {
807
        quint32 srcFxiID = info.source->text(KColumnID).toUInt();
×
808
        quint32 srcChIdx = info.source->text(KColumnChIdx).toUInt();
×
809

810
        quint32 tgtFxiID = info.target->text(KColumnID).toUInt();
×
811
        quint32 tgtChIdx = info.target->text(KColumnChIdx).toUInt();
×
812

813
        sourceList.append(SceneValue(srcFxiID, srcChIdx));
×
814
        targetList.append(SceneValue(tgtFxiID, tgtChIdx));
×
815

816
        // qDebug() << "Remapping fx" << srcFxiID << "ch" << srcChIdx << "to fx" << tgtFxiID << "ch" << tgtChIdx;
817
    }
818

819
    /* **********************************************************************
820
     * 2 - Show a progress dialog, in case the operation takes a while
821
     * ********************************************************************** */
822
    QProgressDialog progress(tr("This might take a while..."), tr("Cancel"), 0, 100, this);
×
823
    progress.setWindowModality(Qt::WindowModal);
×
824
    progress.show();
×
825

826
    /* **********************************************************************
827
     * 3 - replace original project fixtures
828
     * ********************************************************************** */
829

830
    m_doc->replaceFixtures(m_targetDoc->fixtures());
×
831

832
    /* **********************************************************************
833
     * 4 - remap fixture groups and channel groups
834
     * ********************************************************************** */
NEW
835
    foreach (FixtureGroup *group, m_doc->fixtureGroups())
×
836
    {
837
        QMap<QLCPoint, GroupHead> grpHash = group->headsMap();
×
838
        group->reset();
×
839

840
        QMapIterator<QLCPoint, GroupHead> it(grpHash);
×
NEW
841
        while (it.hasNext())
×
842
        {
843
            it.next();
×
844

845
            QLCPoint pt(it.key());
×
846
            GroupHead head(it.value());
×
847

848
            if (head.isValid() == false)
×
849
                continue;
×
850

851
            for (int i = 0; i < sourceList.count(); i++)
×
852
            {
853
                if (sourceList.at(i).fxi == head.fxi)
×
854
                {
855
                    head.fxi = targetList.at(i).fxi;
×
856
                    group->resignHead(pt);
×
857
                    group->assignHead(pt, head);
×
858
                    break;
×
859
                }
860
            }
861
        }
862
    }
863

864
    foreach (ChannelsGroup *grp, m_doc->channelsGroups())
×
865
    {
866
        QList<SceneValue> grpChannels = grp->getChannels();
×
867
        // this is crucial: here all the "unmapped" channels will be lost forever !
868
        grp->resetChannels();
×
869
        QList <SceneValue> newList = remapSceneValues(grpChannels, sourceList, targetList);
×
870
        foreach (SceneValue val, newList)
×
871
            grp->addChannel(val.fxi, val.channel);
×
872
    }
873

874
    /* **********************************************************************
875
     * 5 - scan project functions and perform remapping
876
     * ********************************************************************** */
877
    int funcNum = m_doc->functions().count();
×
878
    int f = 0;
×
879
    foreach (Function *func, m_doc->functions())
×
880
    {
881
        switch (func->type())
×
882
        {
883
            case Function::SceneType:
×
884
            {
885
                Scene *s = qobject_cast<Scene*>(func);
×
886
                qDebug() << "Analyzing Scene #" << s->id();
×
887
                QList <SceneValue> newList = remapSceneValues(s->values(), sourceList, targetList);
×
888
                // this is crucial: here all the "unmapped" channels will be lost forever !
889
                s->clear();
×
890

891
                for (int i = 0; i < newList.count(); i++)
×
892
                {
893
                    s->addFixture(newList.at(i).fxi);
×
894
                    s->setValue(newList.at(i));
×
895
                }
896
            }
897
            break;
×
898
            case Function::SequenceType:
×
899
            {
900
                Sequence *s = qobject_cast<Sequence*>(func);
×
901
                for (int idx = 0; idx < s->stepsCount(); idx++)
×
902
                {
903
                    ChaserStep *cs = s->stepAt(idx);
×
904
                    QList <SceneValue> newList = remapSceneValues(cs->values, sourceList, targetList);
×
905
                    //qDebug() << "Step" << idx << "remapped" << cs.values.count() << "to" << newList.count();
906
                    // this is crucial: here all the "unmapped" channels will be lost forever !
907
                    cs->values.clear();
×
908
                    cs->values = newList;
×
909
                    //s->replaceStep(cs, idx);
910
                }
911
            }
912
            break;
×
913
            case Function::EFXType:
×
914
            {
915
                EFX *e = qobject_cast<EFX*>(func);
×
916
                // make a copy of this EFX fixtures list
917
                QList <EFXFixture*> fixListCopy;
×
NEW
918
                foreach (EFXFixture *efxFix, e->fixtures())
×
919
                {
920
                    EFXFixture* ef = new EFXFixture(e);
×
921
                    ef->copyFrom(efxFix);
×
922
                    fixListCopy.append(ef);
×
923
                }
924
                // this is crucial: here all the "unmapped" fixtures will be lost forever !
925
                e->removeAllFixtures();
×
926
                QList<quint32>remappedFixtures;
×
927

NEW
928
                foreach (EFXFixture *efxFix, fixListCopy)
×
929
                {
930
                    quint32 fxID = efxFix->head().fxi;
×
931
                    for (int i = 0; i < sourceList.count(); i++)
×
932
                    {
933
                        SceneValue srcVal = sourceList.at(i);
×
934
                        SceneValue tgtVal = targetList.at(i);
×
935
                        // check for fixture ID match. EFX remapping must be performed
936
                        // just once for each target fixture
937
                        if (srcVal.fxi == fxID && remappedFixtures.contains(tgtVal.fxi) == false)
×
938
                        {
939
                            Fixture *docFix = m_doc->fixture(tgtVal.fxi);
×
940
                            quint32 fxCh = tgtVal.channel;
×
941
                            const QLCChannel *chan = docFix->channel(fxCh);
×
942
                            if (chan->group() == QLCChannel::Pan ||
×
943
                                chan->group() == QLCChannel::Tilt)
×
944
                            {
945
                                EFXFixture* ef = new EFXFixture(e);
×
946
                                ef->copyFrom(efxFix);
×
947
                                ef->setHead(GroupHead(tgtVal.fxi, 0)); // TODO!!! head!!!
×
948
                                if (e->addFixture(ef) == false)
×
949
                                    delete ef;
×
950
                                qDebug() << "EFX remap" << srcVal.fxi << "to" << tgtVal.fxi;
×
951
                                remappedFixtures.append(tgtVal.fxi);
×
952
                            }
953
                        }
954
                    }
955
                }
956
                fixListCopy.clear();
×
957
            }
958
            break;
×
959
            default:
×
960
            break;
×
961
        }
NEW
962
        if (progress.wasCanceled())
×
963
            break;
×
964
        f++;
×
965
        progress.setValue((f * 100) / funcNum);
×
966
        QApplication::processEvents();
×
967
    }
968

969
    /* **********************************************************************
970
     * 6 - remap Virtual Console widgets
971
     * ********************************************************************** */
972
    VCFrame* contents = VirtualConsole::instance()->contents();
×
973
    QList<VCWidget *> widgetsList = contents->findChildren<VCWidget*>();
×
974

975
    foreach (VCWidget *widget, widgetsList)
×
976
    {
977
        switch (widget->type())
×
978
        {
979
            case VCWidget::SliderWidget:
×
980
            {
981
                VCSlider *slider = qobject_cast<VCSlider*>(widget);
×
982
                if (slider->sliderMode() == VCSlider::Level)
×
983
                {
984
                    qDebug() << "Remapping slider:" << slider->caption();
×
985
                    QList <SceneValue> newChannels;
×
986

987
                    foreach (VCSlider::LevelChannel chan, slider->levelChannels())
×
988
                    {
989
                        for (int v = 0; v < sourceList.count(); v++)
×
990
                        {
991
                            SceneValue val = sourceList.at(v);
×
992
                            if (val.fxi == chan.fixture && val.channel == chan.channel)
×
993
                            {
994
                                qDebug() << "Matching channel:" << chan.fixture << chan.channel << "to target:" << targetList.at(v).fxi << targetList.at(v).channel;
×
995
                                newChannels.append(SceneValue(targetList.at(v).fxi, targetList.at(v).channel));
×
996
                            }
997
                        }
998
                    }
999
                    // this is crucial: here all the "unmapped" channels will be lost forever !
1000
                    slider->clearLevelChannels();
×
1001
                    foreach (SceneValue rmpChan, newChannels)
×
1002
                        slider->addLevelChannel(rmpChan.fxi, rmpChan.channel);
×
1003
                }
1004
            }
1005
            break;
×
1006
            case VCWidget::AudioTriggersWidget:
×
1007
            {
1008
                VCAudioTriggers *triggers = qobject_cast<VCAudioTriggers*>(widget);
×
1009
                foreach (AudioBar *bar, triggers->getAudioBars())
×
1010
                {
1011
                    if (bar->m_type == AudioBar::DMXBar)
×
1012
                    {
1013
                        QList <SceneValue> newList = remapSceneValues(bar->m_dmxChannels, sourceList, targetList);
×
1014
                        // this is crucial: here all the "unmapped" channels will be lost forever !
1015
                        bar->attachDmxChannels(m_doc, newList);
×
1016
                    }
1017
                }
1018
            }
1019
            break;
×
1020
            case VCWidget::XYPadWidget:
×
1021
            {
1022
                VCXYPad *xypad = qobject_cast<VCXYPad*>(widget);
×
1023
                QList<VCXYPadFixture> copyFixtures;
×
1024
                foreach (VCXYPadFixture fix, xypad->fixtures())
×
1025
                {
1026
                    quint32 srxFxID = fix.head().fxi; // TODO: heads !!
×
1027
                    for (int i = 0; i < sourceList.count(); i++)
×
1028
                    {
1029
                        SceneValue val = sourceList.at(i);
×
1030
                        if (val.fxi == srxFxID)
×
1031
                        {
1032
                            SceneValue tgtVal = targetList.at(i);
×
1033
                            Fixture *docFix = m_doc->fixture(tgtVal.fxi);
×
1034
                            quint32 fxCh = tgtVal.channel;
×
1035
                            const QLCChannel *chan = docFix->channel(fxCh);
×
1036
                            if (chan->group() == QLCChannel::Pan ||
×
1037
                                chan->group() == QLCChannel::Tilt)
×
1038
                            {
1039
                                VCXYPadFixture tgtFix(m_doc);
×
1040
                                GroupHead head(tgtVal.fxi, 0);
×
1041
                                tgtFix.setHead(head);
×
1042
                                copyFixtures.append(tgtFix);
×
1043
                            }
1044
                        }
1045
                    }
1046
                }
1047
                // this is crucial: here all the "unmapped" fixtures will be lost forever !
1048
                xypad->clearFixtures();
×
1049
                foreach (VCXYPadFixture fix, copyFixtures)
×
1050
                    xypad->appendFixture(fix);
×
1051
            }
1052
            break;
×
1053
            default:
×
1054
            break;
×
1055
        }
1056
    }
1057

1058
    /* **********************************************************************
1059
     * 7 - remap 2D monitor properties, if defined
1060
     * ********************************************************************** */
1061
    MonitorProperties *props = m_doc->monitorProperties();
×
1062
    if (props != NULL)
×
1063
    {
1064
        QMap <quint32, FixturePreviewItem> remappedFixtureItems;
×
1065

1066
        foreach (quint32 fxID, props->fixtureItemsID())
×
1067
        {
NEW
1068
            for (int v = 0; v < sourceList.count(); v++)
×
1069
            {
1070
                if (sourceList.at(v).fxi == fxID)
×
1071
                {
1072
                    FixturePreviewItem rmpProp = props->fixtureProperties(fxID);
×
1073
                    remappedFixtureItems[targetList.at(v).fxi] = rmpProp;
×
1074
                    break;
×
1075
                }
1076
            }
1077

1078
            props->removeFixture(fxID);
×
1079
        }
1080

1081
        QMapIterator <quint32, FixturePreviewItem> it(remappedFixtureItems);
×
1082
        while (it.hasNext())
×
1083
        {
1084
            it.next();
×
1085
            props->setFixtureProperties(it.key(), it.value());
×
1086
        }
1087
    }
1088

1089
    /* **********************************************************************
1090
     * 8 - save the remapped project into a new file
1091
     * ********************************************************************** */
1092
    App *mainApp = (App *)m_doc->parent();
×
1093
    if (m_targetProjectLabel->text().endsWith(".qxw") == false)
×
1094
        m_targetProjectLabel->setText(m_targetProjectLabel->text() + ".qxw");
×
1095
    mainApp->setFileName(m_targetProjectLabel->text());
×
1096
    mainApp->slotFileSave();
×
1097

1098
    progress.hide();
×
1099

1100
    /* Close dialog */
1101
    QDialog::accept();
×
1102
}
×
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