mainwindow.cpp 23.3 KB
Newer Older
jhammen's avatar
jhammen committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
 * This file is part of Bipscript-IDE.
 *
 * Bipscript-IDE is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Bipscript-IDE is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Bipscript-IDE.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "mainwindow.h"

#include "editorwidget.h"
#include "projectsdialog.h"
21
22
#include "scriptrun.h"
#include "ui_mainwindow.h"
jhammen's avatar
jhammen committed
23
24
25
#include "version.h"

#include <QDebug>
26
27
#include <QDesktopServices>
#include <QDir>
jhammen's avatar
jhammen committed
28
29
30
#include <QFileDialog>
#include <QInputDialog>
#include <QLabel>
31
#include <QTime>
jhammen's avatar
jhammen committed
32

33
34
35
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), audioEngine(AudioEngine::instance()), transportPosition(0),
      ui(new Ui::MainWindow), timeWidget(this), bbtWidget(this) {
jhammen's avatar
jhammen committed
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
    ui->setupUi(this);

    // time in MM:SS + BBT + engine config
    QString labelStyle("QLabel {font-family: monospace; color: green }");
    timeWidget.setFrameStyle(QFrame::Panel | QFrame::Sunken);
    timeWidget.setStyleSheet(labelStyle);
    timeWidget.setText("00:00.000");
    ui->transportToolBar->addWidget(&timeWidget);
    bbtWidget.setFrameStyle(QFrame::Panel | QFrame::Sunken);
    bbtWidget.setStyleSheet(labelStyle);
    bbtWidget.setText("--|--|----");
    ui->transportToolBar->addWidget(&bbtWidget);
    engineWidget.setFrameStyle(QFrame::Panel | QFrame::Sunken);
    engineWidget.setStyleSheet("QLabel {font-family: monospace; color: blue }");
    engineWidget.setText("----- Hz");
    ui->transportToolBar->addWidget(&engineWidget);

jhammen's avatar
jhammen committed
53
54
55
    // output doc starts hidden
    ui->outputDockWidget->setHidden(true);

jhammen's avatar
jhammen committed
56
    ui->folderTree->setColumnCount(1);
jhammen's avatar
jhammen committed
57
58
59
60
    //
    searchWidget = new SearchWidget();
    ui->leftTabWidget->addTab(searchWidget, "Search");
    connect(searchWidget, SIGNAL(searchCurrent(QString)), this, SLOT(searchCurrent(QString)));
jhammen's avatar
jhammen committed
61
62
63

    ProjectList &projectList = ProjectList::getProjectList();
    projectList.load();
64
    if (projectList.currentExists()) {
jhammen's avatar
jhammen committed
65
66
67
68
69
70
71
72
        loadProject();
    } else {
        on_actionOpenProject_triggered();
    }

    connect(ui->folderTree, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this,
            SLOT(folderTreeClicked(QTreeWidgetItem *, int)));

73
    connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));
jhammen's avatar
jhammen committed
74

75
76
    connect(ui->tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeEditorTab(int)));
    connect(ui->outputTabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeOutputTab(int)));
jhammen's avatar
jhammen committed
77
78
}

79
MainWindow::~MainWindow() { delete ui; }
jhammen's avatar
jhammen committed
80
81
82

// ------------ superclass methods

83
void MainWindow::closeEvent(QCloseEvent *event) {
jhammen's avatar
jhammen committed
84
85
86
    // save settings
    ProjectList::getProjectList().save();
    // close tabs
87
    if (!saveAllTabs()) {
jhammen's avatar
jhammen committed
88
89
90
        event->ignore();
    }
    // kill running scripts
91
    if (!killAllScripts()) {
jhammen's avatar
jhammen committed
92
93
94
95
96
97
        event->ignore();
    }
}

// ------------ slots

