<commit_before>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <QDesktopServices>
#include <QFileDialog>
#include <QUrl>

#include "ty.h"
#include "about_dialog.hh"
#include "board_widget.hh"
#include "main_window.hh"

using namespace std;

MainWindow::MainWindow(BoardManagerProxy *manager, QWidget *parent)
    : QMainWindow(parent), manager_(manager)
{
    setupUi(this);

    disableBoardWidgets();
    monitorText->setWordWrapMode(QTextOption::WrapAnywhere);

    // Errors may be thrown from a worker thread, so use signals to queue them on the GUI thread
    ty_error_redirect([](ty_err err, const char *msg, void *udata) {
        TY_UNUSED(err);

        MainWindow *main = static_cast<MainWindow *>(udata);
        emit main->errorMessage(msg);
    }, this);
    connect(this, &MainWindow::errorMessage, this, &MainWindow::showErrorMessage);

    boardList->setModel(manager);
    boardList->setItemDelegate(new BoardItemDelegate(manager));

    connect(boardList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::selectionChanged);
    connect(manager, &BoardManagerProxy::boardAdded, this, &MainWindow::setBoardDefaults);

    connect(monitorText, &QPlainTextEdit::textChanged, this, &MainWindow::monitorTextChanged);

    for (auto &board: *manager)
        setBoardDefaults(board);
}

MainWindow::~MainWindow()
{
    ty_error_redirect(nullptr, nullptr);
}

QString MainWindow::lastError() const
{
    return last_error_;
}

void MainWindow::disableBoardWidgets()
{
    setWindowTitle("TyQt");

    infoTab->setEnabled(false);
    modelText->clear();
    locationText->clear();
    serialText->clear();
    interfaceTree->clear();

    monitorTab->setEnabled(false);
    monitorEdit->setEnabled(false);

    actionUpload->setEnabled(false);
    actionUploadNew->setEnabled(false);
    uploadTab->setEnabled(false);
    firmwarePath->clear();

    actionReset->setEnabled(false);
    actionReboot->setEnabled(false);
}

QString MainWindow::browseForFirmware()
{
    if (!current_board_)
        return QString();

    QString filename = QFileDialog::getOpenFileName(this, tr("Open Firmware"), QString(),
                                                    tr("Binary Files (*.elf *.hex);;All Files (*)"));
    if (filename.isEmpty())
        return QString();

    firmwarePath->setText(filename);
    emit firmwarePath->editingFinished();

    return filename;
}

void MainWindow::uploadCurrentFirmware()
{
    if (!current_board_)
        return;

    current_board_->upload(current_board_->property("firmware").toString(),
                           current_board_->property("resetAfter").toBool());
}

void MainWindow::setBoardDefaults(shared_ptr<BoardProxy> board)
{
    board->setProperty("resetAfter", true);

    if (!boardList->currentIndex().isValid() && manager_->boardCount())
        boardList->setCurrentIndex(manager_->index(0, 0));
}

void MainWindow::selectionChanged(const QItemSelection &selected, const QItemSelection &previous)
{
    TY_UNUSED(previous);

    if (current_board_)
        current_board_->disconnect(this);

    if (selected.indexes().isEmpty()) {
        current_board_ = nullptr;

        disableBoardWidgets();
        monitorText->setDocument(nullptr);

        return;
    }

    current_board_ = manager_->board(selected.indexes().front().row());

    firmwarePath->setText(current_board_->property("firmware").toString());
    resetAfterUpload->setChecked(current_board_->property("resetAfter").toBool());

    monitorText->setDocument(&current_board_->serialDocument());

    refreshBoardInfo();<commit_msg>Drop BoardProxy's serial QTextDocument before releasing the board<commit_after>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <QDesktopServices>
#include <QFileDialog>
#include <QUrl>

#include "ty.h"
#include "about_dialog.hh"
#include "board_widget.hh"
#include "main_window.hh"

using namespace std;

MainWindow::MainWindow(BoardManagerProxy *manager, QWidget *parent)
    : QMainWindow(parent), manager_(manager)
{
    setupUi(this);

    disableBoardWidgets();
    monitorText->setWordWrapMode(QTextOption::WrapAnywhere);

    // Errors may be thrown from a worker thread, so use signals to queue them on the GUI thread
    ty_error_redirect([](ty_err err, const char *msg, void *udata) {
        TY_UNUSED(err);

        MainWindow *main = static_cast<MainWindow *>(udata);
        emit main->errorMessage(msg);
    }, this);
    connect(this, &MainWindow::errorMessage, this, &MainWindow::showErrorMessage);

    boardList->setModel(manager);
    boardList->setItemDelegate(new BoardItemDelegate(manager));

    connect(boardList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MainWindow::selectionChanged);
    connect(manager, &BoardManagerProxy::boardAdded, this, &MainWindow::setBoardDefaults);

    connect(monitorText, &QPlainTextEdit::textChanged, this, &MainWindow::monitorTextChanged);

    for (auto &board: *manager)
        setBoardDefaults(board);
}

MainWindow::~MainWindow()
{
    ty_error_redirect(nullptr, nullptr);
}

QString MainWindow::lastError() const
{
    return last_error_;
}

void MainWindow::disableBoardWidgets()
{
    setWindowTitle("TyQt");

    infoTab->setEnabled(false);
    modelText->clear();
    locationText->clear();
    serialText->clear();
    interfaceTree->clear();

    monitorTab->setEnabled(false);
    monitorEdit->setEnabled(false);

    actionUpload->setEnabled(false);
    actionUploadNew->setEnabled(false);
    uploadTab->setEnabled(false);
    firmwarePath->clear();

    actionReset->setEnabled(false);
    actionReboot->setEnabled(false);
}

QString MainWindow::browseForFirmware()
{
    if (!current_board_)
        return QString();

    QString filename = QFileDialog::getOpenFileName(this, tr("Open Firmware"), QString(),
                                                    tr("Binary Files (*.elf *.hex);;All Files (*)"));
    if (filename.isEmpty())
        return QString();

    firmwarePath->setText(filename);
    emit firmwarePath->editingFinished();

    return filename;
}

void MainWindow::uploadCurrentFirmware()
{
    if (!current_board_)
        return;

    current_board_->upload(current_board_->property("firmware").toString(),
                           current_board_->property("resetAfter").toBool());
}

void MainWindow::setBoardDefaults(shared_ptr<BoardProxy> board)
{
    board->setProperty("resetAfter", true);

    if (!boardList->currentIndex().isValid() && manager_->boardCount())
        boardList->setCurrentIndex(manager_->index(0, 0));
}

void MainWindow::selectionChanged(const QItemSelection &selected, const QItemSelection &previous)
{
    TY_UNUSED(previous);

    if (current_board_)
        current_board_->disconnect(this);

    if (selected.indexes().isEmpty()) {
        monitorText->setDocument(nullptr);

        current_board_ = nullptr;
        disableBoardWidgets();

        return;
    }

    current_board_ = manager_->board(selected.indexes().front().row());

    firmwarePath->setText(current_board_->property("firmware").toString());
    resetAfterUpload->setChecked(current_board_->property("resetAfter").toBool());

    monitorText->setDocument(&current_board_->serialDocument());

    refreshBoardInfo();
{'fraction_non_alphanumeric': 0.13450369588173178, 'fraction_numerical': 0.0015839493136219642, 'mean_word_length': 4.18972602739726, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 87, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '760936', 'n_tokens_mistral': 2197, 'n_tokens_neox': 2125, 'n_words': 424}