• 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/fixturemanager.cpp
1
/*
2
  Q Light Controller
3
  fixturemanager.cpp
4

5
  Copyright (c) Heikki Junnila
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 <QXmlStreamWriter>
22
#include <QTreeWidgetItem>
23
#include <QTextBrowser>
24
#include <QVBoxLayout>
25
#include <QTreeWidget>
26
#include <QScrollArea>
27
#include <QMessageBox>
28
#include <QToolButton>
29
#include <QFileDialog>
30
#include <QTabWidget>
31
#include <QSplitter>
32
#include <QToolBar>
33
#include <QAction>
34
#include <QString>
35
#include <QDebug>
36
#include <QIcon>
37
#include <QMenu>
38
#include <QtGui>
39

40
#include "qlcfixturemode.h"
41
#include "qlcfixturedef.h"
42
#include "qlcchannel.h"
43
#include "qlcfile.h"
44

45
#include "createfixturegroup.h"
46
#include "fixturegroupeditor.h"
47
#include "fixturetreewidget.h"
48
#include "channelsselection.h"
49
#include "addchannelsgroup.h"
50
#include "fixturemanager.h"
51
#include "fixtureremap.h"
52
#include "addrgbpanel.h"
53
#include "addfixture.h"
54
#include "rdmmanager.h"
55
#include "universe.h"
56
#include "fixture.h"
57
#include "apputil.h"
58
#include "doc.h"
59

60
#define SETTINGS_SPLITTER "fixturemanager/splitterstate"
61

62
// List view column numbers
63
#define KColumnName     0
64
#define KColumnChannels 1
65
#define KColumnAddress  2
66

67
#define KXMLQLCFixturesList QString("FixtureList")
68

69
FixtureManager* FixtureManager::s_instance = NULL;
70

71
/*****************************************************************************
72
 * Initialization
73
 *****************************************************************************/
74

75
FixtureManager::FixtureManager(QWidget* parent, Doc* doc)
×
76
    : QWidget(parent)
77
    , m_doc(doc)
×
78
    , m_splitter(NULL)
×
79
    , m_fixtures_tree(NULL)
×
80
    , m_channel_groups_tree(NULL)
×
81
    , m_rdmManager(NULL)
×
82
    , m_info(NULL)
×
83
    , m_groupEditor(NULL)
×
84
    , m_currentTabIndex(0)
×
85
    , m_addAction(NULL)
×
86
    , m_addRGBAction(NULL)
×
87
    , m_removeAction(NULL)
×
88
    , m_propertiesAction(NULL)
×
89
    , m_fadeConfigAction(NULL)
×
90
    , m_remapAction(NULL)
×
91
    , m_groupAction(NULL)
×
92
    , m_unGroupAction(NULL)
×
93
    , m_newGroupAction(NULL)
×
94
    , m_moveUpAction(NULL)
×
95
    , m_moveDownAction(NULL)
×
96
    , m_importAction(NULL)
×
97
    , m_exportAction(NULL)
×
98
    , m_groupMenu(NULL)
×
99
{
100
    Q_ASSERT(s_instance == NULL);
101
    s_instance = this;
×
102

103
    Q_ASSERT(doc != NULL);
104

105
    new QVBoxLayout(this);
×
106
    layout()->setContentsMargins(0, 0, 0, 0);
×
107
    layout()->setSpacing(0);
×
108

109
    initActions();
×
110
    initToolBar();
×
111
    initDataView();
×
112
    updateView();
×
113
    updateChannelsGroupView();
×
114

115
    QTreeWidgetItem* grpItem = m_fixtures_tree->topLevelItem(0);
×
116
    if (grpItem != NULL)
×
117
        grpItem->setExpanded(true);
×
118

119
    /* Connect fixture list change signals from the new document object */
120
    connect(m_doc, SIGNAL(fixtureRemoved(quint32)),
×
121
            this, SLOT(slotFixtureRemoved(quint32)));
122

123
    connect(m_doc, SIGNAL(channelsGroupRemoved(quint32)),
×
124
            this, SLOT(slotChannelsGroupRemoved(quint32)));
125

126
    connect(m_doc, SIGNAL(modeChanged(Doc::Mode)),
×
127
            this, SLOT(slotModeChanged(Doc::Mode)));
128

129
    connect(m_doc, SIGNAL(fixtureGroupRemoved(quint32)),
×
130
            this, SLOT(slotFixtureGroupRemoved(quint32)));
131

132
    connect(m_doc, SIGNAL(fixtureGroupChanged(quint32)),
×
133
            this, SLOT(slotFixtureGroupChanged(quint32)));
134

135
    connect(m_doc, SIGNAL(loaded()),
×
136
            this, SLOT(slotDocLoaded()));
137

138
    slotModeChanged(m_doc->mode());
×
139

140
    QSettings settings;
×
141
    QVariant var = settings.value(SETTINGS_SPLITTER);
×
142
    if (var.isValid() == true)
×
143
        m_splitter->restoreState(var.toByteArray());
×
144
    else
145
        m_splitter->setSizes(QList <int> () << int(this->width() / 2) << int(this->width() / 2));
×
146
}
×
147

148
FixtureManager::~FixtureManager()
×
149
{
150
    QSettings settings;
×
151
    settings.setValue(SETTINGS_SPLITTER, m_splitter->saveState());
×
152
    FixtureManager::s_instance = NULL;
×
153

154
    s_instance = NULL;
155
}
×
156

157
FixtureManager* FixtureManager::instance()
×
158
{
159
    return s_instance;
×
160
}
161

162
/*****************************************************************************
163
 * Doc signal handlers
164
 *****************************************************************************/
165

166
void FixtureManager::slotFixtureRemoved(quint32 id)
×
167
{
168
    QList<QTreeWidgetItem*> groupsToDelete;
169

170
    for (int i = 0; i < m_fixtures_tree->topLevelItemCount(); i++)
×
171
    {
172
        QTreeWidgetItem* grpItem = m_fixtures_tree->topLevelItem(i);
×
173
        Q_ASSERT(grpItem != NULL);
174
        for (int j = 0; j < grpItem->childCount(); j++)
×
175
        {
176
            QTreeWidgetItem* fxiItem = grpItem->child(j);
×
177
            Q_ASSERT(fxiItem != NULL);
178
            QVariant var = fxiItem->data(KColumnName, PROP_ID);
×
179
            if (var.isValid() == true && var.toUInt() == id)
×
180
            {
181
                delete fxiItem;
×
182
                break;
183
            }
184
        }
×
185
        if (grpItem->childCount() == 0)
×
186
            groupsToDelete << grpItem;
187
    }
188
    foreach (QTreeWidgetItem* groupToDelete, groupsToDelete)
×
189
    {
190
        QVariant var = groupToDelete->data(KColumnName, PROP_GROUP);
×
191
        // If the group is a fixture group, delete it from doc.
192
        // If not, it is a universe, just "hide" it from the ui.
193
        if (var.isValid() == true)
×
194
            m_doc->deleteFixtureGroup(groupToDelete->data(KColumnName, PROP_GROUP).toUInt());
×
195
        else
196
            delete groupToDelete;
×
197
    }
×
198
}
×
199

200
void FixtureManager::slotChannelsGroupRemoved(quint32 id)
×
201
{
202
    qDebug() << "Channel group removed: " << id;
203
    for (int i = 0; i < m_channel_groups_tree->topLevelItemCount(); i++)
×
204
    {
205
        QTreeWidgetItem* grpItem = m_channel_groups_tree->topLevelItem(i);
×
206
        Q_ASSERT(grpItem != NULL);
207
        QVariant var = grpItem->data(KColumnName, PROP_ID);
×
208
        if (var.isValid() == true && var.toUInt() == id)
×
209
            delete grpItem;
×
210
    }
×
211
}
×
212

