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

mcallegari / qlcplus / 7252848206

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

push

github

mcallegari
Code style review #1427

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

8 existing lines in 2 files now uncovered.

15169 of 47304 relevant lines covered (32.07%)

23733.74 hits per line

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

0.0
/ui/src/scripteditor.cpp
1
/*
2
  Q Light Controller
3
  scripteditor.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 <QTextDocument>
21
#include <QInputDialog>
22
#include <QFileDialog>
23
#include <QTextCursor>
24
#include <QMessageBox>
25
#include <QFormLayout>
26
#include <QFileInfo>
27
#include <QAction>
28
#include <QDebug>
29
#include <QMenu>
30
#include <cmath>
31

32
#include "functionselection.h"
33
#include "channelsselection.h"
34
#include "assignhotkey.h"
35
#include "scripteditor.h"
36
#include "mastertimer.h"
37
#include "speeddial.h"
38
#include "script.h"
39
#include "doc.h"
40

41
ScriptEditor::ScriptEditor(QWidget* parent, Script* script, Doc* doc)
×
42
    : QWidget(parent)
43
    , m_script(script)
44
    , m_doc(doc)
45
    , m_lastUsedPath(QString())
×
46
{
47
    setupUi(this);
×
48
    initAddMenu();
×
49

50
    /* Name */
51
    m_nameEdit->setText(m_script->name());
×
52
    m_nameEdit->setSelection(0, m_nameEdit->text().length());
×
53
    connect(m_nameEdit, SIGNAL(textEdited(const QString&)),
×
54
            this, SLOT(slotNameEdited(const QString&)));
55

56
    /* Document */
57
    m_document = new QTextDocument(m_script->data(), this);
×
58
#if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0))
59
    m_editor->setTabStopWidth(20);
60
#else
61
    m_editor->setTabStopDistance(20);
×
62
#endif
63
    m_editor->setDocument(m_document);
×
64
    connect(m_document, SIGNAL(undoAvailable(bool)), m_undoButton, SLOT(setEnabled(bool)));
×
65
    m_document->setUndoRedoEnabled(false);
×
66
    m_document->setUndoRedoEnabled(true);
×
67
    m_document->clearUndoRedoStacks();
×
68

69
    m_editor->moveCursor(QTextCursor::End);
×
70
    connect(m_document, SIGNAL(contentsChanged()), this, SLOT(slotContentsChanged()));
×
71

72
    connect(m_testPlayButton, SIGNAL(clicked()), this, SLOT(slotTestRun()));
×
73
    connect(m_checkButton, SIGNAL(clicked()), this, SLOT(slotCheckSyntax()));
×
74

75
    connect(m_script, SIGNAL(stopped(quint32)), this, SLOT(slotFunctionStopped(quint32)));
×
76

77
    // Set focus to the editor
78
    m_nameEdit->setFocus();
×
79
}
×
80

81
ScriptEditor::~ScriptEditor()
×
82
{
83
    delete m_document;
×
84
    m_document = NULL;
×
85

86
    if (m_testPlayButton->isChecked() == true)
×
87
        m_script->stopAndWait();
×
88
}
×
89

