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

mcallegari / qlcplus / 21113004754

18 Jan 2026 02:05PM UTC coverage: 33.998% (-0.2%) from 34.162%
21113004754

Pull #1935

github

web-flow
Merge 447abfe67 into 3d91c2214
Pull Request #1935: engine,ui: move info page creation from engine to ui

2 of 121 new or added lines in 2 files covered. (1.65%)

1 existing line in 1 file now uncovered.

17635 of 51870 relevant lines covered (34.0%)

19812.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
FixtureManager* FixtureManager::s_instance = NULL;
68

69
/*****************************************************************************
70
 * Initialization
71
 *****************************************************************************/
72

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

101
    Q_ASSERT(doc != NULL);
×
102

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

107
    initActions();
×
108
    initToolBar();
×
109
    initDataView();
×
110
    updateView();
×
111
    updateChannelsGroupView();
×
112

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

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

121
    connect(m_doc, SIGNAL(channelsGroupRemoved(quint32)),
×
122
            this, SLOT(slotChannelsGroupRemoved(quint32)));
123

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

127
    connect(m_doc, SIGNAL(fixtureGroupRemoved(quint32)),
×
128
            this, SLOT(slotFixtureGroupRemoved(quint32)));
129

130
    connect(m_doc, SIGNAL(fixtureGroupChanged(quint32)),
×
131
            this, SLOT(slotFixtureGroupChanged(quint32)));
132

133
    connect(m_doc, SIGNAL(loaded()),
×
134
            this, SLOT(slotDocLoaded()));
135

136
    slotModeChanged(m_doc->mode());
×
137

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

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

152
    s_instance = NULL;
×
153
}
×
154

155
FixtureManager* FixtureManager::instance()
×
156
{
157
    return s_instance;
×
158
}
159

160
/*****************************************************************************
161
 * Doc signal handlers
162
 *****************************************************************************/
163

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

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

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

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

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

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

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

297
    updateGroupMenu();
×
298
}
×
299

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

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

312
void FixtureManager::slotDocLoaded()
×
313
{
314
    slotTabChanged(m_currentTabIndex);
×
315
}
×
316

317
/*****************************************************************************
318
 * Data view
319
 *****************************************************************************/
320

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

329
    QTabWidget *tabs = new QTabWidget(this);
×
330
    m_splitter->addWidget(tabs);
×
331

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

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

343
    connect(m_fixtures_tree, SIGNAL(itemSelectionChanged()),
×
344
            this, SLOT(slotSelectionChanged()));
345

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

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

352
    connect(m_fixtures_tree, SIGNAL(expanded(QModelIndex)),
×
353
            this, SLOT(slotFixtureItemExpanded()));
354

355
    connect(m_fixtures_tree, SIGNAL(collapsed(QModelIndex)),
×
356
            this, SLOT(slotFixtureItemExpanded()));
357

358
    tabs->addTab(m_fixtures_tree, tr("Fixture Groups"));
×
359

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

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

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

383
    /* Create the text view */
384
    createInfo();
×
385

386
    slotSelectionChanged();
×
387
}
×
388

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

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

417
    m_fixtures_tree->updateTree();
×
418

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

431
    updateGroupMenu();
×
432
    slotModeChanged(m_doc->mode());
×
433

434
    m_fixtures_tree->header()->resizeSections(QHeaderView::ResizeToContents);
×
435
}
×
436

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

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

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

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

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

480
    m_channel_groups_tree->header()->resizeSections(QHeaderView::ResizeToContents);
×
481
}
×
482

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

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

501
    if (m_info == NULL)
×
502
        createInfo();
×
503

504
    m_info->setText(QString("%1<BODY>%2</BODY></HTML>")
×
505
                    .arg(fixtureInfoStyleSheetHeader())
×
NEW
506
                    .arg(fixtureInfo(fxi)));
×
507

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

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

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

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

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

531
    m_splitter->restoreState(state);
×
532
}
×
533

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

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

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

550
    m_info = new QTextBrowser(this);
×
551
    m_splitter->addWidget(m_info);
×
552

553
    m_splitter->restoreState(state);
×
554
}
×
555

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

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

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

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

598
            if (m_info == NULL)
×
599
                createInfo();
×
600

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

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

609
            info += "</BODY></HTML>";
×
610

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

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

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

636
                    Fixture *fixture = m_doc->fixture(fxID.toUInt());
×
637

638
                    if (fixture == NULL || fixture->fixtureMode() == NULL)
×
639
                        continue;
×
640

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

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

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

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

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

689
    int selectedCount = m_channel_groups_tree->selectedItems().size();
×
690

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

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

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

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

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

769
    m_currentTabIndex = index;
×
770
}
×
771

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

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

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

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