213
void FixtureManager::slotModeChanged(Doc::Mode mode)
×
214
{
215
    if (mode == Doc::Design)
×
216
    {
217
        int selected = m_fixtures_tree->selectedItems().size();
×
218

219
        QTreeWidgetItem* item = m_fixtures_tree->currentItem();
×
220
        if (item == NULL)
×
221
        {
222
            m_addAction->setEnabled(true);
×
223
            m_addRGBAction->setEnabled(true);
×
224
            m_removeAction->setEnabled(false);
×
225
            m_propertiesAction->setEnabled(false);
×
226
            m_groupAction->setEnabled(false);
×
227
            m_unGroupAction->setEnabled(false);
×
228
            m_importAction->setEnabled(true);
×
229
        }
230
        else if (item->data(KColumnName, PROP_ID).isValid() == true)
×
231
        {
232
            // Fixture selected
233
            m_addAction->setEnabled(true);
×
234
            m_addRGBAction->setEnabled(true);
×
235
            m_removeAction->setEnabled(true);
×
236
            if (selected == 1)
×
237
                m_propertiesAction->setEnabled(true);
×
238
            else
239
                m_propertiesAction->setEnabled(false);
×
240
            m_groupAction->setEnabled(true);
×
241

242
            // Don't allow ungrouping from the "All fixtures" group
243
            if (item->parent()->data(KColumnName, PROP_GROUP).isValid() == true)
×
244
                m_unGroupAction->setEnabled(true);
×
245
            else
246
                m_unGroupAction->setEnabled(false);
×
247
        }
248
        else if (item->data(KColumnName, PROP_GROUP).isValid() == true)
×
249
        {
250
            // Fixture group selected
251
            m_addAction->setEnabled(true);
×
252
            m_addRGBAction->setEnabled(true);
×
253
            m_removeAction->setEnabled(true);
×
254
            m_propertiesAction->setEnabled(false);
×
255
            m_groupAction->setEnabled(false);
×
256
            m_unGroupAction->setEnabled(false);
×
257
        }
258
        else
259
        {
260
            // All fixtures selected
261
            m_addAction->setEnabled(true);
×
262
            m_addRGBAction->setEnabled(true);
×
263
            m_removeAction->setEnabled(false);
×
264
            m_propertiesAction->setEnabled(false);
×
265
            m_groupAction->setEnabled(false);
×
266
            m_unGroupAction->setEnabled(false);
×
267
        }
268
        if (m_doc->fixtures().count() > 0)
×
269
            m_fadeConfigAction->setEnabled(true);
×
270
        else
271
            m_fadeConfigAction->setEnabled(false);
×
272
    }
273
    else
274
    {
275
        m_addAction->setEnabled(false);
×
276
        m_addRGBAction->setEnabled(false);
×
277
        m_removeAction->setEnabled(false);
×
278
        m_propertiesAction->setEnabled(false);
×
279
        m_fadeConfigAction->setEnabled(false);
×
280
        m_groupAction->setEnabled(false);
×
281
        m_unGroupAction->setEnabled(false);
×
282
    }
283
}
×
284

285
void FixtureManager::slotFixtureGroupRemoved(quint32 id)
×
286
{
287
    for (int i = 0; i < m_fixtures_tree->topLevelItemCount(); i++)
×
288
    {
289
        QTreeWidgetItem* item = m_fixtures_tree->topLevelItem(i);
×
290
        Q_ASSERT(item != NULL);
291
        QVariant var = item->data(KColumnName, PROP_GROUP);
×
292
        if (var.isValid() && var.toUInt() == id)
×
293
        {
294
            delete item;
×
295
            break;
296
        }
297
    }
×
298

299
    updateGroupMenu();
×
300
}
×
301

302
void FixtureManager::slotFixtureGroupChanged(quint32 id)
×
303
{
304
    QTreeWidgetItem* item = m_fixtures_tree->groupItem(id);
×
305
    if (item == NULL)
×
306
        return;
307

308
    FixtureGroup* grp = m_doc->fixtureGroup(id);
×
309
    Q_ASSERT(grp != NULL);
310
    m_fixtures_tree->updateGroupItem(item, grp);
×
311
    updateGroupMenu();
×
312
}
313

314
void FixtureManager::slotDocLoaded()
×
315
{
316
    slotTabChanged(m_currentTabIndex);
×
317
}
×
318

319
/*****************************************************************************
320
 * Data view
321
 *****************************************************************************/
322

323
void FixtureManager::initDataView()
×
324
{
325
    // Create a splitter to divide list view and text view
326
    m_splitter = new QSplitter(Qt::Horizontal, this);
×
327
    layout()->addWidget(m_splitter);
×
328
    m_splitter->setSizePolicy(QSizePolicy::Expanding,
×
329
                              QSizePolicy::Expanding);
330

331
    QTabWidget *tabs = new QTabWidget(this);
×
332
    m_splitter->addWidget(tabs);
×
333

334
    /* Create a tree widget to the left part of the splitter */
335
    quint32 treeFlags = FixtureTreeWidget::UniverseNumber |
336
                        FixtureTreeWidget::AddressRange |
337
                        FixtureTreeWidget::ShowGroups;
338

339
    m_fixtures_tree = new FixtureTreeWidget(m_doc, treeFlags, this);
×
340
    m_fixtures_tree->setIconSize(QSize(32, 32));
×
341
    m_fixtures_tree->setContextMenuPolicy(Qt::CustomContextMenu);
×
342
    m_fixtures_tree->setSelectionMode(QAbstractItemView::ExtendedSelection);
×
343
    m_fixtures_tree->sortByColumn(KColumnAddress, Qt::AscendingOrder);
×
344

345
    connect(m_fixtures_tree, SIGNAL(itemSelectionChanged()),
×
346
            this, SLOT(slotSelectionChanged()));
347

348
    connect(m_fixtures_tree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
×
349
            this, SLOT(slotDoubleClicked(QTreeWidgetItem*)));
350

351
    connect(m_fixtures_tree, SIGNAL(customContextMenuRequested(const QPoint&)),
×
352
            this, SLOT(slotContextMenuRequested(const QPoint&)));
353

354
    connect(m_fixtures_tree, SIGNAL(expanded(QModelIndex)),
×
355
            this, SLOT(slotFixtureItemExpanded()));
356

357
    connect(m_fixtures_tree, SIGNAL(collapsed(QModelIndex)),
×
358
            this, SLOT(slotFixtureItemExpanded()));
359

360
    tabs->addTab(m_fixtures_tree, tr("Fixture Groups"));
×
361

362
    m_channel_groups_tree = new QTreeWidget(this);
×
363
    QStringList chan_labels;
364
    chan_labels << tr("Name") << tr("Channels");
×
365
    m_channel_groups_tree->setHeaderLabels(chan_labels);
×
366
    m_channel_groups_tree->setRootIsDecorated(false);
×
367
    m_channel_groups_tree->setAllColumnsShowFocus(true);
×
368
    m_channel_groups_tree->setIconSize(QSize(32, 32));
×
369
    m_channel_groups_tree->setSelectionMode(QAbstractItemView::ExtendedSelection);
×
370

371
    connect(m_channel_groups_tree, SIGNAL(itemSelectionChanged()),
×
372
            this, SLOT(slotChannelsGroupSelectionChanged()));
373
    connect(m_channel_groups_tree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
×
374
            this, SLOT(slotChannelsGroupDoubleClicked(QTreeWidgetItem*)));
375

376
    tabs->addTab(m_channel_groups_tree, tr("Channel Groups"));
×
377
/*
378
    m_rdmManager = new RDMManager(this, m_doc);
379
    tabs->addTab(m_rdmManager, "RDM");
380
    connect(m_rdmManager, SIGNAL(fixtureInfoReady(QString&)),
381
            this, SLOT(slotDisplayFixtureInfo(QString&)));
382
*/
383
    connect(tabs, SIGNAL(currentChanged(int)), this, SLOT(slotTabChanged(int)));
×
384

385
    /* Create the text view */
386
    createInfo();
×
387

388
    slotSelectionChanged();
×
389
}
×
390

391
void FixtureManager::updateView()
×
392
{
393
    // Record which top level items are open
394
    QList <QVariant> openGroups;
395
    for (int i = 0; i < m_fixtures_tree->topLevelItemCount(); i++)
×
396
    {
397
        QTreeWidgetItem* item = m_fixtures_tree->topLevelItem(i);
×
398
        if (item->isExpanded() == true)
×
399
            openGroups << item->data(KColumnName, PROP_GROUP);
×
400
    }
401

402
    if (m_doc->fixtures().count() > 0)
×
403
    {
404
        m_exportAction->setEnabled(true);
×
405
        m_remapAction->setEnabled(true);
×
406
        m_fadeConfigAction->setEnabled(true);
×
407
    }
408
    else
409
    {
410
        m_exportAction->setEnabled(false);
×
411
        m_fadeConfigAction->setEnabled(false);
×
412
        m_remapAction->setEnabled(false);
×
413
    }
414
    m_addRGBAction->setEnabled(true);
×
415
    m_importAction->setEnabled(true);
×
416
    m_moveUpAction->setEnabled(false);
×
417
    m_moveDownAction->setEnabled(false);
×
418

419
    m_fixtures_tree->updateTree();
×
420

421
    // Reopen groups that were open before update
422
    for (int i = 0; i < m_fixtures_tree->topLevelItemCount(); i++)
×
423
    {
424
        QTreeWidgetItem* item = m_fixtures_tree->topLevelItem(i);
×
425
        QVariant var = item->data(KColumnName, PROP_GROUP);
×
426
        if (openGroups.contains(var) == true)
×
427
        {
428
            item->setExpanded(true);
×
429
            openGroups.removeAll(var);
×
430
        }
431
    }
×
432

433
    updateGroupMenu();
×
434
    slotModeChanged(m_doc->mode());
×
435

436
    m_fixtures_tree->header()->resizeSections(QHeaderView::ResizeToContents);
×
437
}
×
438