90
void ScriptEditor::initAddMenu()
×
91
{
92
    m_addStartFunctionAction = new QAction(QIcon(":/function.png"), tr("Start Function"), this);
×
93
    connect(m_addStartFunctionAction, SIGNAL(triggered(bool)),
×
94
            this, SLOT(slotAddStartFunction()));
95

96
    m_addStopFunctionAction = new QAction(QIcon(":/fileclose.png"), tr("Stop Function"), this);
×
97
    connect(m_addStopFunctionAction, SIGNAL(triggered(bool)),
×
98
            this, SLOT(slotAddStopFunction()));
99

100
    m_addBlackoutAction = new QAction(QIcon(":/blackout.png"), tr("Blackout"), this);
×
101
    connect(m_addBlackoutAction, SIGNAL(triggered(bool)),
×
102
            this, SLOT(slotAddBlackout()));
103

104
    m_addWaitAction = new QAction(QIcon(":/speed.png"), tr("Wait"), this);
×
105
    connect(m_addWaitAction, SIGNAL(triggered(bool)),
×
106
            this, SLOT(slotAddWait()));
107

108
    m_addWaitKeyAction = new QAction(QIcon(":/key_bindings.png"), tr("Wait Key"), this);
×
109
    connect(m_addWaitKeyAction, SIGNAL(triggered(bool)),
×
110
            this, SLOT(slotAddWaitKey()));
111

112
    m_addSetHtpAction = new QAction(QIcon(":/fixture.png"), tr("Set HTP"), this);
×
113
    connect(m_addSetHtpAction, SIGNAL(triggered(bool)),
×
114
            this, SLOT(slotAddSetHtp()));
115

116
    m_addSetLtpAction = new QAction(QIcon(":/fixture.png"), tr("Set LTP"), this);
×
117
    connect(m_addSetLtpAction, SIGNAL(triggered(bool)),
×
118
            this, SLOT(slotAddSetLtp()));
119

120
    m_addSetFixtureAction = new QAction(QIcon(":/movinghead.png"), tr("Set Fixture"), this);
×
121
    connect(m_addSetFixtureAction, SIGNAL(triggered(bool)),
×
122
            this, SLOT(slotAddSetFixture()));
123

124
    m_addSystemCommandAction = new QAction(QIcon(":/player_play.png"), tr("System Command"), this);
×
125
    connect(m_addSystemCommandAction, SIGNAL(triggered(bool)),
×
126
            this, SLOT(slotAddSystemCommand()));
127

128
    m_addCommentAction = new QAction(QIcon(":/label.png"), tr("Comment"), this);
×
129
    connect(m_addCommentAction, SIGNAL(triggered(bool)),
×
130
            this, SLOT(slotAddComment()));
131

132
    m_addRandomAction = new QAction(QIcon(":/other.png"), tr("Random Number"), this);
×
133
    connect(m_addRandomAction, SIGNAL(triggered(bool)),
×
134
            this, SLOT(slotAddRandom()));
135

136
    m_addFilePathAction = new QAction(QIcon(":/fileopen.png"), tr("File Path"), this);
×
137
    connect(m_addFilePathAction, SIGNAL(triggered(bool)),
×
138
            this, SLOT(slotAddFilePath()));
139

140
    m_addMenu = new QMenu(this);
×
141
    m_addMenu->addAction(m_addStartFunctionAction);
×
142
    m_addMenu->addAction(m_addStopFunctionAction);
×
143
    m_addMenu->addAction(m_addBlackoutAction);
×
144
    //m_addMenu->addAction(m_addSetHtpAction);
145
    //m_addMenu->addAction(m_addSetLtpAction);
146
    m_addMenu->addAction(m_addSetFixtureAction);
×
147
    m_addMenu->addAction(m_addSystemCommandAction);
×
148
    m_addMenu->addSeparator();
×
149
    m_addMenu->addAction(m_addWaitAction);
×
150
    //m_addMenu->addAction(m_addWaitKeyAction);
151
    m_addMenu->addSeparator();
×
152
    m_addMenu->addAction(m_addCommentAction);
×
153
    m_addMenu->addAction(m_addRandomAction);
×
154
    m_addMenu->addAction(m_addFilePathAction);
×
155

156
    m_addButton->setMenu(m_addMenu);
×
157
}
×
158

159
QString ScriptEditor::getFilePath()
×
160
{
161
    /* Create a file open dialog */
162
    QFileDialog dialog(this);
×
163
    dialog.setWindowTitle(tr("Open Executable File"));
×
164
    dialog.setAcceptMode(QFileDialog::AcceptOpen);
×
165

166
    QStringList filters;
×
167
#if defined(WIN32) || defined(Q_OS_WIN)
168
    filters << tr("All Files (*.*)");
169
#else
170
    filters << tr("All Files (*)");
×
171
#endif
172
    dialog.setNameFilters(filters);
×
173

174
    /* Append useful URLs to the dialog */
175
    QList <QUrl> sidebar;
×
176
    sidebar.append(QUrl::fromLocalFile(QDir::homePath()));
×
177
    sidebar.append(QUrl::fromLocalFile(QDir::rootPath()));
×
178
    dialog.setSidebarUrls(sidebar);
×
179

180
    if (!m_lastUsedPath.isEmpty())
×
181
        dialog.setDirectory(m_lastUsedPath);
×
182

183
    /* Get file name */
184
    if (dialog.exec() != QDialog::Accepted)
×
185
        return QString();
×
186

187
    QString fn = dialog.selectedFiles().first();
×
188
    if (fn.isEmpty() == true)
×
189
        return QString();
×
190

191
#if defined(WIN32) || defined(Q_OS_WIN)
192
    fn.replace("/", "\\");
193
#endif
194

195
    if (fn.contains(" "))
×
196
        return QString("\"%1\"").arg(fn);
×
197
    else
198
        return fn;
×
199
}
200