98
void MainWindow::changeProject() {
jhammen's avatar
jhammen committed
99
100
101
    ui->folderTree->clear();
    loadProject();
    // close all edit tabs not still applicable
102
    for (int i = ui->tabWidget->count() - 1; i >= 0; i--) {
jhammen's avatar
jhammen committed
103
        QWidget *widget = ui->tabWidget->widget(i);
104
105
        EditorWidget *editor = static_cast<EditorWidget *>(widget);
        if (findTreeItem(editor->path()) == nullptr) {
jhammen's avatar
jhammen committed
106
107
108
109
110
111
            ui->tabWidget->removeTab(i);
        }
    }
    selectedFolder = nullptr;
}

112
113
114
void MainWindow::closeEditorTab(int index) {
    EditorWidget *editor = qobject_cast<EditorWidget *>(ui->tabWidget->widget(index));
    if (saveOrAbandon(editor)) {
jhammen's avatar
jhammen committed
115
116
117
118
        delete editor;
    }
}

119
120
void MainWindow::closeOutputTab(int index) {
    OutputWidget *output = qobject_cast<OutputWidget *>(ui->outputTabWidget->widget(index));
jhammen's avatar
jhammen committed
121
    QString name = ui->outputTabWidget->tabText(index);
122
    if (killOrDetach(output, name)) {
jhammen's avatar
jhammen committed
123
124
125
126
        delete output;
    }
}

127
void MainWindow::codeError(const QString &path, int line) {
jhammen's avatar
jhammen committed
128
    EditorWidget *editor = findEditor(path);
129
    if (editor) {
jhammen's avatar
jhammen committed
130
131
132
        editor->addError(line);
    }
}
133
void MainWindow::engineDisconnect() {
jhammen's avatar
jhammen committed
134
135
136
137
138
139
140
141
142
    ui->actionStartStopTransport->setChecked(false);
    enableTransportControls(false);
    audioEngine.disconnect();
    timeWidget.setText("00:00.000");
    bbtWidget.setText("--|--|----");
    ui->actionJackConnect->setChecked(false);
    ui->actionJackConnect->setEnabled(true);
}

143
void MainWindow::createOrOpenEditor(NamedFile &file) {
jhammen's avatar
jhammen committed
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
    EditorWidget *editor = findEditor(file.path);
    if (editor) {
        ui->tabWidget->setCurrentWidget(editor);
    } else {
        // new editor for this file
        editor = new EditorWidget(file);
        ui->tabWidget->addTab(editor, file.name);
        ui->tabWidget->setCurrentWidget(editor);
        editor->load();
        connect(editor->document(), SIGNAL(modificationChanged(bool)), this, SLOT(markDirty(bool)));
        connect(editor, SIGNAL(undoAvailable(bool)), ui->actionUndo, SLOT(setEnabled(bool)));
        connect(editor, SIGNAL(redoAvailable(bool)), ui->actionRedo, SLOT(setEnabled(bool)));
        connect(editor, SIGNAL(copyAvailable(bool)), ui->actionCut, SLOT(setEnabled(bool)));
        connect(editor, SIGNAL(copyAvailable(bool)), ui->actionCopy, SLOT(setEnabled(bool)));
    }
    selectedFolder = nullptr;
    enableFileActions(editor);
}

163
void MainWindow::createOrOpenEditor(QString &path) {
jhammen's avatar
jhammen committed
164
165
166
167
168
    QFileInfo fileInfo(path);
    NamedFile file(fileInfo.fileName(), fileInfo.filePath());
    createOrOpenEditor(file);
}

169
170
EditorWidget *MainWindow::currentEditor() {
    return static_cast<EditorWidget *>(ui->tabWidget->currentWidget());
jhammen's avatar
jhammen committed
171
172
}

173
174
void MainWindow::folderTreeClicked(QTreeWidgetItem *item, int) {
    NamedFile &file = static_cast<FolderTreeItem *>(item)->file();
jhammen's avatar
jhammen committed
175
    QFileInfo fileInfo(file.path);
176
    if (fileInfo.isDir()) {
jhammen's avatar
jhammen committed
177
178
        selectedFolder = &(file.path);
        enableFileActions(nullptr);
179
    } else if (fileInfo.isFile()) {
jhammen's avatar
jhammen committed
180
        createOrOpenEditor(file);
181
182
    } else {
        QMessageBox::information(this, "Unknown Entry", file.path);
jhammen's avatar
jhammen committed
183
184
185
    }
}

