view MainWindow.cpp @ 4:e30f00f760d3 default tip

Cleanup OSTC label and removed url The computer type will now show OSTC 4/5 instead of only 4. The url has been removed because it is no longer maintained. The ui header have been deleted because they are generated files shich should not be under version controll. Delete locally if you want to force an update of the dialog layout.
author Ideenmodellierer
date Sun, 30 Nov 2025 18:37:32 +0100
parents 0b3630a29ad8
children
line wrap: on
line source

//////////////////////////////////////////////////////////////////////////////
/// \file   MainWindow.cpp
/// \brief  GUI for OSTC Companion.
/// \author JD Gascuel.
///
/// \copyright (c) 2011-2014 JD Gascuel. All rights reserved.
/// $Id$
//////////////////////////////////////////////////////////////////////////////
//
// BSD 2-Clause License:
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////

#include "MainWindow.h"

#include "Utils/LogAppender.h"
#include "Utils/ProgressEvent.h"

#include "ui_MainWindow.h"
#include "SettingsDialog.h"
#include "editLogDialog.h"

#include <QString>
#include <QDateTime>
#include <QFileDialog>
#include <QInputDialog>
#include <QMenu>
#include <QMenuBar>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QProgressBar>
#include <QSettings>
#include <QTextCursor>
#include "OSTCFrogOperations.h"
#include "OSTC2cOperations.h"
#include "OSTC2Operations.h"
#include "OSTCSportOperations.h"
#include "OSTC3Operations.h"
#include "OSTC3pOperations.h"
#include "OSTC4Operations.h"
#include "OSTC_CR_Operations.h"

extern QSettings* settings;

//////////////////////////////////////////////////////////////////////////////

class EXPORT LogWindow
    : public LogAppender
{
    MainWindow* _window;

    //---- The <<printing>> function -----------------------------------------
    void operator()(const Log &log) override {
        QString message = log.message;

        message.replace("< ", "&lt; ")
               .replace(" >", " &gt;");
        if( ! message.isEmpty() )
            _window->statusMessage(message);
    }

    //---- Reimplementing mandatory methds -----------------------------------
    const char *type() const override {
        return "File";
    }
    Log::Level defaultMinLevel() const override {
        return Log::LEVEL_INFO;
    }
    const char* defaultFormat() const override {
        return "%m";
    }

public:
    LogWindow(MainWindow* window)
      : LogAppender(0, NULL),
        _window(window)
    {}
};

//////////////////////////////////////////////////////////////////////////////

MainWindow::MainWindow()
  : QMainWindow(NULL),
    _ui(new Ui::MainWindow),
    _op(0)
{
    // Connect the Log system to this window:
    new LogWindow(this);
    // Connect the progress system to this window:
    ProgressManager::getInstance()->setMainWindow(this);

    _ui->setupUi(this);
    _ui->progressBar->show();

    _ui->editLog->setVisible(false);

    // Auto-select last model:
    QString model = settings->value("Interface/computerType").toString();
        _ui->computerType->setCurrentIndex(0);

    if( model == "ostc2c" )
        _ui->computerType->setCurrentIndex(0);
    else if( model == "hwOS (Bluetooth)" )
        _ui->computerType->setCurrentIndex(1);
    else if( model == "hwOS (USB)" )
        _ui->computerType->setCurrentIndex(2);
    else if( model == "ostc4" )
        _ui->computerType->setCurrentIndex(3);

    changeTypeSlot();

#ifdef Q_OS_MAC
    {
        QMenuBar *menuBar = new QMenuBar(this);
        QMenu* help = menuBar->addMenu(tr("&Help"));
            help->addAction(tr("Preferences..."), this, SLOT(settingsSlot()));
    }
#endif

    setWindowTitle(QString("OSTC Companion v%1.%2 %3")
                       .arg(MAJOR_VERSION)
                       .arg(MINOR_VERSION)  // kein sprintf nötig, arg konvertiert automatisch
                       .arg(BETA_VERSION
                                ? QString(" beta %1").arg(PATCH_VERSION)
                                : QString::number(PATCH_VERSION))
                   );
}


//////////////////////////////////////////////////////////////////////////////

MainWindow::~MainWindow()
{
    delete _ui;
    delete _op;
}

//////////////////////////////////////////////////////////////////////////////

bool MainWindow::event(QEvent* e)
{
    if( ProgressEvent* p = dynamic_cast<ProgressEvent*>(e) )
    {
        QProgressBar* w = _ui->progressBar;

        if( p->current > p->maximum && p->maximum > 0)
        {
            w->setMaximum(p->maximum);  // Remove throttling mode, if any.
            w->reset();
        }
        else
        {
            if( ! w->isEnabled() )
                w->setEnabled(true);
            if( w->maximum() != p->maximum )
                w->setMaximum(p->maximum);  // Start throttling if max==0
            w->setValue(p->current);
        }
        return true;
    }

    return QMainWindow::event(e);
}