439
void FixtureManager::updateChannelsGroupView()
×
440
{
441
    quint32 selGroupID = ChannelsGroup::invalidId();
×
442

443
    if (m_channel_groups_tree->selectedItems().size() > 0)
×
444
    {
445
        QTreeWidgetItem *item = m_channel_groups_tree->selectedItems().first();
×
446
        selGroupID = item->data(KColumnName, PROP_ID).toUInt();
×
447
    }
448

449
    if (m_channel_groups_tree->topLevelItemCount() > 0)
×
450
        for (int i = m_channel_groups_tree->topLevelItemCount() - 1; i >= 0; i--)
×
451
            m_channel_groups_tree->takeTopLevelItem(i);
×
452

453
    foreach (ChannelsGroup *grp, m_doc->channelsGroups())
×
454
    {
455
        QTreeWidgetItem *grpItem = new QTreeWidgetItem(m_channel_groups_tree);
×
456
        grpItem->setText(KColumnName, grp->name());
×
457
        grpItem->setData(KColumnName, PROP_ID, grp->id());
×
458
        grpItem->setText(KColumnChannels, QString("%1").arg(grp->getChannels().count()));
×
459
        if (grp->getChannels().count() > 0)
×
460
        {
461
            SceneValue scv = grp->getChannels().at(0);
×
462
            Fixture *fxi = m_doc->fixture(scv.fxi);
×
463
            if (fxi == NULL)
×
464
                continue;
465

466
            const QLCChannel *ch = fxi->channel(scv.channel);
×
467
            if (ch != NULL)
×
468
                grpItem->setIcon(KColumnName, ch->getIcon());
×
469
        }
×
470
        if (selGroupID == grp->id())
×
471
            grpItem->setSelected(true);
×
472
    }
473
    m_addRGBAction->setEnabled(false);
×
474
    m_propertiesAction->setEnabled(false);
×
475
    m_groupAction->setEnabled(false);
×
476
    m_unGroupAction->setEnabled(false);
×
477
    m_fadeConfigAction->setEnabled(false);
×
478
    m_exportAction->setEnabled(false);
×
479
    m_importAction->setEnabled(false);
×
480
    m_remapAction->setEnabled(false);
×
481

482
    m_channel_groups_tree->header()->resizeSections(QHeaderView::ResizeToContents);
×
483
}
×
484

485
void FixtureManager::updateRDMView()
×
486
{
487
    m_addRGBAction->setEnabled(false);
×
488
    m_propertiesAction->setEnabled(false);
×
489
    m_groupAction->setEnabled(false);
×
490
    m_unGroupAction->setEnabled(false);
×
491
    m_fadeConfigAction->setEnabled(false);
×
492
    m_exportAction->setEnabled(false);
×
493
    m_importAction->setEnabled(false);
×
494
    m_remapAction->setEnabled(false);
×
495
}
×
496

497
void FixtureManager::fixtureSelected(quint32 id)
×
498
{
499
    Fixture* fxi = m_doc->fixture(id);
×
500
    if (fxi == NULL)
×
501
        return;
502

503
    if (m_info == NULL)
×
504
        createInfo();
×
505

506
    m_info->setText(QString("%1<BODY>%2</BODY></HTML>")
×
507
                    .arg(fixtureInfoStyleSheetHeader())
×
508
                    .arg(fxi->status()));
×
509

510
    // Enable/disable actions
511
    slotModeChanged(m_doc->mode());
×
512
}
513

514
void FixtureManager::fixtureGroupSelected(FixtureGroup* grp)
×
515
{
516
    QByteArray state = m_splitter->saveState();
×
517

518
    if (m_info != NULL)
×
519
    {
520
        delete m_info;
×
521
        m_info = NULL;
×
522
    }
523

524
    if (m_groupEditor != NULL)
×
525
    {
526
        delete m_groupEditor;
×
527
        m_groupEditor = NULL;
×
528
    }
529

530
    m_groupEditor = new FixtureGroupEditor(grp, m_doc, this);
×
531
    m_splitter->addWidget(m_groupEditor);
×
532

533
    m_splitter->restoreState(state);
×
534
}
×
535

536
void FixtureManager::createInfo()
×
537
{
538
    QByteArray state = m_splitter->saveState();
×
539

540
    if (m_info != NULL)
×
541
    {
542
        delete m_info;
×
543
        m_info = NULL;
×
544
    }
545

546
    if (m_groupEditor != NULL)
×
547
    {
548
        delete m_groupEditor;
×
549
        m_groupEditor = NULL;
×
550
    }
551

552
    m_info = new QTextBrowser(this);
×
553
    m_splitter->addWidget(m_info);
×
554

555
    m_splitter->restoreState(state);
×
556
}
×
557

558
void FixtureManager::slotSelectionChanged()
×
559
{
560
    int selectedCount = m_fixtures_tree->selectedItems().size();
×
561
    if (selectedCount == 1)
×
562
    {
563
        QTreeWidgetItem* item = m_fixtures_tree->selectedItems().first();
×
564
        Q_ASSERT(item != NULL);
565

566
        // Set the text view's contents
567
        QVariant fxivar = item->data(KColumnName, PROP_ID);
×
568
        QVariant grpvar = item->data(KColumnName, PROP_GROUP);
×
569
        if (fxivar.isValid() == true)
×
570
        {
571
            // Selected a fixture
572
            fixtureSelected(fxivar.toUInt());
×
573
        }
574
        else if (grpvar.isValid() == true)
×
575
        {
576
            FixtureGroup* grp = m_doc->fixtureGroup(grpvar.toUInt());
×
577
            Q_ASSERT(grp != NULL);
578
            fixtureGroupSelected(grp);
×
579
        }
580
        else
581
        {
582
            QString info = "<HTML><BODY>";
×
583
            QString uniName;
584
            double totalWeight = 0;
585
            int totalPower = 0;
586
            QVariant uniID = item->data(KColumnName, PROP_UNIVERSE);
×
587
            if (uniID.isValid() == true)
×
588
                uniName = m_doc->inputOutputMap()->getUniverseNameByID(uniID.toUInt());
×
589

590
            foreach (Fixture *fixture, m_doc->fixtures())
×
591
            {
592
                if (fixture == NULL || fixture->universe() != uniID.toUInt() || fixture->fixtureMode() == NULL)
×
593
                    continue;
×
594

595
                QLCFixtureMode *mode = fixture->fixtureMode();
×
596
                totalWeight += mode->physical().weight();
×
597
                totalPower += mode->physical().powerConsumption();
×
598
            }
599

600
            if (m_info == NULL)
×
601
                createInfo();
×
602

603
            info += QString("<H1>%1</H1><P>%2 <B>%3</B></P>")
×
604
                    .arg(uniName).arg(tr("This group contains all fixtures of"))
×
605
                    .arg(uniName);
×
606

607
            info += QString("<BR><P><B>%1</B>: %2Kg<BR><B>%3</B>: %4W</P>")
×
608
                    .arg(tr("Total estimated weight")).arg(QString::number(totalWeight))
×
609
                    .arg(tr("Maximum estimated power consumption")).arg(totalPower);
×
610

611
            info += "</BODY></HTML>";
×
612

613
            m_info->setText(info);
×
614
        }
×
615
    }
×
616
    else
617
    {
618
        // More than one or less than one selected
619
        QString info = "<HTML><BODY>";
×
620
        if (selectedCount > 1)
×
621
        {
622
            // Enable removal of multiple items in design mode
623
            if (m_doc->mode() == Doc::Design)
×
624
            {
625
                double totalWeight = 0;
626
                int totalPower = 0;
627

628
                info += tr("<H1>Multiple fixtures selected</H1>" \
×
629
                          "<P>Click <IMG SRC=\"" ":/edit_remove.png\">" \
630
                          " to remove the selected fixtures.</P>");
631

632
                foreach (QTreeWidgetItem *item, m_fixtures_tree->selectedItems())
×
633
                {
634
                    QVariant fxID = item->data(KColumnName, PROP_ID);
×
635
                    if (fxID.isValid() == false)
×
636
                        continue;
×
637

638
                    Fixture *fixture = m_doc->fixture(fxID.toUInt());
×
639

640
                    if (fixture == NULL || fixture->fixtureMode() == NULL)
×
641
                        continue;
×
642

643
                    QLCFixtureMode *mode = fixture->fixtureMode();
×
644
                    totalWeight += mode->physical().weight();
×
645
                    totalPower += mode->physical().powerConsumption();
×
646
                }
×
647

648
                info += QString("<BR><P><B>%1</B>: %2Kg<BR><B>%3</B>: %4W</P>")
×
649
                        .arg(tr("Total estimated weight")).arg(QString::number(totalWeight))
×
650
                        .arg(tr("Maximum estimated power consumption")).arg(totalPower);
×
651
            }
652
            else
653
            {
654
                info += tr("<H1>Multiple fixtures selected</H1>" \
×
655
                          "<P>Fixture list modification is not permitted" \
656
                          " in operate mode.</P>");
657
            }
658
        }
659
        else
660
        {
661
            if (m_fixtures_tree->topLevelItemCount() <= 0)
×
662
            {
663
                info += tr("<H1>No fixtures</H1>" \
×
664
                          "<P>Click <IMG SRC=\"" ":/edit_add.png\">" \
665
                          " to add fixtures.</P>");
666
            }
667
            else
668
            {
669
                info += tr("<H1>Nothing selected</H1>" \
×
670
                          "<P>Select a fixture from the list or " \
671
                          "click <IMG SRC=\"" ":/edit_add.png\">" \
672
                          " to add fixtures.</P>");
673
            }
674
        }
675
        info += "</BODY></HTML>";
×
676

677
        if (m_info == NULL)
×
678
            createInfo();
×
679
        m_info->setText(info);
×
680
    }
×
681

682
    // Enable/disable actions
683
    slotModeChanged(m_doc->mode());
×
684
}
×
685

