Mercurial > public > ostc_companion
diff MainWindow.cpp @ 1:0b3630a29ad8
Initial version based on previous repository.
Project was ported to QT6 and in now cmake based.
| author | Ideenmodellierer <tiefenrauscher@web.de> |
|---|---|
| date | Thu, 27 Nov 2025 18:40:28 +0100 |
| parents | |
| children | e30f00f760d3 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MainWindow.cpp Thu Nov 27 18:40:28 2025 +0100 @@ -0,0 +1,536 @@ +////////////////////////////////////////////////////////////////////////////// +/// \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("< ", "< ") + .replace(" >", " >"); + 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)) + ); + + _ui->companionUrlL->setText( QString("<html>%1 " + "<a href='https://ostc-planner.net/wp/companion" +#if defined(Q_OS_IOS) + "?os=ios" +#elif defined(Q_OS_OSX) + "?os=mac" +#elif defined(Q_OS_WIN) + "?os=win" +#elif defined(Q_OS_ANDROID) + "?os=android" +#elif defined(Q_OS_LINUX) + "?os=linux" +#else + "?os=other" +#endif +#if defined(Q_OS_DARWIN64) || defined(Q_OS_WIN64) || defined(Q_OS_LINUX ) + "&arch=64" +#else + "&arch=32" +#endif + "&version=%2.%3.%4" + "&model=%6" + "'>ostc-planner.net/wp/companion" + "</a>" + "</html>") + .arg( tr("Official web site") ) + .arg(QString::number(MAJOR_VERSION)) + .arg(QString::number(MINOR_VERSION)) + .arg(BETA_VERSION ? QString("beta%1").arg(PATCH_VERSION) + : QString("%1") .arg(PATCH_VERSION)) + .arg(_op->model()) + ); +} + + +////////////////////////////////////////////////////////////////////////////// + +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"; + + + _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; +}
