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

mcallegari / qlcplus / 13633248611

03 Mar 2025 02:31PM UTC coverage: 31.871% (+0.4%) from 31.5%
13633248611

push

github

web-flow
actions: add chrpath to profile

14689 of 46089 relevant lines covered (31.87%)

26426.11 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
#include <QSettings>
28

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

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

60
#define SETTINGS_GEOMETRY "fixturemap/geometry"
61

62
FixtureRemap::FixtureRemap(Doc *doc, QWidget *parent)
×
63
    : QDialog(parent)
64
    , m_doc(doc)
×
65
{
66
    Q_ASSERT(doc != NULL);
67

68
    setupUi(this);
×
69

70
    QSettings settings;
×
71
    QVariant geometrySettings = settings.value(SETTINGS_GEOMETRY);
×
72
    if (geometrySettings.isValid() == true)
×
73
        restoreGeometry(geometrySettings.toByteArray());
×
74

75
    connect(m_importButton, SIGNAL(clicked()),
×
76
            this, SLOT(slotImportFixtures()));
77
    connect(m_addButton, SIGNAL(clicked()),
×
78
            this, SLOT(slotAddTargetFixture()));
79
    connect(m_removeButton, SIGNAL(clicked()),
×
80
            this, SLOT(slotRemoveTargetFixture()));
81
    connect(m_cloneButton, SIGNAL(clicked()),
×
82
            this, SLOT(slotCloneSourceFixture()));
83
    connect(m_remapButton, SIGNAL(clicked()),
×
84
            this, SLOT(slotAddRemap()));
85
    connect(m_unmapButton, SIGNAL(clicked()),
×
86
            this, SLOT(slotRemoveRemap()));
87

88
    m_cloneButton->setEnabled(false);
×
89

90
    remapWidget = new RemapWidget(m_sourceTree, m_targetTree, this);
×
91
    remapWidget->show();
×
92
    m_remapLayout->addWidget(remapWidget);
×
93

94
    m_targetDoc = new Doc(this);
×
95
    /* Load user fixtures first so that they override system fixtures */
96
    m_targetDoc->fixtureDefCache()->load(QLCFixtureDefCache::userDefinitionDirectory());
×
97
    m_targetDoc->fixtureDefCache()->loadMap(QLCFixtureDefCache::systemDefinitionDirectory());
×
98

99
    /* Remove the default set of universes from the target Doc and re-fill it
100
     * with the current Doc universe list */
101
    m_targetDoc->inputOutputMap()->removeAllUniverses();
×
102

103
    int index = 0;
104
    foreach (Universe *uni, m_doc->inputOutputMap()->universes())
×
105
    {
106
        m_targetDoc->inputOutputMap()->addUniverse(uni->id());
×
107
        m_targetDoc->inputOutputMap()->setUniverseName(index, uni->name());
×
108
        m_targetDoc->inputOutputMap()->startUniverses();
×
109
        index++;
×
110
    }
111

112
    m_sourceTree->setIconSize(QSize(24, 24));
×
113
    m_sourceTree->setAllColumnsShowFocus(true);
×
114
    fillFixturesTree(m_doc, m_sourceTree);
×
115

116
    m_targetTree->setIconSize(QSize(24, 24));
×
117
    m_targetTree->setAllColumnsShowFocus(true);
×
118

119
    connect(m_sourceTree->verticalScrollBar(), SIGNAL(valueChanged(int)),
×
120
            this, SLOT(slotUpdateConnections()));
121
    connect(m_sourceTree, SIGNAL(clicked(QModelIndex)),
×
122
            this, SLOT(slotUpdateConnections()));
123
    connect(m_sourceTree, SIGNAL(expanded(QModelIndex)),
×
124
            this, SLOT(slotUpdateConnections()));
125
    connect(m_sourceTree, SIGNAL(collapsed(QModelIndex)),
×
126
            this, SLOT(slotUpdateConnections()));
127
    connect(m_sourceTree, SIGNAL(itemSelectionChanged()),
×
128
            this, SLOT(slotSourceSelectionChanged()));
129

130
    connect(m_targetTree->verticalScrollBar(), SIGNAL(valueChanged(int)),
×
131
            this, SLOT(slotUpdateConnections()));
132
    connect(m_targetTree, SIGNAL(clicked(QModelIndex)),
×
133
            this, SLOT(slotUpdateConnections()));
134
    connect(m_targetTree, SIGNAL(expanded(QModelIndex)),
×
135
            this, SLOT(slotUpdateConnections()));
136
    connect(m_targetTree, SIGNAL(collapsed(QModelIndex)),
×
137
            this, SLOT(slotUpdateConnections()));
138

139
    // retrieve the original project name from the QLC+ App class
140
    App *mainApp = (App *)m_doc->parent();
×
141
    QString prjName = mainApp->fileName();
×
142

143
    if (prjName.lastIndexOf(".") > 0)
×
144
        prjName.insert(prjName.lastIndexOf("."), tr(" (remapped)"));
×
145
    else
146
        prjName.append(tr(" (remapped)"));
×
147

148
    m_targetProjectLabel->setText(prjName);
×
149
}
×
150