201
void ScriptEditor::slotNameEdited(const QString& name)
×
202
{
203
    m_script->setName(name);
×
204
}
×
205

206
void ScriptEditor::slotContentsChanged()
×
207
{
208
    //! @todo: this might become quite heavy if there's a lot of content
209
    m_script->setData(m_document->toPlainText());
×
210
    m_doc->setModified();
×
211
}
×
212

213
void ScriptEditor::slotFunctionStopped(quint32 id)
×
214
{
215
    if (id == m_script->id())
×
216
    {
217
        m_testPlayButton->blockSignals(true);
×
218
        m_testPlayButton->setChecked(false);
×
219
        m_testPlayButton->blockSignals(false);
×
220
    }
221
}
×
222

223
void ScriptEditor::slotAddStartFunction()
×
224
{
225
    FunctionSelection fs(this, m_doc);
×
226
    fs.setDisabledFunctions(QList <quint32> () << m_script->id());
×
227
    if (fs.exec() == QDialog::Accepted)
×
228
    {
229
        m_editor->moveCursor(QTextCursor::StartOfLine);
×
230
        QTextCursor cursor(m_editor->textCursor());
×
231

232
        foreach (quint32 id, fs.selection())
×
233
        {
234
            Function* function = m_doc->function(id);
×
235
            Q_ASSERT(function != NULL);
×
236
            QString cmd = QString("%1:%2 // %3\n").arg(Script::startFunctionCmd)
×
237
                                                   .arg(id)
×
238
                                                   .arg(function->name());
×
239
            cursor.insertText(cmd);
×
240
            m_editor->moveCursor(QTextCursor::Down);
×
241
        }
242
    }
243
}
×
244

245
void ScriptEditor::slotAddStopFunction()
×
246
{
247
    FunctionSelection fs(this, m_doc);
×
248
    fs.setDisabledFunctions(QList <quint32> () << m_script->id());
×
249
    if (fs.exec() == QDialog::Accepted)
×
250
    {
251
        m_editor->moveCursor(QTextCursor::StartOfLine);
×
252
        QTextCursor cursor(m_editor->textCursor());
×
253

254
        foreach (quint32 id, fs.selection())
×
255
        {
256
            Function* function = m_doc->function(id);
×
257
            Q_ASSERT(function != NULL);
×
258
            QString cmd = QString("%1:%2 // %3\n").arg(Script::stopFunctionCmd)
×
259
                                                  .arg(id)
×
260
                                                  .arg(function->name());
×
261
            cursor.insertText(cmd);
×
262
            m_editor->moveCursor(QTextCursor::Down);
×
263
        }
264
    }
265
}
×
266

267
void ScriptEditor::slotAddBlackout()
×
268
{
269
    QDialog dialog(this);
×
270
    // Use a layout allowing to have a label next to each field
271
    QVBoxLayout dLayout(&dialog);
×
272

273
    QCheckBox *cb = new QCheckBox(tr("Blackout state"));
×
274
    cb->setChecked(true);
×
275
    dLayout.addWidget(cb);
×
276

277
    // Add some standard buttons (Cancel/Ok) at the bottom of the dialog
278
    QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
279
                               Qt::Horizontal, &dialog);
×
280
    dLayout.addWidget(&buttonBox);
×
281
    QObject::connect(&buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept()));
×
282
    QObject::connect(&buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject()));
×
283

284
    // Show the dialog as modal
285
    if (dialog.exec() == QDialog::Accepted)
×
286
    {
287
        m_editor->moveCursor(QTextCursor::StartOfLine);
×
288
        m_editor->textCursor().insertText(QString("%1:%2\n")
×
289
                              .arg(Script::blackoutCmd)
×
290
                              .arg(cb->isChecked() ? Script::blackoutOn : Script::blackoutOff));
×
291
    }
292
}
×
293