800
QString FixtureManager::fixtureInfoStyleSheetHeader()
×
801
{
802
    QString info;
×
803

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

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

NEW
836
QString FixtureManager::fixtureInfo(const Fixture *fixture) const
×
837
{
NEW
838
    QString info;
×
839

NEW
840
    QString title("<TR><TD CLASS='hilite' COLSPAN='3'>%1</TD></TR>");
×
NEW
841
    QString subTitle("<TR><TD CLASS='subhi' COLSPAN='3'>%1</TD></TR>");
×
NEW
842
    QString genInfo("<TR><TD CLASS='emphasis'>%1</TD><TD COLSPAN='2'>%2</TD></TR>");
×
843

844
    /********************************************************************
845
     * General info
846
     ********************************************************************/
847

NEW
848
    info += "<TABLE COLS='3' WIDTH='100%'>";
×
849

NEW
850
    if (fixture == NULL)
×
851
    {
NEW
852
        info += "</TABLE>";
×
NEW
853
        return info;
×
854
    }
855

856
    // Fixture title
NEW
857
    info += title.arg(fixture->name());
×
858

NEW
859
    const QLCFixtureDef* fixtureDef = fixture->fixtureDef();
×
NEW
860
    const QLCFixtureMode* fixtureMode = fixture->fixtureMode();
×
861

NEW
862
    if (fixtureDef != NULL && fixtureMode != NULL)
×
863
    {
864
        // Manufacturer
NEW
865
        info += genInfo.arg(tr("Manufacturer")).arg(fixtureDef->manufacturer());
×
NEW
866
        info += genInfo.arg(tr("Model")).arg(fixtureDef->model());
×
NEW
867
        info += genInfo.arg(tr("Mode")).arg(fixtureMode->name());
×
NEW
868
        info += genInfo.arg(tr("Type")).arg(fixtureDef->typeToString(fixtureDef->type()));
×
869
    }
870

871
    // Universe
NEW
872
    info += genInfo.arg(tr("Universe")).arg(fixture->universe() + 1);
×
873

874
    // Address
NEW
875
    QString range = QString("%1 - %2").arg(fixture->address() + 1).arg(fixture->address() + fixture->channels());
×
NEW
876
    info += genInfo.arg(tr("Address Range")).arg(range);
×
877

878
    // Channels
NEW
879
    info += genInfo.arg(tr("Channels")).arg(fixture->channels());
×
880

881
    // Binary address
NEW
882
    QString binaryStr = QString("%1").arg(fixture->address() + 1, 10, 2, QChar('0'));
×
NEW
883
    QString dipTable("<TABLE COLS='33' cellspacing='0'><TR><TD COLSPAN='33'><IMG SRC=\"" ":/ds_top.png\"></TD></TR>");
×
NEW
884
    dipTable += "<TR><TD><IMG SRC=\"" ":/ds_border.png\"></TD><TD><IMG SRC=\"" ":/ds_border.png\"></TD>";
×
NEW
885
    for (int i = 9; i >= 0; i--)
×
886
    {
NEW
887
        if (binaryStr.at(i) == '0')
×
NEW
888
            dipTable += "<TD COLSPAN='3'><IMG SRC=\"" ":/ds_off.png\"></TD>";
×
889
        else
NEW
890
            dipTable += "<TD COLSPAN='3'><IMG SRC=\"" ":/ds_on.png\"></TD>";
×
891
    }
NEW
892
    dipTable += "<TD><IMG SRC=\"" ":/ds_border.png\"></TD></TR>";
×
NEW
893
    dipTable += "<TR><TD COLSPAN='33'><IMG SRC=\"" ":/ds_bottom.png\"></TD></TR>";
×
NEW
894
    dipTable += "</TABLE>";
×
895

NEW
896
    info += genInfo.arg(tr("Binary Address (DIP)"))
×
NEW
897
            .arg(QString("%1").arg(dipTable));
×
898

899
    /********************************************************************
900
     * Channels
901
     ********************************************************************/
902

903
    // Title row
NEW
904
    info += QString("<TR><TD CLASS='subhi'>%1</TD>").arg(tr("Channel"));
×
NEW
905
    info += QString("<TD CLASS='subhi'>%1</TD>").arg(tr("DMX"));
×
NEW
906
    info += QString("<TD CLASS='subhi'>%1</TD></TR>").arg(tr("Name"));
×
907

908
    // Fill table with the fixture's channels
NEW
909
    for (quint32 ch = 0; ch < fixture->channels(); ch++)
×
910
    {
NEW
911
        QString chInfo("<TR><TD>%1</TD><TD>%2</TD><TD>%3</TD></TR>");
×
NEW
912
        info += chInfo.arg(ch + 1).arg(fixture->address() + ch + 1)
×
NEW
913
                .arg(fixture->channel(ch)->name());
×
NEW
914
    }
×
915

916
    /********************************************************************
917
     * Extended device information
918
     ********************************************************************/
919

NEW
920
    if (fixtureMode != NULL)
×
921
    {
NEW
922
        const QLCPhysical physical = fixtureMode->physical();
×
NEW
923
        info += title.arg(tr("Physical"));
×
924

NEW
925
        float mmInch = 0.0393700787;
×
NEW
926
        float kgLbs = 2.20462262;
×
NEW
927
        QString mm("%1mm (%2\")");
×
NEW
928
        QString kg("%1kg (%2 lbs)");
×
NEW
929
        QString W("%1W");
×
NEW
930
        info += genInfo.arg(tr("Width")).arg(mm.arg(physical.width()))
×
NEW
931
                                        .arg(physical.width() * mmInch, 0, 'g', 4);
×
NEW
932
        info += genInfo.arg(tr("Height")).arg(mm.arg(physical.height()))
×
NEW
933
                                         .arg(physical.height() * mmInch, 0, 'g', 4);
×
NEW
934
        info += genInfo.arg(tr("Depth")).arg(mm.arg(physical.depth()))
×
NEW
935
                                        .arg(physical.depth() * mmInch, 0, 'g', 4);
×
NEW
936
        info += genInfo.arg(tr("Weight")).arg(kg.arg(physical.weight()))
×
NEW
937
                                         .arg(physical.weight() * kgLbs, 0, 'g', 4);
×
NEW
938
        info += genInfo.arg(tr("Power consumption")).arg(W.arg(physical.powerConsumption()));
×
NEW
939
        info += genInfo.arg(tr("DMX Connector")).arg(physical.dmxConnector());
×
940

941
        // Bulb
NEW
942
        QString K("%1K");
×
NEW
943
        QString lm("%1lm");
×
NEW
944
        info += subTitle.arg(tr("Bulb"));
×
NEW
945
        info += genInfo.arg(tr("Type")).arg(physical.bulbType());
×
NEW
946
        info += genInfo.arg(tr("Luminous Flux")).arg(lm.arg(physical.bulbLumens()));
×
NEW
947
        info += genInfo.arg(tr("Colour Temperature")).arg(K.arg(physical.bulbColourTemperature()));
×
948

949
        // Lens
NEW
950
        QString angle1("%1&deg;");
×
NEW
951
        QString angle2("%1&deg; &ndash; %2&deg;");
×
952

NEW
953
        info += subTitle.arg(tr("Lens"));
×
NEW
954
        info += genInfo.arg(tr("Name")).arg(physical.lensName());
×
955

NEW
956
        if (physical.lensDegreesMin() == physical.lensDegreesMax())
×
957
        {
NEW
958
            info += genInfo.arg(tr("Beam Angle"))
×
NEW
959
                .arg(angle1.arg(physical.lensDegreesMin()));
×
960
        }
961
        else
962
        {
NEW
963
            info += genInfo.arg(tr("Beam Angle"))
×
NEW
964
                .arg(angle2.arg(physical.lensDegreesMin())
×
NEW
965
                .arg(physical.lensDegreesMax()));
×
966
        }
967

968
        // Focus
NEW
969
        QString frange("%1&deg;");
×
NEW
970
        info += subTitle.arg(tr("Head(s)"));
×
NEW
971
        info += genInfo.arg(tr("Type")).arg(physical.focusType());
×
NEW
972
        info += genInfo.arg(tr("Pan Range")).arg(frange.arg(physical.focusPanMax()));
×
NEW
973
        info += genInfo.arg(tr("Tilt Range")).arg(frange.arg(physical.focusTiltMax()));
×
NEW
974
        if (physical.layoutSize() != QSize(1, 1))
×
975
        {
NEW
976
            info += genInfo.arg(tr("Layout"))
×
NEW
977
                           .arg(QString("%1 x %2").arg(physical.layoutSize().width()).arg(physical.layoutSize().height()));
×
978
        }
NEW
979
    }
×
980

981
    // HTML document & table closure
NEW
982
    info += "</TABLE>";
×
983

NEW
984
    if (fixtureDef != NULL)
×
985
    {
NEW
986
        info += "<HR>";
×
NEW
987
        info += "<DIV CLASS='author' ALIGN='right'>";
×
NEW
988
        info += tr("Fixture definition author: ") + fixtureDef->author();
×
NEW
989
        info += "</DIV>";
×
990
    }
991

NEW
992
    return info;
×
NEW
993
}
×
994

UNCOV
995
QString FixtureManager::channelsGroupInfoStyleSheetHeader()
×
996
{
997
    QString info;
×
998

999
    QPalette pal;
×
1000
    QColor hlBack(pal.color(QPalette::Highlight));
×
1001
    QColor hlBackSmall(pal.color(QPalette::Shadow));
×
1002
    QColor hlText(pal.color(QPalette::HighlightedText));
×
1003

1004
    info += "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">";
×
1005
    info += "<HTML><HEAD></HEAD><STYLE>";
×
1006
    info += QString(".hilite {" \
×
1007
                    "        background-color: %1;" \
1008
                    "        color: %2;" \
1009
                    "        font-size: x-large;" \
1010
                    "}").arg(hlBack.name()).arg(hlText.name());
×
1011
    info += QString(".subhi {" \
×
1012
                    "        background-color: %1;" \
1013
                    "        color: %2;" \
1014
                    "        font-weight: bold;" \
1015
                    "}").arg(hlBackSmall.name()).arg(hlText.name());
×
1016
    info += QString(".emphasis {" \
×
1017
                    "        font-weight: bold;" \
1018
                    "}");
×
1019
    info += QString(".tiny {"\
×
1020
                    "   font-size: small;" \
1021
                    "}");
×
1022
    info += "</STYLE>";
×
1023
    return info;
×
1024
}
×
1025

NEW
1026
QString FixtureManager::channelsGroupInfo(const ChannelsGroup *channelsGroup) const
×
1027
{
NEW
1028
    QString info;
×
1029

NEW
1030
    info += "<TABLE COLS='3' WIDTH='100%'>";
×
1031

NEW
1032
    if (channelsGroup != NULL)
×
1033
    {
1034
        // Fixture title
NEW
1035
        const QString title("<TR><TD CLASS='hilite' COLSPAN='3'><CENTER>%1</CENTER></TD></TR>");
×
NEW
1036
        info += title.arg(channelsGroup->name());
×
1037

1038
        /********************************************************************
1039
         * Channels
1040
         ********************************************************************/
1041

1042
        // Title row
NEW
1043
        info += QString("<TR><TD CLASS='subhi'>%1</TD>").arg(tr("Fixture"));
×
NEW
1044
        info += QString("<TD CLASS='subhi'>%1</TD>").arg(tr("Channel"));
×
NEW
1045
        info += QString("<TD CLASS='subhi'>%1</TD></TR>").arg(tr("Description"));
×
1046

NEW
1047
        foreach (const SceneValue value, channelsGroup->getChannels())
×
1048
        {
NEW
1049
            const Fixture *fixture = m_doc->fixture(value.fxi);
×
NEW
1050
            if (fixture == NULL)
×
NEW
1051
                continue;
×
1052

NEW
1053
            const QLCFixtureMode *mode = fixture->fixtureMode();
×
NEW
1054
            const QString chInfo("<TR><TD>%1</TD><TD>%2</TD><TD>%3</TD></TR>");
×
NEW
1055
            if (mode != NULL)
×
1056
            {
NEW
1057
                info += chInfo.arg(fixture->name()).arg(value.channel + 1)
×
NEW
1058
                    .arg(mode->channels().at(value.channel)->name());
×
1059
            }
1060
            else
1061
            {
NEW
1062
                info += chInfo.arg(fixture->name()).arg(value.channel + 1)
×
NEW
1063
                    .arg(QString(tr("Channel %1")).arg(value.channel));
×
1064
            }
NEW
1065
        }
×
NEW
1066
    }
×
1067

1068
    // HTML document & table closure
NEW
1069
    info += "</TABLE>";
×
1070

NEW
1071
    return info;
×
NEW
1072
}
×
1073

1074
/*****************************************************************************
1075
 * Menu, toolbar and actions
1076
 *****************************************************************************/
1077

1078
void FixtureManager::initActions()
×
1079
{
1080
    // Fixture actions
1081
    m_addAction = new QAction(QIcon(":/edit_add.png"),
×
1082
                              tr("Add fixture..."), this);
×
1083
    connect(m_addAction, SIGNAL(triggered(bool)),
×
1084
            this, SLOT(slotAdd()));
1085

1086
    m_addRGBAction = new QAction(QIcon(":/rgbpanel.png"),
×
1087
                              tr("Add RGB panel..."), this);
×
1088
    connect(m_addRGBAction, SIGNAL(triggered(bool)),
×
1089
            this, SLOT(slotAddRGBPanel()));
1090

1091
    m_removeAction = new QAction(QIcon(":/edit_remove.png"),
×
1092
                                 tr("Delete items"), this);
×
1093
    connect(m_removeAction, SIGNAL(triggered(bool)),
×
1094
            this, SLOT(slotRemove()));
1095

1096
    m_propertiesAction = new QAction(QIcon(":/configure.png"),
×
1097
                                     tr("Properties..."), this);
×
1098
    connect(m_propertiesAction, SIGNAL(triggered(bool)),
×
1099
            this, SLOT(slotProperties()));
1100

1101
    m_fadeConfigAction = new QAction(QIcon(":/fade.png"),
×
1102
                                     tr("Channels Fade Configuration..."), this);
×
1103
    connect(m_fadeConfigAction, SIGNAL(triggered(bool)),
×
1104
            this, SLOT(slotFadeConfig()));
1105

1106
    // Group actions
1107
    m_groupAction = new QAction(QIcon(":/group.png"),
×
1108
                                tr("Add fixture to group..."), this);
×
1109

1110
    m_unGroupAction = new QAction(QIcon(":/ungroup.png"),
×
1111
                                tr("Remove fixture from group"), this);
×
1112
    connect(m_unGroupAction, SIGNAL(triggered(bool)),
×
1113
            this, SLOT(slotUnGroup()));
1114

1115
    m_newGroupAction = new QAction(tr("New Group..."), this);
×
1116

1117
    m_moveUpAction = new QAction(QIcon(":/up.png"),
×
1118
                                 tr("Move channel group up..."), this);
×
1119
    m_moveUpAction->setEnabled(false);
×
1120
    connect(m_moveUpAction, SIGNAL(triggered(bool)),
×
1121
            this, SLOT(slotMoveGroupUp()));
1122

1123
    m_moveDownAction = new QAction(QIcon(":/down.png"),
×
1124
                                 tr("Move channel group down..."), this);
×
1125
    m_moveDownAction->setEnabled(false);
×
1126
    connect(m_moveDownAction, SIGNAL(triggered(bool)),
×
1127
            this, SLOT(slotMoveGroupDown()));
1128

1129
    m_importAction = new QAction(QIcon(":/fileimport.png"),
×
1130
                                 tr("Import fixtures..."), this);
×
1131
    connect(m_importAction, SIGNAL(triggered(bool)),
×
1132
            this, SLOT(slotImport()));
1133

1134
    m_exportAction = new QAction(QIcon(":/fileexport.png"),
×
1135
                                 tr("Export fixtures..."), this);
×
1136

1137
    connect(m_exportAction, SIGNAL(triggered(bool)),
×
1138
            this, SLOT(slotExport()));
1139

1140
    m_remapAction = new QAction(QIcon(":/remap.png"),
×
1141
                               tr("Remap fixtures..."), this);
×
1142
    connect(m_remapAction, SIGNAL(triggered(bool)),
×
1143
            this, SLOT(slotRemap()));
1144
}
×
1145

1146
void FixtureManager::updateGroupMenu()
×
1147
{
1148
    if (m_groupMenu == NULL)
×
1149
    {
1150
        m_groupMenu = new QMenu(this);
×
1151
        connect(m_groupMenu, SIGNAL(triggered(QAction*)),
×
1152
                this, SLOT(slotGroupSelected(QAction*)));
1153
    }
1154

1155
    foreach (QAction* a, m_groupMenu->actions())
×
1156
        m_groupMenu->removeAction(a);
×
1157

1158
    // Put all known fixture groups to the menu
1159
    foreach (FixtureGroup* grp, m_doc->fixtureGroups())
×
1160
    {
1161
        QAction* a = m_groupMenu->addAction(grp->name());
×
1162
        a->setData((qulonglong) grp);
×
1163
    }
×
1164

1165
    // Put a new group action to the group menu
1166
    m_groupMenu->addAction(m_newGroupAction);
×
1167

1168
    // Put the group menu to the group action
1169
    m_groupAction->setMenu(m_groupMenu);
×
1170
}
×
1171

1172
void FixtureManager::initToolBar()
×
1173
{
1174
    QToolBar* toolbar = new QToolBar(tr("Fixture manager"), this);
×
1175
    toolbar->setFloatable(false);
×
1176
    toolbar->setMovable(false);
×
1177
    layout()->setMenuBar(toolbar);
×
1178
    toolbar->addAction(m_addAction);
×
1179
    toolbar->addAction(m_addRGBAction);
×
1180
    toolbar->addAction(m_removeAction);
×
1181
    toolbar->addAction(m_propertiesAction);
×
1182
    toolbar->addAction(m_fadeConfigAction);
×
1183
    toolbar->addSeparator();
×
1184
    toolbar->addAction(m_groupAction);
×
1185
    toolbar->addAction(m_unGroupAction);
×
1186
    toolbar->addSeparator();
×
1187
    toolbar->addAction(m_moveUpAction);
×
1188
    toolbar->addAction(m_moveDownAction);
×
1189
    toolbar->addSeparator();
×
1190
    toolbar->addAction(m_importAction);
×
1191
    toolbar->addAction(m_exportAction);
×
1192
    toolbar->addAction(m_remapAction);
×
1193

1194
    QToolButton* btn = qobject_cast<QToolButton*> (toolbar->widgetForAction(m_groupAction));
×
1195
    Q_ASSERT(btn != NULL);
×
1196
    btn->setPopupMode(QToolButton::InstantPopup);
×
1197
}
×
1198

1199
void FixtureManager::addFixture()
×
1200
{
1201
    AddFixture af(this, m_doc);
×
1202
    if (af.exec() == QDialog::Rejected)
×
1203
        return;
×
1204

1205
    if (af.invalidAddress())
×
1206
    {
1207
        QMessageBox msg(QMessageBox::Critical, tr("Error"),
×
1208
                tr("Please enter a valid address"), QMessageBox::Ok);
×
1209
        msg.exec();
×
1210
        return;
×
1211
    }
×
1212

1213
    quint32 latestFxi = Fixture::invalidId();
×
1214

1215
    QString name = af.name();
×
1216
    quint32 address = af.address();
×
1217
    quint32 universe = af.universe();
×
1218
    quint32 channels = af.channels();
×
1219
    int gap = af.gap();
×
1220

1221
    QLCFixtureDef* fixtureDef = af.fixtureDef();
×
1222
    QLCFixtureMode* mode = af.mode();
×
1223

1224
    FixtureGroup* addToGroup = NULL;
×
1225
    QTreeWidgetItem* current = m_fixtures_tree->currentItem();
×
1226
    if (current != NULL)
×
1227
    {
1228
        if (current->parent() != NULL)
×
1229
        {
1230
            // Fixture selected
1231
            QVariant var = current->parent()->data(KColumnName, PROP_GROUP);
×
1232
            if (var.isValid() == true)
×
1233
                addToGroup = m_doc->fixtureGroup(var.toUInt());
×
1234
        }
×
1235
        else
1236
        {
1237
            // Group selected
1238
            QVariant var = current->data(KColumnName, PROP_GROUP);
×
1239
            if (var.isValid() == true)
×
1240
                addToGroup = m_doc->fixtureGroup(var.toUInt());
×
1241
        }
×
1242
    }
1243

1244
    /* If an empty name was given use the model instead */
1245
    if (name.simplified().isEmpty())
×
1246
    {
1247
        if (fixtureDef != NULL)
×
1248
            name = fixtureDef->model();
×
1249
        else
1250
            name = tr("Generic Dimmer");
×
1251
    }
1252

1253
    /* Add the rest (if any) WITH address gap */
1254
    for (int i = 0; i < af.amount(); i++)
×
1255
    {
1256
        QString modname;
×
1257

1258
        /* If we're adding more than one fixture,
1259
           append a number to the end of the name */
1260
        if (af.amount() > 1)
×
1261
            modname = QString("%1 #%2").arg(name).arg(i + 1, AppUtil::digits(af.amount()), 10, QChar('0'));
×
1262
        else
1263
            modname = name;
×
1264

1265
        /* Create the fixture */
1266
        Fixture* fxi = new Fixture(m_doc);
×
1267

1268
        /* Assign the next address AFTER the previous fixture
1269
           address space plus gap. */
1270
        fxi->setAddress(address + (i * channels) + (i * gap));
×
1271
        fxi->setUniverse(universe);
×
1272
        fxi->setName(modname);
×
1273
        /* Set a fixture definition & mode if they were
1274
           selected. Otherwise create a fixture definition
1275
           and mode for a generic dimmer. */
1276
        if (fixtureDef != NULL && mode != NULL)
×
1277
        {
1278
            fxi->setFixtureDefinition(fixtureDef, mode);
×
1279
        }
1280
        else
1281
        {
1282
            QLCFixtureDef* genericDef = fxi->genericDimmerDef(channels);
×
1283
            QLCFixtureMode* genericMode = fxi->genericDimmerMode(genericDef, channels);
×
1284
            fxi->setFixtureDefinition(genericDef, genericMode);
×
1285
        }
1286

1287
        m_doc->addFixture(fxi);
×
1288
        latestFxi = fxi->id();
×
1289
        if (addToGroup != NULL)
×
1290
            addToGroup->assignFixture(latestFxi);
×
1291
    }
×
1292

1293
    QTreeWidgetItem* selectItem = m_fixtures_tree->fixtureItem(latestFxi);
×
1294
    if (selectItem != NULL)
×
1295
        m_fixtures_tree->setCurrentItem(selectItem);
×
1296

1297
    updateView();
×
1298
}
×
1299

1300
void FixtureManager::addChannelsGroup()
×
1301
{
1302
    ChannelsGroup *group = new ChannelsGroup(m_doc);
×
1303

1304
    AddChannelsGroup cs(this, m_doc, group);
×
1305
    if (cs.exec() == QDialog::Accepted)
×
1306
    {
1307
        qDebug() << "Channels group added. Count: " << group->getChannels().count();
×
1308
        m_doc->addChannelsGroup(group, group->id());
×
1309
        updateChannelsGroupView();
×
1310
    }
1311
    else
1312
        delete group;
×
1313
}
×
1314

1315
void FixtureManager::slotAdd()
×
1316
{
1317
    if (m_currentTabIndex == 1)
×
1318
        addChannelsGroup();
×
1319
    else
1320
        addFixture();
×
1321
}
×
1322

1323
void FixtureManager::slotAddRGBPanel()
×
1324
{
1325
    AddRGBPanel rgb(this, m_doc);
×
1326
    if (rgb.exec() == QDialog::Accepted)
×
1327
    {
1328
        int rows = rgb.rows();
×
1329
        int columns = rgb.columns();
×
1330
        Fixture::Components components = rgb.components();
×
1331

1332
        FixtureGroup *grp = new FixtureGroup(m_doc);
×
1333
        Q_ASSERT(grp != NULL);
×
1334
        grp->setName(rgb.name());
×
1335
        QSize panelSize(columns, rows);
×
1336
        grp->setSize(panelSize);
×
1337
        m_doc->addFixtureGroup(grp);
×
1338
        updateGroupMenu();
×
1339

1340
        int transpose = 0;
×
1341
        if (rgb.direction() == AddRGBPanel::Vertical)
×
1342
        {
1343
                int tmp = columns;
×
1344
                columns = rows;
×
1345
                rows = tmp;
×
1346
                transpose = 1;
×
1347
        }
1348

1349
        QLCFixtureDef *rowDef = NULL;
×
1350
        QLCFixtureMode *rowMode = NULL;
×
1351
        quint32 address = (quint32)rgb.address();
×
1352
        int uniIndex = rgb.universeIndex();
×
1353
        int currRow = 0;
×
1354
        int rowInc = 1;
×
1355
        int xPosStart = 0;
×
1356
        int xPosEnd = columns - 1;
×
1357
        int xPosInc = 1;
×
1358

1359
        quint32 phyWidth = rgb.physicalWidth();
×
1360
        quint32 phyHeight = rgb.physicalHeight() / rows;
×
1361

1362
        if (transpose)
×
1363
        {
1364
                        if (rgb.orientation() == AddRGBPanel::TopRight ||
×
1365
                                rgb.orientation() == AddRGBPanel::BottomRight)
×
1366
                        {
1367
                                currRow = rows -1;
×
1368
                                rowInc = -1;
×
1369
                        }
1370
                        if (rgb.orientation() == AddRGBPanel::BottomRight ||
×
1371
                                rgb.orientation() == AddRGBPanel::BottomLeft)
×
1372
                        {
1373
                                xPosStart = columns - 1;
×
1374
                                xPosEnd = 0;
×
1375
                                xPosInc = -1;
×
1376
                        }
1377
        }
1378
        else
1379
        {
1380
                        if (rgb.orientation() == AddRGBPanel::BottomLeft ||
×
1381
                                rgb.orientation() == AddRGBPanel::BottomRight)
×
1382
                        {
1383
                                currRow = rows -1;
×
1384
                                rowInc = -1;
×
1385
                        }
1386
                        if (rgb.orientation() == AddRGBPanel::TopRight ||
×
1387
                                rgb.orientation() == AddRGBPanel::BottomRight)
×
1388
                        {
1389
                                xPosStart = columns - 1;
×
1390
                                xPosEnd = 0;
×
1391
                                xPosInc = -1;
×
1392
                        }
1393
        }
1394

1395
        for (int i = 0; i < rows; i++)
×
1396
        {
1397
            Fixture *fxi = new Fixture(m_doc);
×
1398
            Q_ASSERT(fxi != NULL);
×
1399
            fxi->setName(tr("%1 - Row %2").arg(rgb.name()).arg(i + 1));
×
1400
            if (rowDef == NULL)
×
1401
                rowDef = fxi->genericRGBPanelDef(columns, components, rgb.is16Bit());
×
1402
            if (rowMode == NULL)
×
1403
                rowMode = fxi->genericRGBPanelMode(rowDef, components, rgb.is16Bit(), phyWidth, phyHeight);
×
1404
            fxi->setFixtureDefinition(rowDef, rowMode);
×
1405

1406
            // Check universe span
1407
            if (address + fxi->channels() > 512)
×
1408
            {
1409
                if (!rgb.crossUniverse())
×
1410
                {
1411
                    uniIndex++;
×
1412
                    address = 0;
×
1413
                }
1414
            }
1415
            if (m_doc->inputOutputMap()->getUniverseID(uniIndex) == m_doc->inputOutputMap()->invalidUniverse())
×
1416
            {
1417
                m_doc->inputOutputMap()->addUniverse();
×
1418
                m_doc->inputOutputMap()->startUniverses();
×
1419
            }
1420

1421
            fxi->setUniverse(m_doc->inputOutputMap()->getUniverseID(uniIndex));
×
1422
            if (address + fxi->channels() > 512)
×
1423
                fxi->setCrossUniverse(rgb.crossUniverse());
×
1424
            fxi->setAddress(address);
×
1425
            m_doc->addFixture(fxi, Fixture::invalidId(), rgb.crossUniverse());
×
1426

1427
            address += fxi->channels();
×
1428
            if (address >= 512 && rgb.crossUniverse())
×
1429
            {
1430
                address -= 512;
×
1431
                uniIndex++;
×
1432
            }
1433

1434
            if (rgb.type() == AddRGBPanel::ZigZag)
×
1435
            {
1436
                int xPos = xPosStart;
×
1437
                for (int h = 0; h < fxi->heads(); h++)
×
1438
                {
1439
                        if (transpose)
×
1440
                                grp->assignHead(QLCPoint(currRow, xPos), GroupHead(fxi->id(), h));
×
1441
                        else
1442
                                grp->assignHead(QLCPoint(xPos, currRow), GroupHead(fxi->id(), h));
×
1443
                    xPos += xPosInc;
×
1444
                }
1445
            }
1446
            else if (rgb.type() == AddRGBPanel::Snake)
×
1447
            {
1448
                if (i%2 == 0)
×
1449
                {
1450
                    int xPos = xPosStart;
×
1451
                    for (int h = 0; h < fxi->heads(); h++)
×
1452
                    {
1453
                            if (transpose)
×
1454
                                    grp->assignHead(QLCPoint(currRow, xPos), GroupHead(fxi->id(), h));
×
1455
                            else
1456
                                    grp->assignHead(QLCPoint(xPos, currRow), GroupHead(fxi->id(), h));
×
1457
                        xPos += xPosInc;
×
1458
                    }
1459
                }
1460
                else
1461
                {
1462
                    int xPos = xPosEnd;
×
1463
                    for (int h = 0; h < fxi->heads(); h++)
×
1464
                    {
1465
                            if (transpose)
×
1466
                                    grp->assignHead(QLCPoint(currRow, xPos), GroupHead(fxi->id(), h));
×
1467
                            else
1468
                                    grp->assignHead(QLCPoint(xPos, currRow), GroupHead(fxi->id(), h));
×
1469
                        xPos += (-xPosInc);
×
1470
                    }
1471
                }
1472
            }
1473
            currRow += rowInc;
×
1474
        }
1475

1476
        updateView();
×
1477
        m_doc->setModified();
×
1478
    }
1479
}
×
1480

1481
void FixtureManager::removeFixture()
×
1482
{
1483
    // Ask before deletion
1484
    if (QMessageBox::question(this, tr("Delete Fixtures"),
×
1485
                              tr("Do you want to delete the selected items?"),
×
1486
                              QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
×
1487
    {
1488
        return;
×
1489
    }
1490

1491
    QListIterator <QTreeWidgetItem*> it(m_fixtures_tree->selectedItems());
×
1492

1493
    // We put items to delete in sets,
1494
    // so no segfault happens when the same fixture is selected twice
1495
    QSet <quint32> groupsToDelete;
×
1496
    QSet <quint32> fixturesToDelete;
×
1497
    while (it.hasNext() == true)
×
1498
    {
1499
        QTreeWidgetItem* item(it.next());
×
1500
        Q_ASSERT(item != NULL);
×
1501

1502
        // Is the item a fixture ?
1503
        QVariant var = item->data(KColumnName, PROP_ID);
×
1504
        if (var.isValid() == true)
×
1505
            fixturesToDelete << var.toUInt();
×
1506
        else
1507
        {
1508
            // Is the item a fixture group ?
1509
            var = item->data(KColumnName, PROP_GROUP);
×
1510
            if (var.isValid() == true)
×
1511
                groupsToDelete << var.toUInt();
×
1512
        }
1513
    }
×
1514

1515
    // delete fixture groups
1516
    foreach (quint32 id, groupsToDelete)
×
1517
        m_doc->deleteFixtureGroup(id);
×
1518

1519
    // delete fixtures
1520
    foreach (quint32 id, fixturesToDelete)
×
1521
    {
1522
        /** @todo This is REALLY bogus here, since Fixture or Doc should do
1523
            this. However, FixtureManager is the only place to destroy fixtures,
1524
            so it's rather safe to reset the fixture's address space here. */
1525
        Fixture* fxi = m_doc->fixture(id);
×
1526
        Q_ASSERT(fxi != NULL);
×
1527
        QList<Universe*> ua = m_doc->inputOutputMap()->claimUniverses();
×
1528
        int universe = fxi->universe();
×
1529
        if (universe < ua.count())
×
1530
            ua[universe]->reset(fxi->address(), fxi->channels());
×
1531
        m_doc->inputOutputMap()->releaseUniverses();
×
1532

1533
        m_doc->deleteFixture(id);
×
1534
    }
×
1535
}
×
1536

1537
void FixtureManager::removeChannelsGroup()
×
1538
{
1539
    // Ask before deletion
1540
    if (QMessageBox::question(this, tr("Delete Channels Group"),
×
1541
                              tr("Do you want to delete the selected groups?"),
×
1542
                              QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
×
1543
    {
1544
        return;
×
1545
    }
1546

1547
    disconnect(m_channel_groups_tree, SIGNAL(itemSelectionChanged()),
×
1548
            this, SLOT(slotChannelsGroupSelectionChanged()));
1549

1550
    QListIterator <QTreeWidgetItem*> it(m_channel_groups_tree->selectedItems());
×
1551
    while (it.hasNext() == true)
×
1552
    {
1553
        QTreeWidgetItem* item(it.next());
×
1554
        Q_ASSERT(item != NULL);
×
1555

1556
        QVariant var = item->data(KColumnName, PROP_ID);
×
1557
        if (var.isValid() == true)
×
1558
            m_doc->deleteChannelsGroup(var.toUInt());
×
1559
    }
×
1560
    updateChannelsGroupView();
×
1561

1562
    connect(m_channel_groups_tree, SIGNAL(itemSelectionChanged()),
×
1563
            this, SLOT(slotChannelsGroupSelectionChanged()));
1564
}
×
1565

1566
void FixtureManager::slotRemove()
×
1567
{
1568
    if (m_currentTabIndex == 1)
×
1569
        removeChannelsGroup();
×
1570
    else
1571
        removeFixture();
×
1572
}
×
1573

1574
void FixtureManager::editFixtureProperties()
×
1575
{
1576
    QTreeWidgetItem* item = m_fixtures_tree->currentItem();
×
1577
    if (item == NULL)
×
1578
        return;
×
1579

1580
    QVariant var = item->data(KColumnName, PROP_ID);
×
1581
    if (var.isValid() == false)
×
1582
        return;
×
1583

1584
    quint32 id = var.toUInt();
×
1585
    Fixture* fxi = m_doc->fixture(id);
×
1586
    if (fxi == NULL)
×
1587
        return;
×
1588

1589
    QString manuf;
×
1590
    QString model;
×
1591
    QString mode;
×
1592

1593
    if (fxi->fixtureDef() != NULL)
×
1594
    {
1595
        manuf = fxi->fixtureDef()->manufacturer();
×
1596
        model = fxi->fixtureDef()->model();
×
1597
        mode = fxi->fixtureMode()->name();
×
1598
    }
1599

1600
    AddFixture af(this, m_doc, fxi);
×
1601
    af.setWindowTitle(tr("Change fixture properties"));
×
1602
    if (af.exec() == QDialog::Accepted)
×
1603
    {
1604
        if (af.invalidAddress() == false)
×
1605
        {
1606
            bool changed = false;
×
1607

1608
            fxi->blockSignals(true);
×
1609
            if (fxi->name() != af.name())
×
1610
            {
1611
                fxi->setName(af.name());
×
1612
                changed = true;
×
1613
            }
1614
            if (fxi->universe() != af.universe())
×
1615
            {
1616
                fxi->setUniverse(af.universe());
×
1617
                changed = true;
×
1618
            }
1619
            if (fxi->address() != af.address())
×
1620
            {
1621
                fxi->setAddress(af.address());
×
1622
                changed = true;
×
1623
            }
1624
            fxi->blockSignals(false);
×
1625

1626
            if (af.fixtureDef() != NULL && af.mode() != NULL)
×
1627
            {
1628
                if (af.fixtureDef()->manufacturer() == KXMLFixtureGeneric &&
×
1629
                    af.fixtureDef()->model() == KXMLFixtureGeneric)
×
1630
                {
1631
                    if (fxi->channels() != af.channels())
×
1632
                    {
1633
                        QLCFixtureDef* fixtureDef = fxi->genericDimmerDef(af.channels());
×
1634
                        QLCFixtureMode* fixtureMode = fxi->genericDimmerMode(fixtureDef, af.channels());
×
1635
                        fxi->setFixtureDefinition(fixtureDef, fixtureMode);
×
1636
                    }
1637
                }
1638
                else
1639
                {
1640
                    fxi->setFixtureDefinition(af.fixtureDef(), af.mode());
×
1641
                }
1642
            }
1643
            else
1644
            {
1645
                /* Generic dimmer */
1646
                fxi->setFixtureDefinition(NULL, NULL);
×
1647
                fxi->setChannels(af.channels());
×
1648
            }
1649

1650
            // Emit changed signal
1651
            if (changed)
×
1652
                fxi->setID(fxi->id());
×
1653

1654
            updateView();
×
1655
            slotSelectionChanged();
×
1656
        }
1657
        else
1658
        {
1659
            QMessageBox msg(QMessageBox::Critical, tr("Error"),
×
1660
                    tr("Please enter a valid address"), QMessageBox::Ok);
×
1661
            msg.exec();
×
1662
        }
×
1663
    }
1664
}
×
1665

1666
void FixtureManager::editChannelGroupProperties()
×
1667
{
1668
    int selectedCount = m_channel_groups_tree->selectedItems().size();
×
1669

1670
    if (selectedCount > 0)
×
1671
    {
1672
        QTreeWidgetItem* current = m_channel_groups_tree->selectedItems().first();
×
1673
        QVariant var = current->data(KColumnName, PROP_ID);
×
1674
        if (var.isValid() == true)
×
1675
        {
1676
            ChannelsGroup *group = m_doc->channelsGroup(var.toUInt());
×
1677

1678
            AddChannelsGroup cs(this, m_doc, group);
×
1679
            if (cs.exec() == QDialog::Accepted)
×
1680
            {
1681
                qDebug() << "CHANNEL GROUP MODIFIED. Count: " << group->getChannels().count();
×
1682
                m_doc->addChannelsGroup(group, group->id());
×
1683
                updateChannelsGroupView();
×
1684
            }
1685
        }
×
1686
    }
×
1687
}
×
1688

1689
int FixtureManager::headCount(const QList <QTreeWidgetItem*>& items) const
×
1690
{
1691
    int count = 0;
×
1692
    QListIterator <QTreeWidgetItem*> it(items);
×
1693
    while (it.hasNext() == true)
×
1694
    {
1695
        QTreeWidgetItem* item = it.next();
×
1696
        Q_ASSERT(item != NULL);
×
1697

1698
        QVariant var = item->data(KColumnName, PROP_ID);
×
1699
        if (var.isValid() == false)
×
1700
            continue;
×
1701

1702
        Fixture* fxi = m_doc->fixture(var.toUInt());
×
1703
        count += fxi->heads();
×
1704
    }
×
1705

1706
    return count;
×
1707
}
×
1708

1709
void FixtureManager::slotProperties()
×
1710
{
1711
    if (m_currentTabIndex == 1)
×
1712
        editChannelGroupProperties();
×
1713
    else
1714
        editFixtureProperties();
×
1715
}
×
1716

1717
void FixtureManager::slotFadeConfig()
×
1718
{
1719
    ChannelsSelection cfg(m_doc, this, ChannelsSelection::ConfigurationMode);
×
1720
    if (cfg.exec() == QDialog::Rejected)
×
1721
        return; // User pressed cancel
×
1722
    m_doc->setModified();
×
1723
}
×
1724

1725
void FixtureManager::slotRemap()
×
1726
{
1727
    FixtureRemap fxr(m_doc);
×
1728
    if (fxr.exec() == QDialog::Rejected)
×
1729
        return; // User pressed cancel
×
1730

1731
    updateView();
×
1732
}
×
1733

1734
void FixtureManager::slotUnGroup()
×
1735
{
1736
    if (QMessageBox::question(this, tr("Ungroup fixtures?"),
×
1737
                              tr("Do you want to ungroup the selected fixtures?"),
×
1738
                              QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
×
1739
    {
1740
        return;
×
1741
    }
1742

1743
    // Because FixtureGroup::resignFixture() emits changed(), which makes the tree
1744
    // update its contents in the middle, invalidating m_tree->selectedItems(),
1745
    // we must pick the list of fixtures and groups first and then resign them in
1746
    // one big bunch.
1747
    QList <QPair<quint32,quint32> > resignList;
×
1748

1749
    foreach (QTreeWidgetItem* item, m_fixtures_tree->selectedItems())
×
1750
    {
1751
        if (item->parent() == NULL)
×
1752
            continue;
×
1753

1754
        QVariant var = item->parent()->data(KColumnName, PROP_GROUP);
×
1755
        if (var.isValid() == false)
×
1756
            continue;
×
1757
        quint32 grp = var.toUInt();
×
1758

1759
        var = item->data(KColumnName, PROP_ID);
×
1760
        if (var.isValid() == false)
×
1761
            continue;
×
1762
        quint32 fxi = var.toUInt();
×
1763

1764
        resignList << QPair <quint32,quint32> (grp, fxi);
×
1765
    }
×
1766

1767
    QListIterator <QPair<quint32,quint32> > it(resignList);
×
1768
    while (it.hasNext() == true)
×
1769
    {
1770
        QPair <quint32,quint32> pair(it.next());
×
1771
        FixtureGroup* grp = m_doc->fixtureGroup(pair.first);
×
1772
        Q_ASSERT(grp != NULL);
×
1773
        grp->resignFixture(pair.second);
×
1774
    }
1775
}
×
1776

1777
void FixtureManager::slotGroupSelected(QAction* action)
×
1778
{
1779
    FixtureGroup* grp = NULL;
×
1780

1781
    if (action->data().isValid() == true)
×
1782
    {
1783
        // Existing group selected
1784
        grp = (FixtureGroup*) (action->data().toULongLong());
×
1785
        Q_ASSERT(grp != NULL);
×
1786
    }
1787
    else
1788
    {
1789
        // New Group selected.
1790

1791
        // Suggest an equilateral grid
1792
        qreal side = sqrt(headCount(m_fixtures_tree->selectedItems()));
×
1793
        if (side != floor(side))
×
1794
            side += 1; // Fixture number doesn't provide a full square
×
1795

1796
        CreateFixtureGroup cfg(this);
×
1797
        cfg.setSize(QSize(side, side));
×
1798
        if (cfg.exec() != QDialog::Accepted)
×
1799
            return; // User pressed cancel
×
1800

1801
        grp = new FixtureGroup(m_doc);
×
1802
        Q_ASSERT(grp != NULL);
×
1803
        grp->setName(cfg.name());
×
1804
        grp->setSize(cfg.size());
×
1805
        m_doc->addFixtureGroup(grp);
×
1806
        updateGroupMenu();
×
1807
    }
×
1808

1809
    // Assign selected fixture items to the group
1810
    foreach (QTreeWidgetItem* item, m_fixtures_tree->selectedItems())
×
1811
    {
1812
        QVariant var = item->data(KColumnName, PROP_ID);
×
1813
        if (var.isValid() == false)
×
1814
            continue;
×
1815

1816
        grp->assignFixture(var.toUInt());
×
1817
    }
×
1818

1819
    updateView();
×
1820
}
1821

1822
void FixtureManager::slotMoveGroupUp()
×
1823
{
1824
    if (m_channel_groups_tree->selectedItems().size() > 0)
×
1825
    {
1826
        QTreeWidgetItem* item = m_channel_groups_tree->selectedItems().first();
×
1827
        quint32 grpID = item->data(KColumnName, PROP_ID).toUInt();
×
1828
        m_doc->moveChannelGroup(grpID, -1);
×
1829
        updateChannelsGroupView();
×
1830
    }
1831
}
×
1832

1833
void FixtureManager::slotMoveGroupDown()
×
1834
{
1835
    if (m_channel_groups_tree->selectedItems().size() > 0)
×
1836
    {
1837
        QTreeWidgetItem* item = m_channel_groups_tree->selectedItems().first();
×
1838
        quint32 grpID = item->data(KColumnName, PROP_ID).toUInt();
×
1839
        m_doc->moveChannelGroup(grpID, 1);
×
1840
        updateChannelsGroupView();
×
1841
    }
1842
}
×
1843

1844
QString FixtureManager::createDialog(bool import)
×
1845
{
1846
    QString fileName;
×
1847

1848
    /* Create a file save dialog */
1849
    QFileDialog dialog(this);
×
1850
    if (import == true)
×
1851
    {
1852
        dialog.setWindowTitle(tr("Import Fixtures List"));
×
1853
        dialog.setAcceptMode(QFileDialog::AcceptOpen);
×
1854
    }
1855
    else
1856
    {
1857
        dialog.setWindowTitle(tr("Export Fixtures List As"));
×
1858
        dialog.setAcceptMode(QFileDialog::AcceptSave);
×
1859
    }
1860

1861
    /* Append file filters to the dialog */
1862
    QStringList filters;
×
1863
    filters << tr("Fixtures List (*%1)").arg(KExtFixtureList);
×
1864
#if defined(WIN32) || defined(Q_OS_WIN)
1865
    filters << tr("All Files (*.*)");
1866
#else
1867
    filters << tr("All Files (*)");
×
1868
#endif
1869
    dialog.setNameFilters(filters);
×
1870

1871
    /* Append useful URLs to the dialog */
1872
    QList <QUrl> sidebar;
×
1873
    sidebar.append(QUrl::fromLocalFile(QDir::homePath()));
×
1874
    sidebar.append(QUrl::fromLocalFile(QDir::rootPath()));
×
1875
    dialog.setSidebarUrls(sidebar);
×
1876

1877
    /* Get file name */
1878
    if (dialog.exec() != QDialog::Accepted)
×
1879
        return "";
×
1880

1881
    fileName = dialog.selectedFiles().first();
×
1882
    if (fileName.isEmpty() == true)
×
1883
        return "";
×
1884

1885
    /* Always use the fixture definition suffix */
1886
    if (import == false && fileName.right(5) != KExtFixtureList)
×
1887
        fileName += KExtFixtureList;
×
1888

1889
    return fileName;
×
1890
}
×
1891

1892
void FixtureManager::slotImport()
×
1893
{
1894
    QString fileName = createDialog(true);
×
1895

1896
    QXmlStreamReader *doc = QLCFile::getXMLReader(fileName);
×
1897
    if (doc == NULL || doc->device() == NULL || doc->hasError())
×
1898
    {
1899
        qWarning() << Q_FUNC_INFO << "Unable to read from" << fileName;
×
1900
        return;
×
1901
    }
1902

1903
    while (!doc->atEnd())
×
1904
    {
1905
        if (doc->readNext() == QXmlStreamReader::DTD)
×
1906
            break;
×
1907
    }
1908
    if (doc->hasError())
×
1909
    {
1910
        QLCFile::releaseXMLReader(doc);
×
1911
        return;
×
1912
    }
1913

1914
    if (doc->dtdName() == KXMLQLCFixturesList)
×
1915
    {
1916
        doc->readNextStartElement();
×
1917
        if (doc->name() != KXMLQLCFixturesList)
×
1918
        {
1919
            qWarning() << Q_FUNC_INFO << "Fixture Definition node not found";
×
1920
            QLCFile::releaseXMLReader(doc);
×
1921
            return;
×
1922
        }
1923

1924
        while (doc->readNextStartElement())
×
1925
        {
1926
            if (doc->name() == KXMLFixture)
×
1927
            {
1928
                Fixture* fxi = new Fixture(m_doc);
×
1929
                Q_ASSERT(fxi != NULL);
×
1930

1931
                if (fxi->loadXML(*doc, m_doc, m_doc->fixtureDefCache()) == true)
×
1932
                {
1933
                    if (m_doc->addFixture(fxi /*, fxi->id()*/) == true)
×
1934
                    {
1935
                        /* Success */
1936
                        qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name() << "successfully created.";
×
1937
                    }
1938
                    else
1939
                    {
1940
                        /* Doc is full */
1941
                        qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name() << "cannot be created.";
×
1942
                        delete fxi;
×
1943
                    }
1944
                }
1945
                else
1946
                {
1947
                    qWarning() << Q_FUNC_INFO << "Fixture" << fxi->name() << "cannot be loaded.";
×
1948
                    delete fxi;
×
1949
                }
1950
            }
1951
            else if (doc->name() == KXMLQLCFixtureGroup)
×
1952
            {
1953
                FixtureGroup* grp = new FixtureGroup(m_doc);
×
1954
                Q_ASSERT(grp != NULL);
×
1955

1956
                if (grp->loadXML(*doc) == true)
×
1957
                {
1958
                    m_doc->addFixtureGroup(grp, grp->id());
×
1959
                }
1960
                else
1961
                {
1962
                    qWarning() << Q_FUNC_INFO << "FixtureGroup" << grp->name() << "cannot be loaded.";
×
1963
                    delete grp;
×
1964
                }
1965
            }
1966
            else
1967
            {
1968
                qWarning() << Q_FUNC_INFO << "Unknown label tag:" << doc->name().toString();
×
1969
                doc->skipCurrentElement();
×
1970
            }
1971
        }
1972
        updateView();
×
1973
    }
1974
    QLCFile::releaseXMLReader(doc);
×
1975
}
×
1976

1977
void FixtureManager::slotExport()
×
1978
{
1979
    QString fileName = createDialog(false);
×
1980

1981
    QFile file(fileName);
×
1982
    if (file.open(QIODevice::WriteOnly) == false)
×
1983
        return;
×
1984

1985
    QXmlStreamWriter doc(&file);
×
1986
    doc.setAutoFormatting(true);
×
1987
    doc.setAutoFormattingIndent(1);
×
1988
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1989
    doc.setCodec("UTF-8");
1990
#endif
1991
    QLCFile::writeXMLHeader(&doc, KXMLQLCFixturesList);
×
1992

1993
    QListIterator <Fixture*> fxit(m_doc->fixtures());
×
1994
    while (fxit.hasNext() == true)
×
1995
    {
1996
        Fixture* fxi(fxit.next());
×
1997
        Q_ASSERT(fxi != NULL);
×
1998
        fxi->saveXML(&doc);
×
1999
    }
2000

2001
    QListIterator <FixtureGroup*>grpit(m_doc->fixtureGroups());
×
2002
    while (grpit.hasNext() == true)
×
2003
    {
2004
        FixtureGroup *fxgrp(grpit.next());
×
2005
        Q_ASSERT(fxgrp != NULL);
×
2006
        fxgrp->saveXML(&doc);
×
2007
    }
2008

2009
    doc.writeEndDocument();
×
2010
    file.close();
×
2011
}
×
2012

2013
void FixtureManager::slotContextMenuRequested(const QPoint&)
×
2014
{
2015
    QMenu menu(this);
×
2016
    menu.addAction(m_addAction);
×
2017
    menu.addAction(m_addRGBAction);
×
2018
    menu.addAction(m_propertiesAction);
×
2019
    menu.addAction(m_removeAction);
×
2020
    menu.addSeparator();
×
2021
    menu.addAction(m_groupAction);
×
2022
    menu.addAction(m_unGroupAction);
×
2023
    menu.exec(QCursor::pos());
×
2024
}
×
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

© 2026 Coveralls, Inc