//////////////////////////////////////////////////////////////////////////////

void MainWindow::changeTypeSlot()
{
    QString name;

    //---- Setup a new driver ------------------------------------------------
    delete _op;
    _op = 0;
    switch( _ui->computerType->currentIndex() ) {
    case 0: name = "ostc2c";
            _op = new OSTC2cOperations;
        break;
    case 1:  name = "hwOS (USB)";
             _op = new OSTC3Operations;
        break;
    case 2: name = "hwOS (Bluetooth)";
             _op = new OSTC3pOperations;
        break;
    case 3: name = "ostc4";
        _op = new OSTC4Operations;
        break;

    default:
        qWarning("Internal error: unknown computer type");
        return;
    }
    LOG_INFO(tr("%1 selected.").arg(_op->model()));

    settings->setValue("Interface/computerType", name);
    settings->sync();

    // backword compatibility >= translate name if necessary
    if ( name =="hwOS (Bluetooth)") name = "ostc3p";
    if( name =="hwOS (USB)") name = "ostc3";
    if( name =="ostc 4/5") name = "ostc4";

    _ui->computerImage->setPixmap( QPixmap(":/Images/" + name + "_160x120.png"));

    updateStatus();
}

//////////////////////////////////////////////////////////////////////////////

void MainWindow::settingsSlot()
{
    Settings* s = new Settings(this, _op);
    s->exec();
    delete s;
}

//////////////////////////////////////////////////////////////////////////////

void MainWindow::connectSlot()
{
    Q_ASSERT( _op );

    try {
        LOG_INFO("Connecting...");

        //---- Already connected ? ----------------------------------------------
        if( ! _op->description().isEmpty() )
            _op->disconnect();

        //---- (Re)connect ------------------------------------------------------
        if( _op->connect() ) {

            if( Settings::autoSetDateTime )
                dateSlot();

            LOG_INFO("Connected: " + _op->description() );
        }
        updateStatus();
    }
    catch(const std::exception& e) {
        LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2")
                    .arg(tr("Error"))
                    .arg(e.what()) );
    }
}

//////////////////////////////////////////////////////////////////////////////

void MainWindow::closeSlot()
{
    Q_ASSERT( _op );

    try {
        LOG_INFO("Disconnecting...");
        if( _op->disconnect() )
            LOG_INFO("Disconnected.");
        updateStatus();
    }
    catch(const std::exception& e) {
        LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2")
                    .arg(tr("Error"))
                    .arg(e.what()) );
    }
}

//////////////////////////////////////////////////////////////////////////////

void MainWindow::updateStatus()
{
    bool ok = _op;
    _ui->connectButton->setEnabled( ok );

    // ON when supported but NOT connected, OFF once connected.
    _ui->upgradeButton->setEnabled( ok
                                && (_op->supported() & HardwareOperations::FIRMWARE)
                                && !_op->serial().isOpen());

    // Only allow buttons when connected:
    ok &= _op->serial().isOpen();
    _ui->dateButton   ->setEnabled( ok && (_op->supported() & HardwareOperations::DATE) );
    _ui->nameButton   ->setEnabled( ok && (_op->supported() & HardwareOperations::NAME) );
    _ui->iconButton   ->setEnabled( ok && (_op->supported() & HardwareOperations::ICON) );
    _ui->signalButton ->setEnabled( ok && (_op->supported() & HardwareOperations::SIGNAL_CHECK) );
    _ui->closeButton  ->setEnabled( ok );
}

//////////////////////////////////////////////////////////////////////////////

void MainWindow::dateSlot()
{
    Q_ASSERT( _op );

    try {
        QDateTime date = QDateTime::currentDateTime();

        LOG_INFO(tr("Settings date & time..."));
        _op->setDate( date );
        LOG_INFO( QString("Date set to %1")
                 .arg(date.toString("yyyy/MM/dd hh:mm:ss")) );
    }
    catch(const std::exception& e) {
        LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2")
                    .arg(tr("Error"))
                    .arg(e.what()) );
    }
}


//////////////////////////////////////////////////////////////////////////////