151
FixtureRemap::~FixtureRemap()
×
152
{
153
    QSettings settings;
×
154
    settings.setValue(SETTINGS_GEOMETRY, saveGeometry());
×
155

156
    delete m_targetDoc;
×
157
}
×
158

159
QTreeWidgetItem *FixtureRemap::getUniverseItem(Doc *doc, quint32 universe, QTreeWidget *tree)
×
160
{
161
    QTreeWidgetItem *topItem = NULL;
162

163
    for (int i = 0; i < tree->topLevelItemCount(); i++)
×
164
    {
165
        QTreeWidgetItem* tItem = tree->topLevelItem(i);
×
166
        quint32 tUni = tItem->text(KColumnUniverse).toUInt();
×
167
        if (tUni == universe)
×
168
        {
169
            topItem = tItem;
170
            break;
171
        }
172
    }
173

174
    // Haven't found this universe node ? Create it.
175
    if (topItem == NULL)
×
176
    {
177
        topItem = new QTreeWidgetItem(tree);
×
178
        topItem->setText(KColumnName, doc->inputOutputMap()->universes().at(universe)->name());
×
179
        topItem->setText(KColumnUniverse, QString::number(universe));
×
180
        topItem->setText(KColumnID, QString::number(Function::invalidId()));
×
181
        topItem->setExpanded(true);
×
182
    }
183

184
    return topItem;
×
185
}
186

187
void FixtureRemap::fillFixturesTree(Doc *doc, QTreeWidget *tree)
×
188
{
189
    foreach (Fixture *fxi, doc->fixtures())
×
190
    {
191
        quint32 uni = fxi->universe();
×
192
        QTreeWidgetItem *topItem = getUniverseItem(doc, uni, tree);
×
193

194
        quint32 baseAddr = fxi->address();
×
195
        QTreeWidgetItem *fItem = new QTreeWidgetItem(topItem);
×
196
        fItem->setText(KColumnName, fxi->name());
×
197
        fItem->setIcon(KColumnName, fxi->getIconFromType());
×
198
        fItem->setText(KColumnAddress, QString("%1 - %2").arg(baseAddr + 1).arg(baseAddr + fxi->channels()));
×
199
        fItem->setText(KColumnUniverse, QString::number(uni));
×
200
        fItem->setText(KColumnID, QString::number(fxi->id()));
×
201

202
        for (quint32 c = 0; c < fxi->channels(); c++)
×
203
        {
204
            const QLCChannel* channel = fxi->channel(c);
×
205
            QTreeWidgetItem *item = new QTreeWidgetItem(fItem);
×
206
            item->setText(KColumnName, QString("%1:%2").arg(c + 1)
×
207
                          .arg(channel->name()));
×
208
            item->setIcon(KColumnName, channel->getIcon());
×
209
            item->setText(KColumnUniverse, QString::number(uni));
×
210
            item->setText(KColumnID, QString::number(fxi->id()));
×
211
            item->setText(KColumnChIdx, QString::number(c));
×
212
        }
213
    }
214

215
    tree->resizeColumnToContents(KColumnName);
×
216
}
×
217

218
QString FixtureRemap::createImportDialog()
×
219
{
220
    QString fileName;
221

222
    /* Create a file save dialog */
223
    QFileDialog dialog(this);
×
224
    dialog.setWindowTitle(tr("Import Fixtures List"));
×
225
    dialog.setAcceptMode(QFileDialog::AcceptOpen);
×
226

227
    /* Append file filters to the dialog */
228
    QStringList filters;
229
    filters << tr("Fixtures List (*%1)").arg(KExtFixtureList);
×
230
#if defined(WIN32) || defined(Q_OS_WIN)
231
    filters << tr("All Files (*.*)");
232
#else
233
    filters << tr("All Files (*)");
×
234
#endif
235
    dialog.setNameFilters(filters);
×
236

237
    /* Append useful URLs to the dialog */
238
    QList <QUrl> sidebar;
239
    sidebar.append(QUrl::fromLocalFile(QDir::homePath()));
×
240
    sidebar.append(QUrl::fromLocalFile(QDir::rootPath()));
×
241
    dialog.setSidebarUrls(sidebar);
×
242

243
    /* Get file name */
244
    if (dialog.exec() != QDialog::Accepted)
×
245
        return "";
×
246

247
    fileName = dialog.selectedFiles().first();
×
248
    if (fileName.isEmpty() == true)
×
249
        return "";
×
250

251
    return fileName;
252
}
×
253