186
void MainWindow::handleLink(const QUrl &url) {
jhammen's avatar
jhammen committed
187
188
    QString path(url.path());
    createOrOpenEditor(path);
189
    if (url.hasQuery()) {
jhammen's avatar
jhammen committed
190
191
192
193
194
195
196
        int column = QString(url.query()).toInt();
        currentEditor()->focusLine(url.port(), column);
    } else {
        currentEditor()->focusLine(url.port());
    }
}

197
void MainWindow::markDirty(bool value) {
jhammen's avatar
jhammen committed
198
    QString tabText(currentEditor()->name());
199
200
201
    if (value) {
        tabText.append("*");
    }
jhammen's avatar
jhammen committed
202
203
204
205
206
207
    int index = ui->tabWidget->currentIndex();
    ui->tabWidget->setTabText(index, tabText);
    ui->actionSaveFile->setEnabled(value);
    setWindowModified(value);
}

jhammen's avatar
jhammen committed
208
209
210
211
212
213
214
215
void MainWindow::searchCurrent(const QString &term) {
    EditorWidget *editor = currentEditor();
    if (editor) {
        uint32_t results = editor->searchText(term);
        searchWidget->results(results);
    }
}

216
void MainWindow::showTime(Position pos) {
jhammen's avatar
jhammen committed
217
218
219
    QTime qtime(0, 0, 0);
    qtime = qtime.addMSecs(pos.msec);
    timeWidget.setText(qtime.toString("mm:ss.zzz"));
220
    bbtWidget.setText(QString("%1|%2|%3").arg(pos.bar, 2).arg(pos.beat, 2).arg(pos.tick, 4));
jhammen's avatar
jhammen committed
221
222
223
    transportPosition = pos.frame;
}

224
225
226
void MainWindow::tabChanged(int i) {
    EditorWidget *editor = qobject_cast<EditorWidget *>(ui->tabWidget->widget(i));
    if (editor == nullptr) { // all tabs closed
jhammen's avatar
jhammen committed
227
228
229
230
231
        ui->folderTree->clearSelection();
        setWindowTitle(QCoreApplication::applicationName());
        enableFileActions(nullptr);
    } else {
        QTreeWidgetItem *item = findTreeItem(editor->path());
232
        if (item) {
jhammen's avatar
jhammen committed
233
234
235
236
237
238
239
240
241
242
            ui->folderTree->setCurrentItem(item);
        } else {
            ui->folderTree->clearSelection();
        }
        setWindowTitle(QString("%1[*]").arg(editor->name()));
        setWindowModified(editor->isDirty());
        enableFileActions(editor);
    }
}

243
244
void MainWindow::closeOtherEditors() {
    if (QAction *action = qobject_cast<QAction *>(sender())) {
245
        int index = action->data().toInt();
246
247
248
249
        for (int i = ui->tabWidget->count() - 1; i >= 0; i--) {
            if (i != index) {
                closeEditorTab(i);
            }
250
251
252
253
        }
    }
}

jhammen's avatar
jhammen committed
254
255
// ------------ auto slots

256
void MainWindow::on_actionAbout_triggered() {
jhammen's avatar
jhammen committed
257
    QString lang = QLocale::languageToString(QLocale().language());
258
259
    QString message =
        tr("Bipscript IDE %1\n%2\nQT %3").arg(BIPSCRIPT_IDE_VERSION).arg(lang).arg(QT_VERSION_STR);
jhammen's avatar
jhammen committed
260
261
262
    QMessageBox::about(this, "Bipscript-IDE", message);
}