686
void FixtureManager::slotChannelsGroupSelectionChanged()
×
687
{
688
    if (m_info == NULL)
×
689
        createInfo();
×
690

691
    int selectedCount = m_channel_groups_tree->selectedItems().size();
×
692

693
    if (selectedCount == 1)
×
694
    {
695
        QTreeWidgetItem* item = m_channel_groups_tree->selectedItems().first();
×
696
        Q_ASSERT(item != NULL);
697

698
        // Set the text view's contents
699
        QVariant grpvar = item->data(KColumnName, PROP_ID);
×
700
        if (grpvar.isValid() == true)
×
701
        {
702
            ChannelsGroup *chGroup = m_doc->channelsGroup(grpvar.toUInt());
×
703
            if (chGroup != NULL)
×
704
                m_info->setText(QString("%1<BODY>%2</BODY></HTML>")
×
705
                                .arg(channelsGroupInfoStyleSheetHeader())
×
706
                                .arg(chGroup->status(m_doc)));
×
707
        }
708
        m_removeAction->setEnabled(true);
×
709
        m_propertiesAction->setEnabled(true);
×
710
        int selIdx = m_channel_groups_tree->currentIndex().row();
×
711
        if (selIdx == 0)
×
712
            m_moveUpAction->setEnabled(false);
×
713
        else
714
            m_moveUpAction->setEnabled(true);
×
715
        if (selIdx == m_channel_groups_tree->topLevelItemCount() - 1)
×
716
            m_moveDownAction->setEnabled(false);
×
717
        else
718
            m_moveDownAction->setEnabled(true);
×
719
    }
×
720
    else if (selectedCount > 1)
×
721
    {
722
        m_info->setText(tr("<HTML><BODY><H1>Multiple groups selected</H1>" \
×
723
                  "<P>Click <IMG SRC=\"" ":/edit_remove.png\">" \
724
                  " to remove the selected groups.</P></BODY></HTML>"));
725
        m_removeAction->setEnabled(true);
×
726
        m_propertiesAction->setEnabled(false);
×
727
    }
728
    else
729
    {
730
        m_info->setText(tr("<HTML><BODY><H1>Nothing selected</H1>" \
×
731
                  "<P>Select a channel group from the list or " \
732
                  "click <IMG SRC=\"" ":/edit_add.png\">" \
733
                  " to add a new channels group.</P></BODY></HTML>"));
734
        m_removeAction->setEnabled(false);
×
735
        m_propertiesAction->setEnabled(false);
×
736
    }
737
}
×
738

739
void FixtureManager::slotDoubleClicked(QTreeWidgetItem* item)
×
740
{
741
    if (item != NULL && m_doc->mode() != Doc::Operate)
×
742
        slotProperties();
×
743
}
×
744

745
void FixtureManager::slotChannelsGroupDoubleClicked(QTreeWidgetItem*)
×
746
{
747
    slotChannelsGroupSelectionChanged();
×
748
    editChannelGroupProperties();
×
749
}
×
750

751
void FixtureManager::slotTabChanged(int index)
×
752
{
753
    if (index == 1)
×
754
    {
755
        m_addAction->setToolTip(tr("Add group..."));
×
756
        updateChannelsGroupView();
×
757
        slotChannelsGroupSelectionChanged();
×
758
    }
759
    else if (index == 2)
×
760
    {
761
        m_addAction->setToolTip(tr("Add fixture..."));
×
762
        updateRDMView();
×
763
    }
764
    else
765
    {
766
        m_addAction->setToolTip(tr("Add fixture..."));
×
767
        updateView();
×
768
        slotSelectionChanged();
×
769
    }
770

771
    m_currentTabIndex = index;
×
772
}
×
773

774
void FixtureManager::slotFixtureItemExpanded()
×
775
{
776
    m_fixtures_tree->header()->resizeSections(QHeaderView::ResizeToContents);
×
777
}
×
778

779
void FixtureManager::slotDisplayFixtureInfo(QString &info)
×
780
{
781
    m_info->setText(info);
×
782
}
×
783

784
void FixtureManager::selectGroup(quint32 id)
×
785
{
786
    for (int i = 0; i < m_fixtures_tree->topLevelItemCount(); i++)
×
787
    {
788
        QTreeWidgetItem* item = m_fixtures_tree->topLevelItem(i);
×
789
        QVariant var = item->data(KColumnName, PROP_GROUP);
×
790
        if (var.isValid() == false)
×
791
            continue;
792

793
        if (var.toUInt() == id)
×
794
        {
795
            m_fixtures_tree->setCurrentItem(item);
×
796
            slotSelectionChanged();
×
797
            break;
798
        }
799
    }
×
800
}
×
801

802
QString FixtureManager::fixtureInfoStyleSheetHeader()
×
803
{
804
    QString info;
805

806
    QPalette pal;
×
807
    QColor hlBack(pal.color(QPalette::Highlight));
808
    QColor hlText(pal.color(QPalette::HighlightedText));
809

810
    info += "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">";
×
811
    info += "<HTML><HEAD></HEAD><STYLE>";
×
812
    info += QString(".hilite {" \
×
813
                    "        background-color: %1;" \
814
                    "        color: %2;" \
815
                    "        font-size: x-large;" \
816
                    "}").arg(hlBack.name()).arg(hlText.name());
×
817
    info += QString(".subhi {" \
×
818
                    "        background-color: %1;" \
819
                    "        color: %2;" \
820
                    "        font-weight: bold;" \
821
                    "}").arg(hlBack.name()).arg(hlText.name());
×
822
    info += QString(".emphasis {" \
×
823
                    "        font-weight: bold;" \
824
                    "}");
825
    info += QString(".tiny {"\
×
826
                    "   font-size: small;" \
827
                    "}");
828
    info += QString(".author {" \
×
829
                    "        font-weight: light;" \
830
                    "        font-style: italic;" \
831
                    "   text-align: right;" \
832
                    "   font-size: small;"  \
833
                    "}");
834
    info += "</STYLE>";
×
835
    return info;
×
836
}
×
837

838
QString FixtureManager::channelsGroupInfoStyleSheetHeader()
×
839
{
840
    QString info;
841

842
    QPalette pal;
×
843
    QColor hlBack(pal.color(QPalette::Highlight));
844
    QColor hlBackSmall(pal.color(QPalette::Shadow));
845
    QColor hlText(pal.color(QPalette::HighlightedText));
846

847
    info += "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">";
×
848
    info += "<HTML><HEAD></HEAD><STYLE>";
×
849
    info += QString(".hilite {" \
×
850
                    "        background-color: %1;" \
851
                    "        color: %2;" \
852
                    "        font-size: x-large;" \
853
                    "}").arg(hlBack.name()).arg(hlText.name());
×
854
    info += QString(".subhi {" \
×
855
                    "        background-color: %1;" \
856
                    "        color: %2;" \
857
                    "        font-weight: bold;" \
858
                    "}").arg(hlBackSmall.name()).arg(hlText.name());
×
859
    info += QString(".emphasis {" \
×
860
                    "        font-weight: bold;" \
861
                    "}");
862
    info += QString(".tiny {"\
×
863
                    "   font-size: small;" \
864
                    "}");
865
    info += "</STYLE>";
×
866
    return info;
×
867
}
×
868

869
/*****************************************************************************
870
 * Menu, toolbar and actions
871
 *****************************************************************************/
872

873
void FixtureManager::initActions()
×
874
{
875
    // Fixture actions
876
    m_addAction = new QAction(QIcon(":/edit_add.png"),
×
877
                              tr("Add fixture..."), this);
×
878
    connect(m_addAction, SIGNAL(triggered(bool)),
×
879
            this, SLOT(slotAdd()));
880

881
    m_addRGBAction = new QAction(QIcon(":/rgbpanel.png"),
×
882
                              tr("Add RGB panel..."), this);
×
883
    connect(m_addRGBAction, SIGNAL(triggered(bool)),
×
884
            this, SLOT(slotAddRGBPanel()));
885

886
    m_removeAction = new QAction(QIcon(":/edit_remove.png"),
×
887
                                 tr("Delete items"), this);
×
888
    connect(m_removeAction, SIGNAL(triggered(bool)),
×
889
            this, SLOT(slotRemove()));
890

891
    m_propertiesAction = new QAction(QIcon(":/configure.png"),
×
892
                                     tr("Properties..."), this);
×
893
    connect(m_propertiesAction, SIGNAL(triggered(bool)),
×
894
            this, SLOT(slotProperties()));
895

896
    m_fadeConfigAction = new QAction(QIcon(":/fade.png"),
×
897
                                     tr("Channels Fade Configuration..."), this);
×
898
    connect(m_fadeConfigAction, SIGNAL(triggered(bool)),
×
899
            this, SLOT(slotFadeConfig()));
900

901
    // Group actions
902
    m_groupAction = new QAction(QIcon(":/group.png"),
×
903
                                tr("Add fixture to group..."), this);
×
904

905
    m_unGroupAction = new QAction(QIcon(":/ungroup.png"),
×
906
                                tr("Remove fixture from group"), this);
×
907
    connect(m_unGroupAction, SIGNAL(triggered(bool)),
×
908
            this, SLOT(slotUnGroup()));
909

910
    m_newGroupAction = new QAction(tr("New Group..."), this);
×
911

912
    m_moveUpAction = new QAction(QIcon(":/up.png"),
×
913
                                 tr("Move channel group up..."), this);
×
914
    m_moveUpAction->setEnabled(false);
×
915
    connect(m_moveUpAction, SIGNAL(triggered(bool)),
×
916
            this, SLOT(slotMoveGroupUp()));
917

918
    m_moveDownAction = new QAction(QIcon(":/down.png"),
×
919
                                 tr("Move channel group down..."), this);
×
920
    m_moveDownAction->setEnabled(false);
×
921
    connect(m_moveDownAction, SIGNAL(triggered(bool)),
×
922
            this, SLOT(slotMoveGroupDown()));
923

924
    m_importAction = new QAction(QIcon(":/fileimport.png"),
×
925
                                 tr("Import fixtures..."), this);
×
926
    connect(m_importAction, SIGNAL(triggered(bool)),
×
927
            this, SLOT(slotImport()));
928

929
    m_exportAction = new QAction(QIcon(":/fileexport.png"),
×
930
                                 tr("Export fixtures..."), this);
×
931

932
    connect(m_exportAction, SIGNAL(triggered(bool)),
×
933
            this, SLOT(slotExport()));
934

935
    m_remapAction = new QAction(QIcon(":/remap.png"),
×
936
                               tr("Remap fixtures..."), this);
×
937
    connect(m_remapAction, SIGNAL(triggered(bool)),
×
938
            this, SLOT(slotRemap()));
939
}
×
940