254
void FixtureRemap::slotImportFixtures()
×
255
{
256
    QString fileName = createImportDialog();
×
257

258
    QMessageBox msgBox;
×
259
    msgBox.setText(tr("Do you want to automatically connect fixtures with the same name?"));
×
260
    msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
×
261
    msgBox.setDefaultButton(QMessageBox::Yes);
×
262
    bool autoConnect = msgBox.exec() == QMessageBox::Yes ? true : false;
×
263

264
    QXmlStreamReader *doc = QLCFile::getXMLReader(fileName);
×
265
    if (doc == NULL || doc->device() == NULL || doc->hasError())
×
266
    {
267
        qWarning() << Q_FUNC_INFO << "Unable to read from" << fileName;
×
268
        return;
×
269
    }
270

271
    while (!doc->atEnd())
×
272
    {
273
        if (doc->readNext() == QXmlStreamReader::DTD)
×
274
            break;
275
    }
276
    if (doc->hasError())
×
277
    {
278
        QLCFile::releaseXMLReader(doc);
×
279
        return;
280
    }
281

282
    if (doc->dtdName() == KXMLQLCFixturesList)
×
283
    {
284
        doc->readNextStartElement();
×
285
        if (doc->name() != KXMLQLCFixturesList)
×
286
        {
287
            qWarning() << Q_FUNC_INFO << "Fixture Definition node not found";
×
288
            QLCFile::releaseXMLReader(doc);
×
289
            return;
290
        }
291

292
        while (doc->readNextStartElement())
×
293
        {
294
            if (doc->name() == KXMLFixture)
×
295
            {
296
                Fixture* fxi = new Fixture(m_targetDoc);
×
297
                Q_ASSERT(fxi != NULL);
298

299
                if (fxi->loadXML(*doc, m_targetDoc, m_doc->fixtureDefCache()) == true)
×
300
                {
301
                    if (m_targetDoc->addFixture(fxi) == false)
×
302
                    {
303
                        qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name() << "cannot be created.";
×
304
                        delete fxi;
×
305
                    }
306
                }
307
                else
308
                {
309
                    qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name() << "cannot be loaded.";
×
310
                    delete fxi;
×
311
                }
312
            }
313
            else if (doc->name() == KXMLQLCFixtureGroup)
×
314
            {
315
                FixtureGroup* grp = new FixtureGroup(m_targetDoc);
×
316
                Q_ASSERT(grp != NULL);
317

318
                if (grp->loadXML(*doc) == true)
×
319
                {
320
                    m_targetDoc->addFixtureGroup(grp, grp->id());
×
321
                }
322
                else
323
                {
324
                    qWarning() << Q_FUNC_INFO << "FixtureGroup" << grp->name() << "cannot be loaded.";
×
325
                    delete grp;
×
326
                }
327
            }
328
            else
329
            {
330
                qWarning() << Q_FUNC_INFO << "Unknown label tag:" << doc->name().toString();
×
331
                doc->skipCurrentElement();
×
332
            }
333
        }
334
        fillFixturesTree(m_targetDoc, m_targetTree);
×
335

336
        if (autoConnect)
×
337
        {
338
            for (int tu = 0; tu < m_targetTree->topLevelItemCount(); tu++)
×
339
            {
340
                QTreeWidgetItem *tgtUniItem = m_targetTree->topLevelItem(tu);
×
341

342
                for (int ti = 0; ti < tgtUniItem->childCount(); ti++)
×
343
                {
344
                    QTreeWidgetItem *tgtItem = tgtUniItem->child(ti);
×
345

346
                    for (int su = 0; su < m_sourceTree->topLevelItemCount(); su++)
×
347
                    {
348
                        QTreeWidgetItem *srcUniItem = m_sourceTree->topLevelItem(su);
×
349

350
                        for (int si = 0; si < srcUniItem->childCount(); si++)
×
351
                        {
352
                            QTreeWidgetItem *srcItem = srcUniItem->child(si);
×
353

354
                            if (srcItem->text(KColumnName) == tgtItem->text(KColumnName))
×
355
                            {
356
                                connectFixtures(srcItem, tgtItem);
×
357
                                break;
358
                            }
359
                        }
360
                    }
361
                }
362
            }
363
            remapWidget->setRemapList(m_remapList);
×
364
        }
365
    }
366
    QLCFile::releaseXMLReader(doc);
×
367
}
×
368