263
void MainWindow::on_actionAPIDocumentation_triggered() {
jhammen's avatar
jhammen committed
264
265
    QStringList supported = {"en", "es"};
    QString lang = QLocale().name().left(2);
266
267
268
    if (supported.contains(lang)) {
        lang = supported[0];
    }
jhammen's avatar
jhammen committed
269
270
271
272
    QString link = QString("http://www.bipscript.org/%1/api/").arg(lang);
    QDesktopServices::openUrl(QUrl(link));
}

273
void MainWindow::on_actionCloseFile_triggered() { closeEditorTab(ui->tabWidget->currentIndex()); }
jhammen's avatar
jhammen committed
274

275
void MainWindow::on_actionComment_triggered() { currentEditor()->commentSelection(); }
jhammen's avatar
jhammen committed
276

277
void MainWindow::on_actionCopy_triggered() { currentEditor()->copy(); }
jhammen's avatar
jhammen committed
278

279
void MainWindow::on_actionCut_triggered() { currentEditor()->cut(); }
jhammen's avatar
jhammen committed
280

281
void MainWindow::on_actionNewFile_triggered() {
jhammen's avatar
jhammen committed
282
283
284
285
    QString message("New file in ");
    message.append(*selectedFolder);
    bool ok;
    QString name = QInputDialog::getText(this, "New File", message, QLineEdit::Normal, "", &ok);
286
    if (name.length() > 0) {
jhammen's avatar
jhammen committed
287
288
289
290
        // create file
        QFileInfo fileInfo(*selectedFolder + "/" + name);
        QString path(fileInfo.filePath());
        QFile file(path);
291
        if (file.exists()) {
jhammen's avatar
jhammen committed
292
293
294
295
296
297
298
299
300
301
302
303
304
305
            QMessageBox::warning(this, "File Exists", path + " already exists");
        } else {
            file.open(QIODevice::WriteOnly);
            file.close();
            // open in tree
            ui->folderTree->clear();
            loadProject();
        }
        QTreeWidgetItem *item = findTreeItem(path);
        ui->folderTree->setCurrentItem(item);
        createOrOpenEditor(path);
    }
}

306
void MainWindow::on_actionDeleteFile_triggered() {
jhammen's avatar
jhammen committed
307
308
309
310
311
312
313
    QMessageBox msgBox;
    QString path(currentEditor()->path());
    msgBox.setText("Delete " + path);
    msgBox.setInformativeText("Do you want to delete " + currentEditor()->name() + "?");
    msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
    msgBox.setDefaultButton(QMessageBox::Cancel);
    int choice = msgBox.exec();
314
    if (choice == QMessageBox::Ok) {
jhammen's avatar
jhammen committed
315
        // remove from tree
316
        FolderTreeItem *item = static_cast<FolderTreeItem *>(findTreeItem(path));
jhammen's avatar
jhammen committed
317
318
319
320
321
322
323
324
        item->parent()->removeChild(item);
        // remove editor if any
        delete findEditor(path);
        // remove file from filesystem
        QFile::remove(path);
    }
}

325
void MainWindow::on_actionExamples_triggered() {
jhammen's avatar
jhammen committed
326
327
    QRegExp versionExp = QRegExp("v([0-9\\.]+)");
    QString versionString = QString(BIPSCRIPT_IDE_VERSION);
328
    if (versionExp.indexIn(versionString) == 0) {
jhammen's avatar
jhammen committed
329
330
331
332
333
334
335
336
337
338
        QString version = versionExp.cap(1);
        QString link("https://github.com/bipscript/examples/releases/tag/v");
        link.append(version);
        QDesktopServices::openUrl(QUrl(link));
    } else {
        QString message = tr("No examples for version %1").arg(versionString);
        QMessageBox::warning(this, tr("Unknown Version"), message, QMessageBox::Ok);
    }
}

339
void MainWindow::on_actionExit_triggered() { QApplication::closeAllWindows(); }
jhammen's avatar
jhammen committed
340

341
void MainWindow::on_actionFastForward_triggered() {
jhammen's avatar
jhammen committed
342
343
344
345
346
347
348
349
350
    transportPosition = transportPosition + audioConfig.framesPerSecond;
    audioEngine.reposition(transportPosition);
}