void MainWindow::nameSlot()
{
    Q_ASSERT( _op );
    try {
        LOG_INFO(tr("Settings name..."));

        //---- Get old name, and reformat to multi-lines ---------------------
        QString oldName = _op->description().section(", ", 2);
        QString oldText;
        QSize size = _op->nameSize();

        for(int l=0; l<size.height(); ++l) {
            QString line = oldName.left( size.width() ).leftJustified(size.width());
            if( line.contains("\n") ) {
                line = line.section("\n", 0, 0);
                oldName = oldName.mid(line.length());
            }
            else {
                oldName = oldName.mid(line.length());
                if( oldName[0] == '\n' )
                    oldName = oldName.mid(1);
            }
            oldText += line + "|\n";
        }

        //---- Ask user ------------------------------------------------------

        QInputDialog* d = new QInputDialog(this);
        d->setWindowTitle("Set Computer Name...");
        d->setInputMode(QInputDialog::TextInput);
        d->setOptions(QInputDialog::UsePlainTextEditForTextInput);
        d->setTextValue(oldText);

        QPlainTextEdit* edit = d->findChild<QPlainTextEdit*>();
        assert(edit);
        edit->setStyleSheet(
            "background-color: black;"
            "color: green;"
            "font: 14pt 'Courier New';"
        );

        if( d->exec() != QDialog::Accepted )
            return;

        QString newText = d->textValue();
        delete d;

        //---- Reformat to single padded string ------------------------------
        QStringList lines = newText.split("\n");
        QString name;
        for(int l=0; l<size.height(); ++l) {
            if( l < lines.count() )
                name += lines[l].leftJustified(size.width(), ' ', true);
            else
                name += QString(size.width(), ' ');
        }

        //---- Send result ---------------------------------------------------
        _op->setName( name );
        _op->getIdentity();
        LOG_INFO( QString("Name set to '%1'")
            .arg(_op->description().section(',', 2)).replace("\n", "|") );
    }
    catch(const std::exception& e) {
        LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2")
                    .arg(tr("Error"))
                    .arg(e.what()) );
    }
}

//////////////////////////////////////////////////////////////////////////////

void MainWindow::iconSlot()
{
    Q_ASSERT( _op );
    try {
        LOG_INFO(tr("Settings icons..."));

        QString fileName = QFileDialog::getOpenFileName(this,
                                                        "Icon File...",
                                                        QString(),
                                                        "Images (*.gif *.png *.tif *.tiff *.jpg *.jpeg *.ico *.svg);;"
                                                        "GIF Image (*.gif);;"
                                                        "PNG Image (*.png);;"
                                                        "TIFF Image (*.tif *.tiff);;"
                                                        "JPEG Image (*.jpg *.jpeg);;"
                                                        "Icon Image (*.ico);;"
                                                        "SVG Image (*.svg);;"
                                                        "Anything (*.*)");
        if( ! fileName.isEmpty() )
            _op->setIcons(fileName);
    }
    catch(const std::exception& e) {
        LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2")
                    .arg(tr("Error"))
                    .arg(e.what()) );
    }
}

//////////////////////////////////////////////////////////////////////////////

void MainWindow::upgradeSlot()
{
    Q_ASSERT( _op );

    try {
        LOG_INFO(tr("Upgrading firmware..."));

        QString hexFile = QFileDialog::getOpenFileName(0,
                                                       "Hex File...",
                                                       Settings::currentPath,
                                                       _op->firmwareTemplate());
        if( hexFile.isEmpty() )
            return;

        Settings::currentPath = QFileInfo(hexFile).absoluteDir().path();
        Settings::save();

        if( _op )
            _op->upgradeFW(hexFile);
    }
    catch(const std::exception& e) {
        LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2")
                    .arg(tr("Error"))
                    .arg(e.what()) );
    }
}

//////////////////////////////////////////////////////////////////////////////

void MainWindow::statusMessage(const QString& msg)
{
    {   // Move cursor to end of document.
        QTextCursor c = _ui->console->textCursor();
        c.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
        _ui->console->setTextCursor(c);
    }
    _ui->console->appendHtml(msg);
    _ui->console->ensureCursorVisible();

    qApp->processEvents(QEventLoop::AllEvents, 50);
}

void MainWindow::retranslate()
{
    _ui->retranslateUi(this);
}

//////////////////////////////////////////////////////////////////////////////
void MainWindow::on_signalButton_clicked()
{
    Q_ASSERT( _op );

    try {
            LOG_INFO(tr("Request Bluetooth signal strength..."));
            _op->getSignal();
    }
    catch(const std::exception& e) {
        LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2")
                    .arg(tr("Error"))
                    .arg(e.what()) );
    }
}


void MainWindow::on_editLog_clicked()
{
    EditLogDialog* eL = new EditLogDialog(this, _op);
    eL->exec();
    delete eL;
}