369
void FixtureRemap::slotAddTargetFixture()
×
370
{
371
    AddFixture af(this, m_targetDoc);
×
372
    if (af.exec() == QDialog::Rejected)
×
373
        return;
374

375
    QString name = af.name();
×
376
    quint32 address = af.address();
×
377
    quint32 universe = af.universe();
×
378
    quint32 channels = af.channels();
×
379
    QLCFixtureDef* fixtureDef = af.fixtureDef();
×
380
    QLCFixtureMode* mode = af.mode();
×
381
    int gap = af.gap();
×
382

383
    for (int i = 0; i < af.amount(); i++)
×
384
    {
385
        QString modname;
386

387
        /* If an empty name was given use the model instead */
388
        if (name.simplified().isEmpty())
×
389
        {
390
            if (fixtureDef != NULL)
×
391
                name = fixtureDef->model();
×
392
            else
393
                name = tr("Generic Dimmer");
×
394
        }
395

396
        /* If we're adding more than one fixture,
397
           append a number to the end of the name */
398
        if (af.amount() > 1)
×
399
            modname = QString("%1 #%2").arg(name).arg(i+1);
×
400
        else
401
            modname = name;
×
402

403
        /* Create the target fixture */
404
        Fixture* fxi = new Fixture(m_targetDoc);
×
405

406
        /* Add the first fixture without gap, at the given address */
407
        fxi->setAddress(address + (i * channels) + (i * gap));
×
408
        fxi->setUniverse(universe);
×
409
        fxi->setName(modname);
×
410

411
        /* Set a fixture definition & mode if they were selected.
412
           Otherwise assign channels to a generic dimmer. */
413
        if (fixtureDef != NULL && mode != NULL)
×
414
            fxi->setFixtureDefinition(fixtureDef, mode);
×
415
        else
416
        {
417
            fixtureDef = fxi->genericDimmerDef(channels);
×
418
            mode = fxi->genericDimmerMode(fixtureDef, channels);
×
419
            fxi->setFixtureDefinition(fixtureDef, mode);
×
420
            //fxi->setChannels(channels);
421
        }
422

423
        m_targetDoc->addFixture(fxi);
×
424

425
        QTreeWidgetItem *topItem = getUniverseItem(m_targetDoc, universe, m_targetTree);
×
426

427
        quint32 baseAddr = fxi->address();
×
428
        QTreeWidgetItem *fItem = new QTreeWidgetItem(topItem);
×
429
        fItem->setText(KColumnName, fxi->name());
×
430
        fItem->setIcon(KColumnName, fxi->getIconFromType());
×
431
        fItem->setText(KColumnAddress, QString("%1 - %2").arg(baseAddr + 1).arg(baseAddr + fxi->channels()));
×
432
        fItem->setText(KColumnUniverse, QString::number(universe));
×
433
        fItem->setText(KColumnID, QString::number(fxi->id()));
×
434

435
        for (quint32 c = 0; c < fxi->channels(); c++)
×
436
        {
437
            const QLCChannel* channel = fxi->channel(c);
×
438
            QTreeWidgetItem *item = new QTreeWidgetItem(fItem);
×
439
            item->setText(KColumnName, QString("%1:%2").arg(c + 1)
×
440
                          .arg(channel->name()));
×
441
            item->setIcon(KColumnName, channel->getIcon());
×
442
            item->setText(KColumnUniverse, QString::number(universe));
×
443
            item->setText(KColumnID, QString::number(fxi->id()));
×
444
            item->setText(KColumnChIdx, QString::number(c));
×
445
        }
446
    }
×
447
    m_targetTree->resizeColumnToContents(KColumnName);
×
448

449
    qDebug() << "Fixtures in target doc:" << m_targetDoc->fixtures().count();
450
}
×
451