/**
 * @brief MainWindow::on_actionRun_triggered
 *
 * action to run script
 */
351
void MainWindow::on_actionRun_triggered() {
jhammen's avatar
jhammen committed
352
    // check if possible
353
    if (!ScriptRun::possible()) {
jhammen's avatar
jhammen committed
354
355
356
357
358
359
360
361
362
363
364
365
        QMessageBox::warning(this, "Executable", "Cannot launch bipscript executable");
        return;
    }
    ScriptRun *run = new ScriptRun(currentEditor()->path());
    // add output widget for this run
    OutputWidget *out = new OutputWidget();
    ui->outputTabWidget->addTab(out, currentEditor()->name());
    ui->outputTabWidget->setCurrentWidget(out);
    connect(run, SIGNAL(started(int)), out, SLOT(running(int)));
    connect(run, SIGNAL(readStdout(QString)), out, SLOT(write(QString)));
    connect(run, SIGNAL(readStderr(QString)), out, SLOT(writeError(QString)));
    connect(run, SIGNAL(error(QString)), out, SLOT(writeError(QString)));
366
367
368
369
    connect(run, SIGNAL(exited(int)), out, SLOT(completed(int)));
    connect(out, SIGNAL(codeError(const QString &, int)), this,
            SLOT(codeError(const QString &, int)));
    connect(out, SIGNAL(linkClicked(const QUrl &)), this, SLOT(handleLink(const QUrl &)));
jhammen's avatar
jhammen committed
370
371
372
373
374
    // show output area if hidden
    ui->outputDockWidget->show();
    run->start();
}

375
void MainWindow::enableTransportControls(bool enabled) {
jhammen's avatar
jhammen committed
376
377
378
379
380
381
    ui->actionStartStopTransport->setEnabled(enabled);
    ui->actionRewind->setEnabled(enabled);
    ui->actionRestart->setEnabled(enabled);
    ui->actionFastForward->setEnabled(enabled);
}

382
383
void MainWindow::on_actionJackConnect_toggled(bool connected) {
    if (connected) {
jhammen's avatar
jhammen committed
384
385
386
        audioConfig = audioEngine.connect();
        atimer = audioEngine.timerService();
        connect(atimer, SIGNAL(transport(Position)), this, SLOT(showTime(Position)));
387
388
        connect(atimer, SIGNAL(rolling(bool)), ui->actionStartStopTransport,
                SLOT(setChecked(bool)));
jhammen's avatar
jhammen committed
389
390
391
392
393
394
395
396
397
398
        connect(atimer, SIGNAL(finished()), this, SLOT(engineDisconnect()));
        engineWidget.setText(QString("%1 Hz").arg(audioConfig.framesPerSecond));
    } else {
        ui->actionJackConnect->setEnabled(false);
        atimer->stop();
        engineWidget.setText("----- Hz");
    }
    enableTransportControls(connected && !ui->actionTransportLock->isChecked());
}

399
void MainWindow::on_actionOpenFile_triggered() {
jhammen's avatar
jhammen committed
400
401
402
403
    ProjectList &projectList = ProjectList::getProjectList();
    QFileDialog fileDialog(this);
    fileDialog.setFileMode(QFileDialog::AnyFile);
    fileDialog.setDirectory(projectList.commonRoot());
404
    if (fileDialog.exec()) {
jhammen's avatar
jhammen committed
405
406
407
408
409
        QString path = fileDialog.selectedFiles()[0];
        createOrOpenEditor(path);
    }
}

410
411
void MainWindow::on_actionOpenProject_triggered() {
    if (saveAllTabs()) {
jhammen's avatar
jhammen committed
412
413
414
415
416
417
        ProjectsDialog projectsDialog(this);
        connect(&projectsDialog, SIGNAL(projectChanged()), this, SLOT(changeProject()));
        projectsDialog.exec();
    }
}