294
void ScriptEditor::slotAddWait()
×
295
{
296
    QDialog dialog(this);
×
297
    // Use a layout allowing to have a label next to each field
298
    QVBoxLayout dLayout(&dialog);
×
299

300
    dLayout.addWidget(new QLabel(tr("Enter the desired time")));
×
301
    SpeedDial *sd = new SpeedDial(this);
×
302
    ushort dialMask = sd->visibilityMask();
×
303
    dialMask = (dialMask & ~SpeedDial::Infinite);
×
304
    dialMask = (dialMask & ~SpeedDial::Tap);
×
305
    sd->setVisibilityMask(dialMask);
×
306
    sd->setValue(1000);
×
307
    dLayout.addWidget(sd);
×
308

309
    // Add some standard buttons (Cancel/Ok) at the bottom of the dialog
310
    QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
311
                               Qt::Horizontal, &dialog);
×
312
    dLayout.addWidget(&buttonBox);
×
313
    QObject::connect(&buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept()));
×
314
    QObject::connect(&buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject()));
×
315

316
    // Show the dialog as modal
317
    if (dialog.exec() == QDialog::Accepted)
×
318
    {
319
        m_editor->moveCursor(QTextCursor::StartOfLine);
×
320
        m_editor->textCursor().insertText(QString("%1:%2\n")
×
321
                              .arg(Script::waitCmd).arg(Function::speedToString(sd->value())));
×
322
    }
323
}
×
324

325
void ScriptEditor::slotAddWaitKey()
×
326
{
327
    AssignHotKey ahk(this);
×
328
    if (ahk.exec() == QDialog::Accepted)
×
329
    {
330
        m_editor->moveCursor(QTextCursor::StartOfLine);
×
331
        m_editor->textCursor().insertText(QString("%1:%2 // Not supported yet\n")
×
332
                                                    .arg(Script::waitKeyCmd)
×
333
                                                    .arg(ahk.keySequence().toString()));
×
334
    }
335
}
×
336

337
void ScriptEditor::slotAddSetHtp()
×
338
{
339
    m_editor->moveCursor(QTextCursor::StartOfLine);
×
340
    m_editor->textCursor().insertText(QString("sethtp:0 val:0 uni:1 // Not supported yet\n"));
×
341
    m_editor->moveCursor(QTextCursor::EndOfLine);
×
342
}
×
343

344
void ScriptEditor::slotAddSetLtp()
×
345
{
346
    m_editor->moveCursor(QTextCursor::StartOfLine);
×
347
    m_editor->textCursor().insertText(QString("setltp:0 val:0 uni:1 // Not supported yet\n"));
×
348
    m_editor->moveCursor(QTextCursor::EndOfLine);
×
349
}
×
350

351
void ScriptEditor::slotAddSetFixture()
×
352
{
353
    ChannelsSelection cfg(m_doc, this);
×
354
    if (cfg.exec() == QDialog::Rejected)
×
355
        return; // User pressed cancel
×
356

357
    QList<SceneValue> channelsList = cfg.channelsList();
×
NEW
358
    foreach (SceneValue sv, channelsList)
×
359
    {
360
        Fixture* fxi = m_doc->fixture(sv.fxi);
×
361
        if (fxi != NULL)
×
362
        {
363
            const QLCChannel* channel = fxi->channel(sv.channel);
×
364
            m_editor->moveCursor(QTextCursor::StartOfLine);
×
365
            m_editor->textCursor().insertText(QString("%1:%2 ch:%3 val:0 // %4, %5\n")
×
366
                                                .arg(Script::setFixtureCmd)
×
367
                                                .arg(fxi->id()).arg(sv.channel)
×
368
                                                .arg(fxi->name()).arg(channel->name()));
×
369
            m_editor->moveCursor(QTextCursor::Down);
×
370
        }
371
    }
372
}
373

374
void ScriptEditor::slotAddSystemCommand()
×
375
{
376
    QString fn = getFilePath();
×
377
    if (fn.isEmpty())
×
378
        return;
×
379

380
    QFileInfo fInfo(fn);
×
381
#if !defined(WIN32) && !defined(Q_OS_WIN)
382
    if (fInfo.isExecutable() == false)
×
383
    {
384
        QMessageBox::warning(this, tr("Invalid executable"), tr("Please select an executable file!"));
×
385
        return;
×
386
    }
387
#endif
388
    m_lastUsedPath = fInfo.absolutePath();
×
389

390
    QString args = QInputDialog::getText(this, tr("Enter the program arguments (leave empty if not required)"), "",
×
391
                                        QLineEdit::Normal, QString());
×
392

393
    QStringList argsList = args.split(" ");
×
394
    QString formattedArgs;
×
NEW
395
    foreach (QString arg, argsList)
×
396
    {
397
        formattedArgs.append(QString("arg:%1 ").arg(arg));
×
398
    }
399

400
    m_editor->moveCursor(QTextCursor::StartOfLine);
×
401
    m_editor->textCursor().insertText(QString("%1:%2 %3\n")
×
402
                                        .arg(Script::systemCmd)
×
403
                                        .arg(fn).arg(formattedArgs));
×
404
    m_editor->moveCursor(QTextCursor::Down);
×
405
}
406

