Mercurial > public > ostc_companion
view 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 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("< ", "< ") .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; }