418
void MainWindow::on_actionPaste_triggered() { currentEditor()->paste(); }
jhammen's avatar
jhammen committed
419

420
void MainWindow::on_actionProjectOptions_triggered() {
jhammen's avatar
jhammen committed
421
422
423
424
425
426
    ProjectList &projects = ProjectList::getProjectList();
    Project &project = projects.currentProject();
    QString value = project.executable();
    QString message("Executable Path");
    bool ok;
    QString name = QInputDialog::getText(this, "Options", message, QLineEdit::Normal, value, &ok);
427
    if (name.length() > 0) {
jhammen's avatar
jhammen committed
428
429
430
431
432
433
        // TODO: test existance etc.
        project.executable(name);
        ScriptRun::setExecutable(project.executable());
    }
}

434
void MainWindow::on_actionRedo_triggered() { currentEditor()->redo(); }
jhammen's avatar
jhammen committed
435

436
437
void MainWindow::on_actionRenameFile_triggered() {
    if (saveOrAbandon(currentEditor())) {
jhammen's avatar
jhammen committed
438
439
440
        QString message("New name");
        QString oldPath = currentEditor()->path();
        bool ok;
441
442
443
        QString path =
            QInputDialog::getText(this, "Rename", message, QLineEdit::Normal, oldPath, &ok);
        if (path.length() > 0) {
jhammen's avatar
jhammen committed
444
            QFileInfo fileInfo(path);
445
            if (fileInfo.exists()) {
jhammen's avatar
jhammen committed
446
447
448
449
450
                QString message = QString("Cannot move file, target exists:\n%1").arg(path);
                QMessageBox::warning(this, "File exists", message);
                return;
            }
            QFile oldFile(oldPath);
451
            if (!oldFile.rename(fileInfo.filePath())) {
jhammen's avatar
jhammen committed
452
453
454
455
456
457
458
459
460
461
                QString message = QString("Error on move\n %1 ->\n %2").arg(oldPath).arg(path);
                QMessageBox::warning(this, "File Move Error", message);
                return;
            }
            // success
            NamedFile file;
            file.name = fileInfo.fileName();
            file.path = fileInfo.filePath();
            // remove old from tree
            QTreeWidgetItem *item = findTreeItem(oldPath);
462
            if (item) {
jhammen's avatar
jhammen committed
463
464
465
466
467
                item->parent()->removeChild(item);
            }
            // add new to tree if possible
            QTreeWidgetItem *parent = findTreeItem(fileInfo.path());
            ui->folderTree->clearSelection();
468
469
470
            if (parent) {
                FolderTreeItem *newItem =
                    new FolderTreeItem(parent, fileInfo.fileName(), fileInfo.filePath());
jhammen's avatar
jhammen committed
471
472
473
474
475
476
                ui->folderTree->setCurrentItem(newItem);
            }
            // update editor + tab
            currentEditor()->moveFile(file);
            int index = ui->tabWidget->currentIndex();
            QString tabText(currentEditor()->name());
477
478
479
            if (currentEditor()->isDirty()) {
                tabText.append("*");
            }
jhammen's avatar
jhammen committed
480
481
482
483
484
485
486
            ui->tabWidget->setTabText(index, tabText);
            // update main window title
            setWindowTitle(QString("%1[*]").arg(file.name));
        }
    }
}

487
void MainWindow::on_actionRestart_triggered() { audioEngine.reposition(0); }
jhammen's avatar
jhammen committed
488

489
void MainWindow::on_actionRewind_triggered() {
jhammen's avatar
jhammen committed
490
491
492
493
494
    int newpos = static_cast<int>(transportPosition - audioConfig.framesPerSecond);
    transportPosition = static_cast<uint>(qMax(newpos, 0));
    audioEngine.reposition(transportPosition);
}

495
void MainWindow::on_actionSaveFile_triggered() { currentEditor()->save(); }
jhammen's avatar
jhammen committed
496