407
void ScriptEditor::slotAddComment()
×
408
{
409
    bool ok = false;
×
410
    QString str = QInputDialog::getText(this, tr("Add Comment"), "",
×
411
                                        QLineEdit::Normal, QString(), &ok);
×
412
    if (ok == true)
×
413
    {
414
        //m_editor->moveCursor(QTextCursor::StartOfLine);
415
        m_editor->textCursor().insertText(QString("// %1").arg(str));
×
416
        //m_editor->moveCursor(QTextCursor::EndOfLine);
417
    }
418
}
×
419

420
void ScriptEditor::slotAddRandom()
×
421
{
422
    QDialog dialog(this);
×
423
    // Use a layout allowing to have a label next to each field
424
    QFormLayout dLayout(&dialog);
×
425

426
    dLayout.addRow(new QLabel(tr("Enter the range for the randomization")));
×
427

428
    QSpinBox *minSB = new QSpinBox(this);
×
429
    minSB->setRange(0, 999);
×
430
    QSpinBox *maxSB = new QSpinBox(this);
×
431
    maxSB->setRange(0, 999);
×
432
    maxSB->setValue(255);
×
433
    dLayout.addRow(tr("Minimum value"), minSB);
×
434
    dLayout.addRow(tr("Maximum value"), maxSB);
×
435

436
    // Add some standard buttons (Cancel/Ok) at the bottom of the dialog
437
    QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
438
                               Qt::Horizontal, &dialog);
×
439
    dLayout.addRow(&buttonBox);
×
440
    QObject::connect(&buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept()));
×
441
    QObject::connect(&buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject()));
×
442

443
    // Show the dialog as modal
444
    if (dialog.exec() == QDialog::Accepted)
×
445
    {
446
        m_editor->moveCursor(QTextCursor::StartOfLine);
×
447
        m_editor->textCursor().insertText(QString("random(%1,%2)")
×
448
                              .arg(minSB->value()).arg(maxSB->value()));
×
449
        m_editor->moveCursor(QTextCursor::EndOfLine);
×
450
    }
451
}
×
452

453
void ScriptEditor::slotAddFilePath()
×
454
{
455
    QString fn = getFilePath();
×
456
    if (fn.isEmpty())
×
457
        return;
×
458

459
    QFileInfo fInfo(fn);
×
460
    m_lastUsedPath = fInfo.absolutePath();
×
461

462
    //m_editor->textCursor().insertText(QUrl::toPercentEncoding(fn));
463
    m_editor->textCursor().insertText(fn);
×
464
}
465

466
void ScriptEditor::slotCheckSyntax()
×
467
{
468
    QString errResult;
×
469
    QString scriptText = m_document->toPlainText();
×
470
    m_script->setData(scriptText);
×
471
    QList<int> errLines = m_script->syntaxErrorsLines();
×
472
    if (errLines.isEmpty())
×
473
    {
474
        errResult.append(tr("No syntax errors found in the script"));
×
475
    }
476
    else
477
    {
478
        QStringList lines = scriptText.split(QRegularExpression("(\\r\\n|\\n\\r|\\r|\\n)"));
×
NEW
479
        foreach (int line, errLines)
×
480
        {
481
            errResult.append(tr("Syntax error at line %1:\n%2\n\n").arg(line).arg(lines.at(line - 1)));
×
482
        }
483
    }
484
    QMessageBox::information(this, tr("Script check results"), errResult);
×
485
}
×
486

487
void ScriptEditor::slotTestRun()
×
488
{
489
    if (m_testPlayButton->isChecked() == true)
×
490
        m_script->start(m_doc->masterTimer(), functionParent());
×
491
    else
492
        m_script->stopAndWait();
×
493
}
×
494

495
FunctionParent ScriptEditor::functionParent() const
×
496
{
497
    return FunctionParent::master();
×
498
}
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