941
void FixtureManager::updateGroupMenu()
×
942
{
943
    if (m_groupMenu == NULL)
×
944
    {
945
        m_groupMenu = new QMenu(this);
×
946
        connect(m_groupMenu, SIGNAL(triggered(QAction*)),
×
947
                this, SLOT(slotGroupSelected(QAction*)));
948
    }
949

950
    foreach (QAction* a, m_groupMenu->actions())
×
951
        m_groupMenu->removeAction(a);
×
952

953
    // Put all known fixture groups to the menu
954
    foreach (FixtureGroup* grp, m_doc->fixtureGroups())
×
955
    {
956
        QAction* a = m_groupMenu->addAction(grp->name());
×
957
        a->setData((qulonglong) grp);
×
958
    }
959

960
    // Put a new group action to the group menu
961
    m_groupMenu->addAction(m_newGroupAction);
×
962

963
    // Put the group menu to the group action
964
    m_groupAction->setMenu(m_groupMenu);
×
965
}
×
966

967
void FixtureManager::initToolBar()
×
968
{
969
    QToolBar* toolbar = new QToolBar(tr("Fixture manager"), this);
×
970
    toolbar->setFloatable(false);
×
971
    toolbar->setMovable(false);
×
972
    layout()->setMenuBar(toolbar);
×
973
    toolbar->addAction(m_addAction);
×
974
    toolbar->addAction(m_addRGBAction);
×
975
    toolbar->addAction(m_removeAction);
×
976
    toolbar->addAction(m_propertiesAction);
×
977
    toolbar->addAction(m_fadeConfigAction);
×
978
    toolbar->addSeparator();
×
979
    toolbar->addAction(m_groupAction);
×
980
    toolbar->addAction(m_unGroupAction);
×
981
    toolbar->addSeparator();
×
982
    toolbar->addAction(m_moveUpAction);
×
983
    toolbar->addAction(m_moveDownAction);
×
984
    toolbar->addSeparator();
×
985
    toolbar->addAction(m_importAction);
×
986
    toolbar->addAction(m_exportAction);
×
987
    toolbar->addAction(m_remapAction);
×
988

989
    QToolButton* btn = qobject_cast<QToolButton*> (toolbar->widgetForAction(m_groupAction));
×
990
    Q_ASSERT(btn != NULL);
991
    btn->setPopupMode(QToolButton::InstantPopup);
×
992
}
×
993

994
void FixtureManager::addFixture()
×
995
{
996
    AddFixture af(this, m_doc);
×
997
    if (af.exec() == QDialog::Rejected)
×
998
        return;
999

1000
    if (af.invalidAddress())
×
1001
    {
1002
        QMessageBox msg(QMessageBox::Critical, tr("Error"),
×
1003
                tr("Please enter a valid address"), QMessageBox::Ok);
×
1004
        msg.exec();
×
1005
        return;
1006
    }
×
1007

1008
    quint32 latestFxi = Fixture::invalidId();
×
1009

1010
    QString name = af.name();
×
1011
    quint32 address = af.address();
×
1012
    quint32 universe = af.universe();
×
1013
    quint32 channels = af.channels();
×
1014
    int gap = af.gap();
×
1015

1016
    QLCFixtureDef* fixtureDef = af.fixtureDef();
×
1017
    QLCFixtureMode* mode = af.mode();
×
1018

1019
    FixtureGroup* addToGroup = NULL;
1020
    QTreeWidgetItem* current = m_fixtures_tree->currentItem();
×
1021
    if (current != NULL)
×
1022
    {
1023
        if (current->parent() != NULL)
×
1024
        {
1025
            // Fixture selected
1026
            QVariant var = current->parent()->data(KColumnName, PROP_GROUP);
×
1027
            if (var.isValid() == true)
×
1028
                addToGroup = m_doc->fixtureGroup(var.toUInt());
×
1029
        }
×
1030
        else
1031
        {
1032
            // Group selected
1033
            QVariant var = current->data(KColumnName, PROP_GROUP);
×
1034
            if (var.isValid() == true)
×
1035
                addToGroup = m_doc->fixtureGroup(var.toUInt());
×
1036
        }
×
1037
    }
1038

1039
    /* If an empty name was given use the model instead */
1040
    if (name.simplified().isEmpty())
×
1041
    {
1042
        if (fixtureDef != NULL)
×
1043
            name = fixtureDef->model();
×
1044
        else
1045
            name = tr("Generic Dimmer");
×
1046
    }
1047

1048
    /* Add the rest (if any) WITH address gap */
1049
    for (int i = 0; i < af.amount(); i++)
×
1050
    {
1051
        QString modname;
1052

1053
        /* If we're adding more than one fixture,
1054
           append a number to the end of the name */
1055
        if (af.amount() > 1)
×
1056
            modname = QString("%1 #%2").arg(name).arg(i + 1, AppUtil::digits(af.amount()), 10, QChar('0'));
×
1057
        else
1058
            modname = name;
×
1059

1060
        /* Create the fixture */
1061
        Fixture* fxi = new Fixture(m_doc);
×
1062

1063
        /* Assign the next address AFTER the previous fixture
1064
           address space plus gap. */
1065
        fxi->setAddress(address + (i * channels) + (i * gap));
×
1066
        fxi->setUniverse(universe);
×
1067
        fxi->setName(modname);
×
1068
        /* Set a fixture definition & mode if they were
1069
           selected. Otherwise create a fixture definition
1070
           and mode for a generic dimmer. */
1071
        if (fixtureDef != NULL && mode != NULL)
×
1072
        {
1073
            fxi->setFixtureDefinition(fixtureDef, mode);
×
1074
        }
1075
        else
1076
        {
1077
            QLCFixtureDef* genericDef = fxi->genericDimmerDef(channels);
×
1078
            QLCFixtureMode* genericMode = fxi->genericDimmerMode(genericDef, channels);
×
1079
            fxi->setFixtureDefinition(genericDef, genericMode);
×
1080
        }
1081

1082
        m_doc->addFixture(fxi);
×
1083
        latestFxi = fxi->id();
×
1084
        if (addToGroup != NULL)
×
1085
            addToGroup->assignFixture(latestFxi);
×
1086
    }
×
1087

1088
    QTreeWidgetItem* selectItem = m_fixtures_tree->fixtureItem(latestFxi);
×
1089
    if (selectItem != NULL)
×
1090
        m_fixtures_tree->setCurrentItem(selectItem);
×
1091

1092
    updateView();
×
1093
}
×
1094

1095
void FixtureManager::addChannelsGroup()
×
1096
{
1097
    ChannelsGroup *group = new ChannelsGroup(m_doc);
×
1098

1099
    AddChannelsGroup cs(this, m_doc, group);
×
1100
    if (cs.exec() == QDialog::Accepted)
×
1101
    {
1102
        qDebug() << "Channels group added. Count: " << group->getChannels().count();
1103
        m_doc->addChannelsGroup(group, group->id());
×
1104
        updateChannelsGroupView();
×
1105
    }
1106
    else
1107
        delete group;
×
1108
}
×
1109

1110
void FixtureManager::slotAdd()
×
1111
{
1112
    if (m_currentTabIndex == 1)
×
1113
        addChannelsGroup();
×
1114
    else
1115
        addFixture();
×
1116
}
×
1117