jhammen's avatar
jhammen committed
497
498
499
500
501
void MainWindow::on_actionFocusFind_triggered() {
    searchWidget->newFileSearch();
    ui->leftTabWidget->setCurrentWidget(searchWidget);
}

502
503
void MainWindow::on_actionStartStopTransport_toggled(bool start) {
    if (start) {
jhammen's avatar
jhammen committed
504
505
506
507
508
509
510
        audioEngine.transportStart();
    } else {
        audioEngine.transportStop();
    }
    ui->actionJackConnect->setEnabled(!start);
}

511
void MainWindow::on_actionTransportLock_toggled(bool locked) {
jhammen's avatar
jhammen committed
512
513
514
515
516
517
518
    bool enabled = !locked && ui->actionJackConnect->isChecked();
    ui->actionStartStopTransport->setEnabled(enabled);
    ui->actionRewind->setEnabled(enabled);
    ui->actionRestart->setEnabled(enabled);
    ui->actionFastForward->setEnabled(enabled);
}

519
void MainWindow::on_actionUndo_triggered() { currentEditor()->undo(); }
jhammen's avatar
jhammen committed
520

521
void MainWindow::on_tabWidget_customContextMenuRequested(const QPoint &pos) {
522
523
524
525
526
527
528
    QMenu menu;
    QAction *action = menu.addAction(tr("Close Other Tabs"), this, SLOT(closeOtherEditors()));
    action->setData(ui->tabWidget->tabBar()->tabAt(pos));
    action->setEnabled(ui->tabWidget->count() > 1);
    menu.exec(QCursor::pos());
}

jhammen's avatar
jhammen committed
529
530
// ------------ private methods

531
void MainWindow::enableFileActions(EditorWidget *editor) {
jhammen's avatar
jhammen committed
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
    // requires file selected
    ui->actionDeleteFile->setEnabled(editor);
    ui->actionRun->setEnabled(editor);
    ui->actionPaste->setEnabled(editor);
    ui->actionCloseFile->setEnabled(editor);
    ui->actionComment->setEnabled(editor);
    ui->actionRenameFile->setEnabled(editor);
    // requires directory selected
    ui->actionNewFile->setEnabled(selectedFolder);
    // requires state from the editor
    ui->actionSaveFile->setEnabled(editor && editor->isDirty());
    ui->actionUndo->setEnabled(editor && editor->isUndoAvailable());
    ui->actionRedo->setEnabled(editor && editor->isRedoAvailable());
    ui->actionCut->setEnabled(editor && editor->hasSelection());
    ui->actionCopy->setEnabled(editor && editor->hasSelection());
}

549
550
551
552
553
554
EditorWidget *MainWindow::findEditor(const QString &path) {
    for (int i = 0; i < ui->tabWidget->count(); i++) {
        EditorWidget *editor = qobject_cast<EditorWidget *>(ui->tabWidget->widget(i));
        if (editor->path() == path) {
            return editor;
        }
jhammen's avatar
jhammen committed
555
556
557
558
    }
    return nullptr;
}

559
QTreeWidgetItem *MainWindow::findTreeItem(const QString &path) {
jhammen's avatar
jhammen committed
560
561
    QTreeWidgetItemIterator it(ui->folderTree);
    while (*it) {
562
563
564
565
        FolderTreeItem *item = static_cast<FolderTreeItem *>(*it);
        if (item->file().path == path) {
            return item;
        }
jhammen's avatar
jhammen committed
566
567
568
569
570
        ++it;
    }
    return nullptr;
}

571
void MainWindow::loadProject() {
jhammen's avatar
jhammen committed
572
573
574
575
576
577
578
579
580
    ProjectList &projects = ProjectList::getProjectList();
    Project &project = projects.currentProject();
    ScriptRun::setExecutable(project.executable());
    for (int i = 0; i < project.folderCount(); ++i) {
        NamedFile &folder = project.folder(i);
        ui->folderTree->addTopLevelItem(toTreeFolder(folder));
    }
}