452
void FixtureRemap::slotRemoveTargetFixture()
×
453
{
454
    if (m_targetTree->selectedItems().count() == 0)
×
455
        return;
×
456

457
    QTreeWidgetItem *item = m_targetTree->selectedItems().first();
×
458
    bool ok = false;
×
459
    quint32 fxid = item->text(KColumnID).toUInt(&ok);
×
460
    if (ok == false)
×
461
        return;
462

463
    // Ask before deletion
464
    if (QMessageBox::question(this, tr("Delete Fixtures"),
×
465
                              tr("Do you want to delete the selected items?"),
×
466
                              QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
467
    {
468
        return;
469
    }
470

471
    int i = 0;
472
    QListIterator <RemapInfo> it(m_remapList);
×
473
    while (it.hasNext() == true)
×
474
    {
475
        RemapInfo info = it.next();
×
476
        quint32 tgtID = info.target->text(KColumnID).toUInt();
×
477
        if (tgtID == fxid)
×
478
            m_remapList.takeAt(i);
×
479
        else
480
            i++;
×
481
    }
482
    remapWidget->setRemapList(m_remapList);
×
483
    m_targetDoc->deleteFixture(fxid);
×
484
    for (int i = 0; i < item->childCount(); i++)
×
485
    {
486
        QTreeWidgetItem *child = item->child(i);
×
487
        delete child;
×
488
    }
489
    delete item;
×
490
    m_targetTree->resizeColumnToContents(KColumnName);
×
491

492
    qDebug() << "Fixtures in target doc:" << m_targetDoc->fixtures().count();
493
}
494

495
void FixtureRemap::slotCloneSourceFixture()
×
496
{
497
    if (m_sourceTree->selectedItems().count() == 0)
×
498
        return; // popup here ??
499

500
    QTreeWidgetItem *sItem = m_sourceTree->selectedItems().first();
×
501
    quint32 fxID = sItem->text(KColumnID).toUInt();
×
502
    Fixture *srcFix = m_doc->fixture(fxID);
×
503
    if (srcFix == NULL)
×
504
        return; // popup here ?
505

506
    quint32 srcAddr = srcFix->universeAddress();
×
507
    for (quint32 i = srcAddr; i < srcAddr + srcFix->channels(); i++)
×
508
    {
509
        quint32 fxCheck = m_targetDoc->fixtureForAddress(i);
×
510
        if (fxCheck != Fixture::invalidId())
×
511
        {
512
            QMessageBox::warning(this,
×
513
                                 tr("Invalid operation"),
×
514
                                 tr("You are trying to clone a fixture on an address already in use. "
×
515
                                    "Please fix the target list first."));
516
            return;
×
517
        }
518
    }
519

520
    // create a copy of the fixture and add it to the target document
521
    /* Create the target fixture */
522
    Fixture* tgtFix = new Fixture(m_targetDoc);
×
523

524
    /* Add the first fixture without gap, at the given address */
525
    tgtFix->setAddress(srcFix->address());
×
526
    tgtFix->setUniverse(srcFix->universe());
×
527
    tgtFix->setName(srcFix->name());
×
528

529
    /* Set a fixture definition & mode if they were selected.
530
       Otherwise assign channels to a generic dimmer. */
531
    if (srcFix->fixtureDef()->manufacturer() == KXMLFixtureGeneric &&
×
532
        srcFix->fixtureDef()->model() == KXMLFixtureGeneric)
×
533
            tgtFix->setChannels(srcFix->channels());
×
534
    else
535
        tgtFix->setFixtureDefinition(srcFix->fixtureDef(), srcFix->fixtureMode());
×
536

537
    m_targetDoc->addFixture(tgtFix);
×
538

539
    // create the tree element and add it to the target tree
540
    QTreeWidgetItem *topItem = getUniverseItem(m_targetDoc, tgtFix->universe(), m_targetTree);
×
541
    quint32 baseAddr = tgtFix->address();
×
542
    QTreeWidgetItem *fItem = new QTreeWidgetItem(topItem);
×
543
    fItem->setText(KColumnName, tgtFix->name());
×
544
    fItem->setIcon(KColumnName, tgtFix->getIconFromType());
×
545
    fItem->setText(KColumnAddress, QString("%1 - %2").arg(baseAddr + 1).arg(baseAddr + tgtFix->channels()));
×
546
    fItem->setText(KColumnUniverse, QString::number(tgtFix->universe()));
×
547
    fItem->setText(KColumnID, QString::number(tgtFix->id()));
×
548

549
    for (quint32 c = 0; c < tgtFix->channels(); c++)
×
550
    {
551
        const QLCChannel* channel = tgtFix->channel(c);
×
552
        QTreeWidgetItem *item = new QTreeWidgetItem(fItem);
×
553
        item->setText(KColumnName, QString("%1:%2").arg(c + 1)
×
554
                      .arg(channel->name()));
×
555
        item->setIcon(KColumnName, channel->getIcon());
×
556
        item->setText(KColumnUniverse, QString::number(tgtFix->universe()));
×
557
        item->setText(KColumnID, QString::number(tgtFix->id()));
×
558
        item->setText(KColumnChIdx, QString::number(c));
×
559
    }
560

561
    m_targetTree->resizeColumnToContents(KColumnName);
×
562

563
    foreach (QTreeWidgetItem *it, m_targetTree->selectedItems())
×
564
        it->setSelected(false);
×
565
    fItem->setSelected(true);
×
566

567
    slotAddRemap();
×
568
}
569

570
void FixtureRemap::slotAddRemap()
×
571
{
572
    if (m_sourceTree->selectedItems().count() == 0 ||
×
573
        m_targetTree->selectedItems().count() == 0)
×
574
    {
575
        QMessageBox::warning(this,
×
576
                tr("Invalid selection"),
×
577
                tr("Please select a source and a target fixture or channel to perform this operation."));
×
578
        return;
×
579
    }
580

581
    connectFixtures(m_sourceTree->selectedItems().first(),
×
582
                    m_targetTree->selectedItems().first());
×
583

584
    remapWidget->setRemapList(m_remapList);
×
585
}
586

587
void FixtureRemap::connectFixtures(QTreeWidgetItem *sourceItem, QTreeWidgetItem *targetItem)
×
588
{
589
    if (sourceItem == NULL || targetItem == NULL)
×
590
        return;
×
591

592
    RemapInfo newRemap;
593
    newRemap.source = sourceItem;
×
594
    newRemap.target = targetItem;
×
595

596
    quint32 srcFxiID = newRemap.source->text(KColumnID).toUInt();
×
597
    Fixture *srcFxi = m_doc->fixture(srcFxiID);
×
598
    quint32 tgtFxiID = newRemap.target->text(KColumnID).toUInt();
×
599
    Fixture *tgtFxi = m_targetDoc->fixture(tgtFxiID);
×
600
    if (srcFxi == NULL || tgtFxi == NULL)
×
601
    {
602
        QMessageBox::warning(this,
×
603
                tr("Invalid selection"),
×
604
                tr("Please select a source and a target fixture or channel to perform this operation."));
×
605
        return;
×
606
    }
607

608
    bool srcFxiSelected = false;
609
    bool tgtFxiSelected = false;
610

611
    bool ok = false;
×
612
    int srcIdx = newRemap.source->text(KColumnChIdx).toInt(&ok);
×
613
    if (ok == false)
×
614
        srcFxiSelected = true;
615
    ok = false;
×
616
    int tgtIdx = newRemap.target->text(KColumnChIdx).toInt(&ok);
×
617
    if (ok == false)
×
618
        tgtFxiSelected = true;
619

620
    qDebug() << "Idx:" << srcIdx << ", src:" << srcFxiSelected << ", tgt:" << tgtFxiSelected;
621

622
    if ((srcFxiSelected == true && tgtFxiSelected == false) ||
×
623
        (srcFxiSelected == false && tgtFxiSelected == true))
×
624
    {
625
        QMessageBox::warning(this,
×
626
                             tr("Invalid selection"),
×
627
                             tr("To perform a fixture remap, please select fixtures on both lists."));
×
628
        return;
×
629
    }
630
    else if (srcFxiSelected == true && tgtFxiSelected == true)
×
631
    {
632
        // perform a full fixture remap
633
        const QLCFixtureDef *srcFxiDef = srcFxi->fixtureDef();
×
634
        const QLCFixtureDef *tgtFxiDef = tgtFxi->fixtureDef();
×
635
        const QLCFixtureMode *srcFxiMode = srcFxi->fixtureMode();
×
636
        const QLCFixtureMode *tgtFxiMode = tgtFxi->fixtureMode();
×
637
        bool oneToOneRemap = false;
638

639
        if (m_remapNamesCheck->isChecked())
×
640
        {
641
            tgtFxi->setName(srcFxi->name());
×
642
            newRemap.target->setText(KColumnName, srcFxi->name());
×
643
        }
644

645
        // 1-to-1 channel remapping is required for fixtures with
646
        // the same definition and mode
647
        if (srcFxiDef != NULL && tgtFxiDef != NULL &&
×
648
            srcFxiMode != NULL && tgtFxiMode != NULL)
×
649
        {
650
            if (srcFxiDef->name() == tgtFxiDef->name() &&
×
651
                srcFxiMode->name() == tgtFxiMode->name())
×
652
                    oneToOneRemap = true;
653
        }
654
        // 1-to-1 channel remapping is required for
655
        // generic dimmer packs
656
        else if (srcFxiDef == NULL && tgtFxiDef == NULL &&
×
657
                 srcFxiMode == NULL && tgtFxiMode == NULL)
×
658
                    oneToOneRemap = true;
659

660
        if (oneToOneRemap == true)
661
        {
662
            tgtFxi->setForcedHTPChannels(srcFxi->forcedHTPChannels());
×
663
            tgtFxi->setForcedLTPChannels(srcFxi->forcedLTPChannels());
×
664
        }
665

666
        for (quint32 s = 0; s < srcFxi->channels(); s++)
×
667
        {
668
            if (oneToOneRemap == true)
×
669
            {
670
                if (s < tgtFxi->channels())
×
671
                {
672
                    RemapInfo matchInfo;
673
                    matchInfo.source = newRemap.source->child(s);
×
674
                    matchInfo.target = newRemap.target->child(s);
×
675
                    m_remapList.append(matchInfo);
×
676

677
                    if (srcFxi->channelCanFade(s) == false)
×
678
                        tgtFxi->setChannelCanFade(s, false);
×
679
                }
680
            }
681
            else
682
            {
683
                const QLCChannel* srcCh = srcFxi->channel(s);
×
684

685
                for (quint32 t = 0; t < tgtFxi->channels(); t++)
×
686
                {
687
                    const QLCChannel* tgtCh = tgtFxi->channel(t);
×
688

689
                    if ((tgtCh->group() == srcCh->group()) &&
×
690
                        (tgtCh->controlByte() == srcCh->controlByte()))
×
691
                    {
692
                        if (tgtCh->group() == QLCChannel::Intensity &&
×
693
                            tgtCh->colour() != srcCh->colour())
×
694
                                continue;
×
695
                        RemapInfo matchInfo;
696
                        matchInfo.source = newRemap.source->child(s);
×
697
                        matchInfo.target = newRemap.target->child(t);
×
698
                        m_remapList.append(matchInfo);
×
699

700
                        if (srcFxi->channelCanFade(s) == false)
×
701
                            tgtFxi->setChannelCanFade(t, false);
×
702
                        break;
×
703
                    }
704
                }
705
            }
706
        }
707
    }
708
    else
709
    {
710
        // perform a single channel remap
711
        m_remapList.append(newRemap);
×
712
        if (srcFxi->channelCanFade(srcIdx) == false)
×
713
            tgtFxi->setChannelCanFade(tgtIdx, false);
×
714
    }
715
}
716

717
void FixtureRemap::slotRemoveRemap()
×
718
{
719
    if (m_sourceTree->selectedItems().count() == 0 ||
×
720
        m_targetTree->selectedItems().count() == 0)
×
721
    {
722
        QMessageBox::warning(this,
×
723
                             tr("Invalid selection"),
×
724
                             tr("Please select a source and a target fixture or channel to perform this operation."));
×
725
        return;
×
726
    }
727

728
    RemapInfo delRemap;
729
    delRemap.source = m_sourceTree->selectedItems().first();
×
730
    delRemap.target = m_targetTree->selectedItems().first();
×
731

732
    bool tgtFxiSelected = false;
733
    bool fxok = false, chok = false;
×
734
    quint32 fxid = delRemap.target->text(KColumnID).toUInt(&fxok);
×
735
    delRemap.target->text(KColumnChIdx).toInt(&chok);
×
736
    if (fxok == true && chok == false)
×
737
        tgtFxiSelected = true;
738

739
    for (int i = 0; i < m_remapList.count(); i++)
×
740
    {
741
        RemapInfo info = m_remapList.at(i);
×
742
        // full fixture remap delete
743
        if (tgtFxiSelected == true)
×
744
        {
745
            quint32 rmpFxID = info.target->text(KColumnID).toUInt();
×
746
            if (rmpFxID == fxid)
×
747
            {
748
                m_remapList.takeAt(i);
×
749
                i--;
×
750
            }
751
        }
752
        // single channel remap delete. Source and target must match
753
        else if (info.source == delRemap.source && info.target == delRemap.target)
×
754
        {
755
            m_remapList.takeAt(i);
×
756
            i--;
×
757
        }
758
    }
759
    remapWidget->setRemapList(m_remapList);
×
760
}
761

762
void FixtureRemap::slotUpdateConnections()
×
763
{
764
    remapWidget->update();
×
765
    m_sourceTree->resizeColumnToContents(KColumnName);
×
766
    m_targetTree->resizeColumnToContents(KColumnName);
×
767
}
×
768

769
void FixtureRemap::slotSourceSelectionChanged()
×
770
{
771
    if (m_sourceTree->selectedItems().count() > 0)
×
772
    {
773
        QTreeWidgetItem *item = m_sourceTree->selectedItems().first();
×
774
        bool fxOK = false, chOK = false;
×
775
        item->text(KColumnID).toUInt(&fxOK);
×
776
        item->text(KColumnChIdx).toInt(&chOK);
×
777
        if (fxOK == true && chOK == false)
×
778
            m_cloneButton->setEnabled(true);
×
779
        else
780
            m_cloneButton->setEnabled(false);
×
781
    }
782
    else
783
        m_cloneButton->setEnabled(false);
×
784
}
×
785

786
QList<SceneValue> FixtureRemap::remapSceneValues(QList<SceneValue> funcList,
×
787
                                    QList<SceneValue> &srcList,
788
                                    QList<SceneValue> &tgtList)
789
{
790
    QList <SceneValue> newValuesList;
791
    foreach (SceneValue val, funcList)
×
792
    {
793
        for (int v = 0; v < srcList.count(); v++)
×
794
        {
795
            if (val == srcList.at(v))
×
796
            {
797
                SceneValue tgtVal = tgtList.at(v);
×
798
                //qDebug() << "[Scene] Remapping" << val.fxi << val.channel << " to " << tgtVal.fxi << tgtVal.channel;
799
                newValuesList.append(SceneValue(tgtVal.fxi, tgtVal.channel, val.value));
×
800
            }
×
801
        }
802
    }
×
803
    std::sort(newValuesList.begin(), newValuesList.end());
×
804
    return newValuesList;
×
805
}
×
806

807
void FixtureRemap::accept()
×
808
{
809
    /* **********************************************************************
810
     * 1 - create a map of SceneValues from the fixtures channel associations
811
     * ********************************************************************** */
812
    QList<SceneValue> sourceList;
813
    QList<SceneValue> targetList;
814

815
    foreach (RemapInfo info, m_remapList)
×
816
    {
817
        quint32 srcFxiID = info.source->text(KColumnID).toUInt();
×
818
        quint32 srcChIdx = info.source->text(KColumnChIdx).toUInt();
×
819

820
        quint32 tgtFxiID = info.target->text(KColumnID).toUInt();
×
821
        quint32 tgtChIdx = info.target->text(KColumnChIdx).toUInt();
×
822

823
        sourceList.append(SceneValue(srcFxiID, srcChIdx));
×
824
        targetList.append(SceneValue(tgtFxiID, tgtChIdx));
×
825

826
        // qDebug() << "Remapping fx" << srcFxiID << "ch" << srcChIdx << "to fx" << tgtFxiID << "ch" << tgtChIdx;
827
    }
828

829
    /* **********************************************************************
830
     * 2 - Show a progress dialog, in case the operation takes a while
831
     * ********************************************************************** */
832
    QProgressDialog progress(tr("This might take a while..."), tr("Cancel"), 0, 100, this);
×
833
    progress.setWindowModality(Qt::WindowModal);
×
834
    progress.show();
×
835

836
    /* **********************************************************************
837
     * 3 - replace original project fixtures
838
     * ********************************************************************** */
839

840
    m_doc->replaceFixtures(m_targetDoc->fixtures());
×
841

842
    /* **********************************************************************
843
     * 4 - remap fixture groups and channel groups
844
     * ********************************************************************** */
845
    foreach (FixtureGroup *group, m_doc->fixtureGroups())
×
846
    {
847
        QMap<QLCPoint, GroupHead> grpHash = group->headsMap();
×
848
        group->reset();
×
849

850
        QMapIterator<QLCPoint, GroupHead> it(grpHash);
×
851
        while (it.hasNext())
×
852
        {
853
            it.next();
×
854

855
            QLCPoint pt(it.key());
×
856
            GroupHead head(it.value());
×
857

858
            if (head.isValid() == false)
×
859
                continue;
860

861
            for (int i = 0; i < sourceList.count(); i++)
×
862
            {
863
                if (sourceList.at(i).fxi == head.fxi)
×
864
                {
865
                    head.fxi = targetList.at(i).fxi;
×
866
                    group->resignHead(pt);
×
867
                    group->assignHead(pt, head);
×
868
                    break;
869
                }
870
            }
871
        }
×
872
    }
×
873

874
    foreach (ChannelsGroup *grp, m_doc->channelsGroups())
×
875
    {
876
        QList<SceneValue> grpChannels = grp->getChannels();
×
877
        // this is crucial: here all the "unmapped" channels will be lost forever !
878
        grp->resetChannels();
×
879
        QList <SceneValue> newList = remapSceneValues(grpChannels, sourceList, targetList);
×
880
        foreach (SceneValue val, newList)
×
881
            grp->addChannel(val.fxi, val.channel);
×
882
    }
×
883

884
    /* **********************************************************************
885
     * 5 - scan project functions and perform remapping
886
     * ********************************************************************** */
887
    int funcNum = m_doc->functions().count();
×
888
    int f = 0;
889
    foreach (Function *func, m_doc->functions())
×
890
    {
891
        switch (func->type())
×
892
        {
893
            case Function::SceneType:
894
            {
895
                Scene *s = qobject_cast<Scene*>(func);
896
                qDebug() << "Analyzing Scene #" << s->id();
897
                QList <SceneValue> newList = remapSceneValues(s->values(), sourceList, targetList);
×
898
                // this is crucial: here all the "unmapped" channels will be lost forever !
899
                s->clear();
×
900

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

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

979
    /* **********************************************************************
980
     * 6 - remap Virtual Console widgets
981
     * ********************************************************************** */
982
    VCFrame* contents = VirtualConsole::instance()->contents();
×
983
    QList<VCWidget *> widgetsList = contents->findChildren<VCWidget*>();
×
984

985
    foreach (VCWidget *widget, widgetsList)
×
986
    {
987
        switch (widget->type())
×
988
        {
989
            case VCWidget::SliderWidget:
990
            {
991
                VCSlider *slider = qobject_cast<VCSlider*>(widget);
992
                if (slider->sliderMode() == VCSlider::Level)
×
993
                {
994
                    qDebug() << "Remapping slider:" << slider->caption();
995
                    QList <SceneValue> newChannels;
996

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

1068
    /* **********************************************************************
1069
     * 7 - remap 2D monitor properties, if defined
1070
     * ********************************************************************** */
1071
    MonitorProperties *props = m_doc->monitorProperties();
×
1072
    if (props != NULL)
×
1073
    {
1074
        QMap <quint32, FixturePreviewItem> remappedFixtureItems;
1075

1076
        foreach (quint32 fxID, props->fixtureItemsID())
×
1077
        {
1078
            for (int v = 0; v < sourceList.count(); v++)
×
1079
            {
1080
                if (sourceList.at(v).fxi == fxID)
×
1081
                {
1082
                    FixturePreviewItem rmpProp = props->fixtureProperties(fxID);
×
1083
                    remappedFixtureItems[targetList.at(v).fxi] = rmpProp;
×
1084
                    break;
1085
                }
×
1086
            }
1087

1088
            props->removeFixture(fxID);
×
1089
        }
1090

1091
        QMapIterator <quint32, FixturePreviewItem> it(remappedFixtureItems);
×
1092
        while (it.hasNext())
×
1093
        {
1094
            it.next();
×
1095
            props->setFixtureProperties(it.key(), it.value());
×
1096
        }
1097
    }
×
1098

1099
    /* **********************************************************************
1100
     * 8 - save the remapped project into a new file
1101
     * ********************************************************************** */
1102
    App *mainApp = (App *)m_doc->parent();
×
1103
    if (m_targetProjectLabel->text().endsWith(".qxw") == false)
×
1104
        m_targetProjectLabel->setText(m_targetProjectLabel->text() + ".qxw");
×
1105
    mainApp->setFileName(m_targetProjectLabel->text());
×
1106
    mainApp->slotFileSave();
×
1107

1108
    progress.hide();
×
1109

1110
    /* Close dialog */
1111
    QDialog::accept();
×
1112
}
×
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