1118
void FixtureManager::slotAddRGBPanel()
×
1119
{
1120
    AddRGBPanel rgb(this, m_doc);
×
1121
    if (rgb.exec() == QDialog::Accepted)
×
1122
    {
1123
        int rows = rgb.rows();
×
1124
        int columns = rgb.columns();
×
1125
        Fixture::Components components = rgb.components();
×
1126

1127
        FixtureGroup *grp = new FixtureGroup(m_doc);
×
1128
        Q_ASSERT(grp != NULL);
1129
        grp->setName(rgb.name());
×
1130
        QSize panelSize(columns, rows);
1131
        grp->setSize(panelSize);
×
1132
        m_doc->addFixtureGroup(grp);
×
1133
        updateGroupMenu();
×
1134

1135
        int transpose = 0;
1136
        if (rgb.direction() == AddRGBPanel::Vertical)
×
1137
        {
1138
                int tmp = columns;
1139
                columns = rows;
1140
                rows = tmp;
1141
                transpose = 1;
1142
        }
1143

1144
        QLCFixtureDef *rowDef = NULL;
1145
        QLCFixtureMode *rowMode = NULL;
1146
        quint32 address = (quint32)rgb.address();
×
1147
        int uniIndex = rgb.universeIndex();
×
1148
        int currRow = 0;
1149
        int rowInc = 1;
1150
        int xPosStart = 0;
1151
        int xPosEnd = columns - 1;
×
1152
        int xPosInc = 1;
1153

1154
        quint32 phyWidth = rgb.physicalWidth();
×
1155
        quint32 phyHeight = rgb.physicalHeight() / rows;
×
1156

1157
        if (transpose)
×
1158
        {
1159
                        if (rgb.orientation() == AddRGBPanel::TopRight ||
×
1160
                                rgb.orientation() == AddRGBPanel::BottomRight)
×
1161
                        {
1162
                                currRow = rows -1;
×
1163
                                rowInc = -1;
1164
                        }
1165
                        if (rgb.orientation() == AddRGBPanel::BottomRight ||
×
1166
                                rgb.orientation() == AddRGBPanel::BottomLeft)
×
1167
                        {
1168
                                xPosStart = columns - 1;
1169
                                xPosEnd = 0;
1170
                                xPosInc = -1;
1171
                        }
1172
        }
1173
        else
1174
        {
1175
                        if (rgb.orientation() == AddRGBPanel::BottomLeft ||
×
1176
                                rgb.orientation() == AddRGBPanel::BottomRight)
×
1177
                        {
1178
                                currRow = rows -1;
×
1179
                                rowInc = -1;
1180
                        }
1181
                        if (rgb.orientation() == AddRGBPanel::TopRight ||
×
1182
                                rgb.orientation() == AddRGBPanel::BottomRight)
×
1183
                        {
1184
                                xPosStart = columns - 1;
1185
                                xPosEnd = 0;
1186
                                xPosInc = -1;
1187
                        }
1188
        }
1189

1190
        for (int i = 0; i < rows; i++)
×
1191
        {
1192
            Fixture *fxi = new Fixture(m_doc);
×
1193
            Q_ASSERT(fxi != NULL);
1194
            fxi->setName(tr("%1 - Row %2").arg(rgb.name()).arg(i + 1));
×
1195
            if (rowDef == NULL)
×
1196
                rowDef = fxi->genericRGBPanelDef(columns, components, rgb.is16Bit());
×
1197
            if (rowMode == NULL)
×
1198
                rowMode = fxi->genericRGBPanelMode(rowDef, components, rgb.is16Bit(), phyWidth, phyHeight);
×
1199
            fxi->setFixtureDefinition(rowDef, rowMode);
×
1200

1201
            // Check universe span
1202
            if (address + fxi->channels() > 512)
×
1203
            {
1204
                if (!rgb.crossUniverse())
×
1205
                {
1206
                    uniIndex++;
×
1207
                    address = 0;
1208
                }
1209
            }
1210
            if (m_doc->inputOutputMap()->getUniverseID(uniIndex) == m_doc->inputOutputMap()->invalidUniverse())
×
1211
            {
1212
                m_doc->inputOutputMap()->addUniverse();
×
1213
                m_doc->inputOutputMap()->startUniverses();
×
1214
            }
1215

1216
            fxi->setUniverse(m_doc->inputOutputMap()->getUniverseID(uniIndex));
×
1217
            if (address + fxi->channels() > 512)
×
1218
                fxi->setCrossUniverse(rgb.crossUniverse());
×
1219
            fxi->setAddress(address);
×
1220
            m_doc->addFixture(fxi, Fixture::invalidId(), rgb.crossUniverse());
×
1221

1222
            address += fxi->channels();
×
1223
            if (address >= 512 && rgb.crossUniverse())
×
1224
            {
1225
                address -= 512;
×
1226
                uniIndex++;
×
1227
            }
1228

1229
            if (rgb.type() == AddRGBPanel::ZigZag)
×
1230
            {
1231
                int xPos = xPosStart;
1232
                for (int h = 0; h < fxi->heads(); h++)
×
1233
                {
1234
                        if (transpose)
×
1235
                                grp->assignHead(QLCPoint(currRow, xPos), GroupHead(fxi->id(), h));
×
1236
                        else
1237
                                grp->assignHead(QLCPoint(xPos, currRow), GroupHead(fxi->id(), h));
×
1238
                    xPos += xPosInc;
×
1239
                }
1240
            }
1241
            else if (rgb.type() == AddRGBPanel::Snake)
×
1242
            {
1243
                if (i%2 == 0)
×
1244
                {
1245
                    int xPos = xPosStart;
1246
                    for (int h = 0; h < fxi->heads(); h++)
×
1247
                    {
1248
                            if (transpose)
×
1249
                                    grp->assignHead(QLCPoint(currRow, xPos), GroupHead(fxi->id(), h));
×
1250
                            else
1251
                                    grp->assignHead(QLCPoint(xPos, currRow), GroupHead(fxi->id(), h));
×
1252
                        xPos += xPosInc;
×
1253
                    }
1254
                }
1255
                else
1256
                {
1257
                    int xPos = xPosEnd;
1258
                    for (int h = 0; h < fxi->heads(); h++)
×
1259
                    {
1260
                            if (transpose)
×
1261
                                    grp->assignHead(QLCPoint(currRow, xPos), GroupHead(fxi->id(), h));
×
1262
                            else
1263
                                    grp->assignHead(QLCPoint(xPos, currRow), GroupHead(fxi->id(), h));
×
1264
                        xPos += (-xPosInc);
×
1265
                    }
1266
                }
1267
            }
1268
            currRow += rowInc;
×
1269
        }
1270

1271
        updateView();
×
1272
        m_doc->setModified();
×
1273
    }
1274
}
×
1275