581
582
583
584
bool MainWindow::saveAllTabs() {
    for (int i = 0; i < ui->tabWidget->count(); i++) {
        EditorWidget *editor = static_cast<EditorWidget *>(ui->tabWidget->widget(i));
        if (editor->isDirty()) {
jhammen's avatar
jhammen committed
585
            QMessageBox::StandardButton choice = unsavedChangesDialog(editor->path());
586
            if (choice == QMessageBox::Cancel) {
jhammen's avatar
jhammen committed
587
                return false;
588
            } else if (choice == QMessageBox::Save) {
jhammen's avatar
jhammen committed
589
590
591
592
593
594
595
                editor->save();
            }
        }
    }
    return true;
}

596
597
598
599
bool MainWindow::saveOrAbandon(EditorWidget *editor) {
    if (editor->isDirty()) {
        QMessageBox::StandardButton choice = unsavedChangesDialog(editor->path());
        if (choice == QMessageBox::Cancel) {
jhammen's avatar
jhammen committed
600
            return false;
601
602
        } else if (choice == QMessageBox::Save) {
            if (!editor->save()) {
jhammen's avatar
jhammen committed
603
604
605
606
607
608
609
                return false;
            }
        }
    }
    return true;
}

610
611
612
bool MainWindow::killAllScripts() {
    for (int i = 0; i < ui->outputTabWidget->count(); i++) {
        OutputWidget *output = qobject_cast<OutputWidget *>(ui->outputTabWidget->widget(i));
jhammen's avatar
jhammen committed
613
        QString name = ui->outputTabWidget->tabText(i);
614
        if (!killOrDetach(output, name)) {
jhammen's avatar
jhammen committed
615
616
617
618
619
620
            return false;
        }
    }
    return true;
}

621
622
bool MainWindow::killOrDetach(OutputWidget *output, const QString &name) {
    if (output->pid() != 0) {
jhammen's avatar
jhammen committed
623
624
        QString id = QString("%1 pid %2").arg(name).arg(output->pid());
        QString mesg = "%1 is still running.\nDo you wish to kill the process?";
625
626
627
628
        QMessageBox::StandardButton choice =
            QMessageBox::warning(this, tr("Running Process"), mesg.arg(id),
                                 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
        if (choice == QMessageBox::Cancel) {
jhammen's avatar
jhammen committed
629
            return false;
630
        } else if (choice == QMessageBox::Yes) {
jhammen's avatar
jhammen committed
631
632
            output->killScript(true);
        }
633
634
    }
    return true;
jhammen's avatar
jhammen committed
635
636
}

637
FolderTreeItem *MainWindow::toTreeFolder(NamedFile &folder) {
jhammen's avatar
jhammen committed
638
639
640
641
    FolderTreeItem *folderItem = new FolderTreeItem(nullptr, folder.name, folder.path);
    QDir dir(folder.path);
    dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
    foreach (QFileInfo fileInfo, dir.entryInfoList()) {
642
643
644
645
        if (fileInfo.isFile()) {
            folderItem->addChild(
                new FolderTreeItem(folderItem, fileInfo.fileName(), fileInfo.filePath()));
        } else if (fileInfo.isDir()) {
jhammen's avatar
jhammen committed
646
647
648
649
650
651
652
            NamedFile childFolder(fileInfo.baseName(), fileInfo.filePath());
            folderItem->addChild(toTreeFolder(childFolder));
        }
    }
    return folderItem;
}

653
654
655
QMessageBox::StandardButton MainWindow::unsavedChangesDialog(const QString &path) {
    return QMessageBox::warning(this, tr("Unsaved Changes"),
                                tr("%1 has unsaved changes.\nDo you wish to save?").arg(path),
jhammen's avatar
jhammen committed
656
657
658
                                QMessageBox::Save | QMessageBox::No | QMessageBox::Cancel);
}

659
void MainWindow::on_actionWebsite_triggered() {
jhammen's avatar
jhammen committed
660
661
662
    QString link = "http://www.bipscript.org";
    QDesktopServices::openUrl(QUrl(link));
}