1276
void FixtureManager::removeFixture()
×
1277
{
1278
    // Ask before deletion
1279
    if (QMessageBox::question(this, tr("Delete Fixtures"),
×
1280
                              tr("Do you want to delete the selected items?"),
×
1281
                              QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
1282
    {
1283
        return;
×
1284
    }
1285

1286
    QListIterator <QTreeWidgetItem*> it(m_fixtures_tree->selectedItems());
×
1287

1288
    // We put items to delete in sets,
1289
    // so no segfault happens when the same fixture is selected twice
1290
    QSet <quint32> groupsToDelete;
1291
    QSet <quint32> fixturesToDelete;
1292
    while (it.hasNext() == true)
×
1293
    {
1294
        QTreeWidgetItem* item(it.next());
×
1295
        Q_ASSERT(item != NULL);
1296

1297
        // Is the item a fixture ?
1298
        QVariant var = item->data(KColumnName, PROP_ID);
×
1299
        if (var.isValid() == true)
×
1300
            fixturesToDelete << var.toUInt();
×
1301
        else
1302
        {
1303
            // Is the item a fixture group ?
1304
            var = item->data(KColumnName, PROP_GROUP);
×
1305
            if (var.isValid() == true)
×
1306
                groupsToDelete << var.toUInt();
×
1307
        }
1308
    }
×
1309

1310
    // delete fixture groups
1311
    foreach (quint32 id, groupsToDelete)
×
1312
        m_doc->deleteFixtureGroup(id);
×
1313

1314
    // delete fixtures
1315
    foreach (quint32 id, fixturesToDelete)
×
1316
    {
1317
        /** @todo This is REALLY bogus here, since Fixture or Doc should do
1318
            this. However, FixtureManager is the only place to destroy fixtures,
1319
            so it's rather safe to reset the fixture's address space here. */
1320
        Fixture* fxi = m_doc->fixture(id);
×
1321
        Q_ASSERT(fxi != NULL);
1322
        QList<Universe*> ua = m_doc->inputOutputMap()->claimUniverses();
×
1323
        int universe = fxi->universe();
×
1324
        if (universe < ua.count())
×
1325
            ua[universe]->reset(fxi->address(), fxi->channels());
×
1326
        m_doc->inputOutputMap()->releaseUniverses();
×
1327

1328
        m_doc->deleteFixture(id);
×
1329
    }
×
1330
}
1331

1332
void FixtureManager::removeChannelsGroup()
×
1333
{
1334
    // Ask before deletion
1335
    if (QMessageBox::question(this, tr("Delete Channels Group"),
×
1336
                              tr("Do you want to delete the selected groups?"),
×
1337
                              QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
1338
    {
1339
        return;
×
1340
    }
1341

1342
    disconnect(m_channel_groups_tree, SIGNAL(itemSelectionChanged()),
×
1343
            this, SLOT(slotChannelsGroupSelectionChanged()));
1344

1345
    QListIterator <QTreeWidgetItem*> it(m_channel_groups_tree->selectedItems());
×
1346
    while (it.hasNext() == true)
×
1347
    {
1348
        QTreeWidgetItem* item(it.next());
×
1349
        Q_ASSERT(item != NULL);
1350

1351
        QVariant var = item->data(KColumnName, PROP_ID);
×
1352
        if (var.isValid() == true)
×
1353
            m_doc->deleteChannelsGroup(var.toUInt());
×
1354
    }
×
1355
    updateChannelsGroupView();
×
1356

1357
    connect(m_channel_groups_tree, SIGNAL(itemSelectionChanged()),
×
1358
            this, SLOT(slotChannelsGroupSelectionChanged()));
1359
}
1360

1361
void FixtureManager::slotRemove()
×
1362
{
1363
    if (m_currentTabIndex == 1)
×
1364
        removeChannelsGroup();
×
1365
    else
1366
        removeFixture();
×
1367
}
×
1368

1369
void FixtureManager::editFixtureProperties()
×
1370
{
1371
    QTreeWidgetItem* item = m_fixtures_tree->currentItem();
×
1372
    if (item == NULL)
×
1373
        return;
×
1374

1375
    QVariant var = item->data(KColumnName, PROP_ID);
×
1376
    if (var.isValid() == false)
×
1377
        return;
1378

1379
    quint32 id = var.toUInt();
×
1380
    Fixture* fxi = m_doc->fixture(id);
×
1381
    if (fxi == NULL)
×
1382
        return;
1383

1384
    QString manuf;
1385
    QString model;
1386
    QString mode;
1387

1388
    if (fxi->fixtureDef() != NULL)
×
1389
    {
1390
        manuf = fxi->fixtureDef()->manufacturer();
×
1391
        model = fxi->fixtureDef()->model();
×
1392
        mode = fxi->fixtureMode()->name();
×
1393
    }
1394

1395
    AddFixture af(this, m_doc, fxi);
×
1396
    af.setWindowTitle(tr("Change fixture properties"));
×
1397
    if (af.exec() == QDialog::Accepted)
×
1398
    {
1399
        if (af.invalidAddress() == false)
×
1400
        {
1401
            bool changed = false;
1402

1403
            fxi->blockSignals(true);
×
1404
            if (fxi->name() != af.name())
×
1405
            {
1406
                fxi->setName(af.name());
×
1407
                changed = true;
1408
            }
1409
            if (fxi->universe() != af.universe())
×
1410
            {
1411
                fxi->setUniverse(af.universe());
×
1412
                changed = true;
1413
            }
1414
            if (fxi->address() != af.address())
×
1415
            {
1416
                fxi->setAddress(af.address());
×
1417
                changed = true;
1418
            }
1419
            fxi->blockSignals(false);
×
1420

1421
            if (af.fixtureDef() != NULL && af.mode() != NULL)
×
1422
            {
1423
                if (af.fixtureDef()->manufacturer() == KXMLFixtureGeneric &&
×
1424
                    af.fixtureDef()->model() == KXMLFixtureGeneric)
×
1425
                {
1426
                    if (fxi->channels() != af.channels())
×
1427
                    {
1428
                        QLCFixtureDef* fixtureDef = fxi->genericDimmerDef(af.channels());
×
1429
                        QLCFixtureMode* fixtureMode = fxi->genericDimmerMode(fixtureDef, af.channels());
×
1430
                        fxi->setFixtureDefinition(fixtureDef, fixtureMode);
×
1431
                    }
1432
                }
1433
                else
1434
                {
1435
                    fxi->setFixtureDefinition(af.fixtureDef(), af.mode());
×
1436
                }
1437
            }
1438
            else
1439
            {
1440
                /* Generic dimmer */
1441
                fxi->setFixtureDefinition(NULL, NULL);
×
1442
                fxi->setChannels(af.channels());
×
1443
            }
1444

1445
            // Emit changed signal
1446
            if (changed)
×
1447
                fxi->setID(fxi->id());
×
1448

1449
            updateView();
×
1450
            slotSelectionChanged();
×
1451
        }
1452
        else
1453
        {
1454
            QMessageBox msg(QMessageBox::Critical, tr("Error"),
×
1455
                    tr("Please enter a valid address"), QMessageBox::Ok);
×
1456
            msg.exec();
×
1457
        }
×
1458
    }
1459
}
×
1460

1461
void FixtureManager::editChannelGroupProperties()
×
1462
{
1463
    int selectedCount = m_channel_groups_tree->selectedItems().size();
×
1464

1465
    if (selectedCount > 0)
×
1466
    {
1467
        QTreeWidgetItem* current = m_channel_groups_tree->selectedItems().first();
×
1468
        QVariant var = current->data(KColumnName, PROP_ID);
×
1469
        if (var.isValid() == true)
×
1470
        {
1471
            ChannelsGroup *group = m_doc->channelsGroup(var.toUInt());
×
1472

1473
            AddChannelsGroup cs(this, m_doc, group);
×
1474
            if (cs.exec() == QDialog::Accepted)
×
1475
            {
1476
                qDebug() << "CHANNEL GROUP MODIFIED. Count: " << group->getChannels().count();
1477
                m_doc->addChannelsGroup(group, group->id());
×
1478
                updateChannelsGroupView();
×
1479
            }
1480
        }
×
1481
    }
×
1482
}
×
1483

1484
int FixtureManager::headCount(const QList <QTreeWidgetItem*>& items) const
×
1485
{
1486
    int count = 0;
1487
    QListIterator <QTreeWidgetItem*> it(items);
×
1488
    while (it.hasNext() == true)
×
1489
    {
1490
        QTreeWidgetItem* item = it.next();
×
1491
        Q_ASSERT(item != NULL);
1492

1493
        QVariant var = item->data(KColumnName, PROP_ID);
×
1494
        if (var.isValid() == false)
×
1495
            continue;
1496

1497
        Fixture* fxi = m_doc->fixture(var.toUInt());
×
1498
        count += fxi->heads();
×
1499
    }
×
1500

1501
    return count;
×
1502
}
1503

1504
void FixtureManager::slotProperties()
×
1505
{
1506
    if (m_currentTabIndex == 1)
×
1507
        editChannelGroupProperties();
×
1508
    else
1509
        editFixtureProperties();
×
1510
}
×
1511

1512
void FixtureManager::slotFadeConfig()
×
1513
{
1514
    ChannelsSelection cfg(m_doc, this, ChannelsSelection::ConfigurationMode);
×
1515
    if (cfg.exec() == QDialog::Rejected)
×
1516
        return; // User pressed cancel
1517
    m_doc->setModified();
×
1518
}
×
1519

1520
void FixtureManager::slotRemap()
×
1521
{
1522
    FixtureRemap fxr(m_doc);
×
1523
    if (fxr.exec() == QDialog::Rejected)
×
1524
        return; // User pressed cancel
1525

1526
    updateView();
×
1527
}
×
1528

1529
void FixtureManager::slotUnGroup()
×
1530
{
1531
    if (QMessageBox::question(this, tr("Ungroup fixtures?"),
×
1532
                              tr("Do you want to ungroup the selected fixtures?"),
×
1533
                              QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
1534
    {
1535
        return;
×
1536
    }
1537

1538
    // Because FixtureGroup::resignFixture() emits changed(), which makes the tree
1539
    // update its contents in the middle, invalidating m_tree->selectedItems(),
1540
    // we must pick the list of fixtures and groups first and then resign them in
1541
    // one big bunch.
1542
    QList <QPair<quint32,quint32> > resignList;
1543

1544
    foreach (QTreeWidgetItem* item, m_fixtures_tree->selectedItems())
×
1545
    {
1546
        if (item->parent() == NULL)
×
1547
            continue;
×
1548

1549
        QVariant var = item->parent()->data(KColumnName, PROP_GROUP);
×
1550
        if (var.isValid() == false)
×
1551
            continue;
×
1552
        quint32 grp = var.toUInt();
×
1553

1554
        var = item->data(KColumnName, PROP_ID);
×
1555
        if (var.isValid() == false)
×
1556
            continue;
×
1557
        quint32 fxi = var.toUInt();
×
1558

1559
        resignList << QPair <quint32,quint32> (grp, fxi);
×
1560
    }
×
1561

1562
    QListIterator <QPair<quint32,quint32> > it(resignList);
×
1563
    while (it.hasNext() == true)
×
1564
    {
1565
        QPair <quint32,quint32> pair(it.next());
×
1566
        FixtureGroup* grp = m_doc->fixtureGroup(pair.first);
×
1567
        Q_ASSERT(grp != NULL);
1568
        grp->resignFixture(pair.second);
×
1569
    }
1570
}
×
1571

1572
void FixtureManager::slotGroupSelected(QAction* action)
×
1573
{
1574
    FixtureGroup* grp = NULL;
1575

1576
    if (action->data().isValid() == true)
×
1577
    {
1578
        // Existing group selected
1579
        grp = (FixtureGroup*) (action->data().toULongLong());
×
1580
        Q_ASSERT(grp != NULL);
1581
    }
1582
    else
1583
    {
1584
        // New Group selected.
1585

1586
        // Suggest an equilateral grid
1587
        qreal side = sqrt(headCount(m_fixtures_tree->selectedItems()));
×
1588
        if (side != floor(side))
×
1589
            side += 1; // Fixture number doesn't provide a full square
×
1590

1591
        CreateFixtureGroup cfg(this);
×
1592
        cfg.setSize(QSize(side, side));
×
1593
        if (cfg.exec() != QDialog::Accepted)
×
1594
            return; // User pressed cancel
1595

1596
        grp = new FixtureGroup(m_doc);
×
1597
        Q_ASSERT(grp != NULL);
1598
        grp->setName(cfg.name());
×
1599
        grp->setSize(cfg.size());
×
1600
        m_doc->addFixtureGroup(grp);
×
1601
        updateGroupMenu();
×
1602
    }
×
1603

1604
    // Assign selected fixture items to the group
1605
    foreach (QTreeWidgetItem* item, m_fixtures_tree->selectedItems())
×
1606
    {
1607
        QVariant var = item->data(KColumnName, PROP_ID);
×
1608
        if (var.isValid() == false)
×
1609
            continue;
1610

1611
        grp->assignFixture(var.toUInt());
×
1612
    }
×
1613

1614
    updateView();
×
1615
}
1616

1617
void FixtureManager::slotMoveGroupUp()
×
1618
{
1619
    if (m_channel_groups_tree->selectedItems().size() > 0)
×
1620
    {
1621
        QTreeWidgetItem* item = m_channel_groups_tree->selectedItems().first();
×
1622
        quint32 grpID = item->data(KColumnName, PROP_ID).toUInt();
×
1623
        m_doc->moveChannelGroup(grpID, -1);
×
1624
        updateChannelsGroupView();
×
1625
    }
1626
}
×
1627

1628
void FixtureManager::slotMoveGroupDown()
×
1629
{
1630
    if (m_channel_groups_tree->selectedItems().size() > 0)
×
1631
    {
1632
        QTreeWidgetItem* item = m_channel_groups_tree->selectedItems().first();
×
1633
        quint32 grpID = item->data(KColumnName, PROP_ID).toUInt();
×
1634
        m_doc->moveChannelGroup(grpID, 1);
×
1635
        updateChannelsGroupView();
×
1636
    }
1637
}
×
1638

1639
QString FixtureManager::createDialog(bool import)
×
1640
{
1641
    QString fileName;
1642

1643
    /* Create a file save dialog */
1644
    QFileDialog dialog(this);
×
1645
    if (import == true)
×
1646
    {
1647
        dialog.setWindowTitle(tr("Import Fixtures List"));
×
1648
        dialog.setAcceptMode(QFileDialog::AcceptOpen);
×
1649
    }
1650
    else
1651
    {
1652
        dialog.setWindowTitle(tr("Export Fixtures List As"));
×
1653
        dialog.setAcceptMode(QFileDialog::AcceptSave);
×
1654
    }
1655

1656
    /* Append file filters to the dialog */
1657
    QStringList filters;
1658
    filters << tr("Fixtures List (*%1)").arg(KExtFixtureList);
×
1659
#if defined(WIN32) || defined(Q_OS_WIN)
1660
    filters << tr("All Files (*.*)");
1661
#else
1662
    filters << tr("All Files (*)");
×
1663
#endif
1664
    dialog.setNameFilters(filters);
×
1665

1666
    /* Append useful URLs to the dialog */
1667
    QList <QUrl> sidebar;
1668
    sidebar.append(QUrl::fromLocalFile(QDir::homePath()));
×
1669
    sidebar.append(QUrl::fromLocalFile(QDir::rootPath()));
×
1670
    dialog.setSidebarUrls(sidebar);
×
1671

1672
    /* Get file name */
1673
    if (dialog.exec() != QDialog::Accepted)
×
1674
        return "";
×
1675

1676
    fileName = dialog.selectedFiles().first();
×
1677
    if (fileName.isEmpty() == true)
×
1678
        return "";
×
1679

1680
    /* Always use the fixture definition suffix */
1681
    if (import == false && fileName.right(5) != KExtFixtureList)
×
1682
        fileName += KExtFixtureList;
×
1683

1684
    return fileName;
1685
}
×
1686

1687
void FixtureManager::slotImport()
×
1688
{
1689
    QString fileName = createDialog(true);
×
1690

1691
    QXmlStreamReader *doc = QLCFile::getXMLReader(fileName);
×
1692
    if (doc == NULL || doc->device() == NULL || doc->hasError())
×
1693
    {
1694
        qWarning() << Q_FUNC_INFO << "Unable to read from" << fileName;
×
1695
        return;
×
1696
    }
1697

1698
    while (!doc->atEnd())
×
1699
    {
1700
        if (doc->readNext() == QXmlStreamReader::DTD)
×
1701
            break;
1702
    }
1703
    if (doc->hasError())
×
1704
    {
1705
        QLCFile::releaseXMLReader(doc);
×
1706
        return;
1707
    }
1708

1709
    if (doc->dtdName() == KXMLQLCFixturesList)
×
1710
    {
1711
        doc->readNextStartElement();
×
1712
        if (doc->name() != KXMLQLCFixturesList)
×
1713
        {
1714
            qWarning() << Q_FUNC_INFO << "Fixture Definition node not found";
×
1715
            QLCFile::releaseXMLReader(doc);
×
1716
            return;
1717
        }
1718

1719
        while (doc->readNextStartElement())
×
1720
        {
1721
            if (doc->name() == KXMLFixture)
×
1722
            {
1723
                Fixture* fxi = new Fixture(m_doc);
×
1724
                Q_ASSERT(fxi != NULL);
1725

1726
                if (fxi->loadXML(*doc, m_doc, m_doc->fixtureDefCache()) == true)
×
1727
                {
1728
                    if (m_doc->addFixture(fxi /*, fxi->id()*/) == true)
×
1729
                    {
1730
                        /* Success */
1731
                        qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name() << "successfully created.";
×
1732
                    }
1733
                    else
1734
                    {
1735
                        /* Doc is full */
1736
                        qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name() << "cannot be created.";
×
1737
                        delete fxi;
×
1738
                    }
1739
                }
1740
                else
1741
                {
1742
                    qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name() << "cannot be loaded.";
×
1743
                    delete fxi;
×
1744
                }
1745
            }
1746
            else if (doc->name() == KXMLQLCFixtureGroup)
×
1747
            {
1748
                FixtureGroup* grp = new FixtureGroup(m_doc);
×
1749
                Q_ASSERT(grp != NULL);
1750

1751
                if (grp->loadXML(*doc) == true)
×
1752
                {
1753
                    m_doc->addFixtureGroup(grp, grp->id());
×
1754
                }
1755
                else
1756
                {
1757
                    qWarning() << Q_FUNC_INFO << "FixtureGroup" << grp->name() << "cannot be loaded.";
×
1758
                    delete grp;
×
1759
                }
1760
            }
1761
            else
1762
            {
1763
                qWarning() << Q_FUNC_INFO << "Unknown label tag:" << doc->name().toString();
×
1764
                doc->skipCurrentElement();
×
1765
            }
1766
        }
1767
        updateView();
×
1768
    }
1769
    QLCFile::releaseXMLReader(doc);
×
1770
}
×
1771

1772
void FixtureManager::slotExport()
×
1773
{
1774
    QString fileName = createDialog(false);
×
1775

1776
    QFile file(fileName);
×
1777
    if (file.open(QIODevice::WriteOnly) == false)
×
1778
        return;
1779

1780
    QXmlStreamWriter doc(&file);
×
1781
    doc.setAutoFormatting(true);
×
1782
    doc.setAutoFormattingIndent(1);
×
1783
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1784
    doc.setCodec("UTF-8");
×
1785
#endif
1786
    QLCFile::writeXMLHeader(&doc, KXMLQLCFixturesList);
×
1787

1788
    QListIterator <Fixture*> fxit(m_doc->fixtures());
×
1789
    while (fxit.hasNext() == true)
×
1790
    {
1791
        Fixture* fxi(fxit.next());
×
1792
        Q_ASSERT(fxi != NULL);
1793
        fxi->saveXML(&doc);
×
1794
    }
1795

1796
    QListIterator <FixtureGroup*>grpit(m_doc->fixtureGroups());
×
1797
    while (grpit.hasNext() == true)
×
1798
    {
1799
        FixtureGroup *fxgrp(grpit.next());
×
1800
        Q_ASSERT(fxgrp != NULL);
1801
        fxgrp->saveXML(&doc);
×
1802
    }
1803

1804
    doc.writeEndDocument();
×
1805
    file.close();
×
1806
}
×
1807

1808
void FixtureManager::slotContextMenuRequested(const QPoint&)
×
1809
{
1810
    QMenu menu(this);
×
1811
    menu.addAction(m_addAction);
×
1812
    menu.addAction(m_addRGBAction);
×
1813
    menu.addAction(m_propertiesAction);
×
1814
    menu.addAction(m_removeAction);
×
1815
    menu.addSeparator();
×
1816
    menu.addAction(m_groupAction);
×
1817
    menu.addAction(m_unGroupAction);
×
1818
    menu.exec(QCursor::pos());
×
1819
}
×
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