Mercurial > public > ostc_companion
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 0:76ccd6ce50c0 | 1:0b3630a29ad8 |
|---|---|
| 1 ////////////////////////////////////////////////////////////////////////////// | |
| 2 /// \file MainWindow.cpp | |
| 3 /// \brief GUI for OSTC Companion. | |
| 4 /// \author JD Gascuel. | |
| 5 /// | |
| 6 /// \copyright (c) 2011-2014 JD Gascuel. All rights reserved. | |
| 7 /// $Id$ | |
| 8 ////////////////////////////////////////////////////////////////////////////// | |
| 9 // | |
| 10 // BSD 2-Clause License: | |
| 11 // | |
| 12 // Redistribution and use in source and binary forms, with or without | |
| 13 // modification, are permitted provided that the following conditions | |
| 14 // are met: | |
| 15 // | |
| 16 // 1. Redistributions of source code must retain the above copyright notice, | |
| 17 // this list of conditions and the following disclaimer. | |
| 18 // | |
| 19 // 2. Redistributions in binary form must reproduce the above copyright notice, | |
| 20 // this list of conditions and the following disclaimer in the documentation | |
| 21 // and/or other materials provided with the distribution. | |
| 22 // | |
| 23 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| 24 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 25 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 26 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | |
| 27 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| 28 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| 29 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| 30 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| 31 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| 32 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
| 33 // THE POSSIBILITY OF SUCH DAMAGE. | |
| 34 // | |
| 35 ////////////////////////////////////////////////////////////////////////////// | |
| 36 | |
| 37 #include "MainWindow.h" | |
| 38 | |
| 39 #include "Utils/LogAppender.h" | |
| 40 #include "Utils/ProgressEvent.h" | |
| 41 | |
| 42 #include "ui_MainWindow.h" | |
| 43 #include "SettingsDialog.h" | |
| 44 #include "editLogDialog.h" | |
| 45 | |
| 46 #include <QString> | |
| 47 #include <QDateTime> | |
| 48 #include <QFileDialog> | |
| 49 #include <QInputDialog> | |
| 50 #include <QMenu> | |
| 51 #include <QMenuBar> | |
| 52 #include <QMessageBox> | |
| 53 #include <QPlainTextEdit> | |
| 54 #include <QProgressBar> | |
| 55 #include <QSettings> | |
| 56 #include <QTextCursor> | |
| 57 #include "OSTCFrogOperations.h" | |
| 58 #include "OSTC2cOperations.h" | |
| 59 #include "OSTC2Operations.h" | |
| 60 #include "OSTCSportOperations.h" | |
| 61 #include "OSTC3Operations.h" | |
| 62 #include "OSTC3pOperations.h" | |
| 63 #include "OSTC4Operations.h" | |
| 64 #include "OSTC_CR_Operations.h" | |
| 65 | |
| 66 extern QSettings* settings; | |
| 67 | |
| 68 ////////////////////////////////////////////////////////////////////////////// | |
| 69 | |
| 70 class EXPORT LogWindow | |
| 71 : public LogAppender | |
| 72 { | |
| 73 MainWindow* _window; | |
| 74 | |
| 75 //---- The <<printing>> function ----------------------------------------- | |
| 76 void operator()(const Log &log) override { | |
| 77 QString message = log.message; | |
| 78 | |
| 79 message.replace("< ", "< ") | |
| 80 .replace(" >", " >"); | |
| 81 if( ! message.isEmpty() ) | |
| 82 _window->statusMessage(message); | |
| 83 } | |
| 84 | |
| 85 //---- Reimplementing mandatory methds ----------------------------------- | |
| 86 const char *type() const override { | |
| 87 return "File"; | |
| 88 } | |
| 89 Log::Level defaultMinLevel() const override { | |
| 90 return Log::LEVEL_INFO; | |
| 91 } | |
| 92 const char* defaultFormat() const override { | |
| 93 return "%m"; | |
| 94 } | |
| 95 | |
| 96 public: | |
| 97 LogWindow(MainWindow* window) | |
| 98 : LogAppender(0, NULL), | |
| 99 _window(window) | |
| 100 {} | |
| 101 }; | |
| 102 | |
| 103 ////////////////////////////////////////////////////////////////////////////// | |
| 104 | |
| 105 MainWindow::MainWindow() | |
| 106 : QMainWindow(NULL), | |
| 107 _ui(new Ui::MainWindow), | |
| 108 _op(0) | |
| 109 { | |
| 110 // Connect the Log system to this window: | |
| 111 new LogWindow(this); | |
| 112 // Connect the progress system to this window: | |
| 113 ProgressManager::getInstance()->setMainWindow(this); | |
| 114 | |
| 115 _ui->setupUi(this); | |
| 116 _ui->progressBar->show(); | |
| 117 | |
| 118 _ui->editLog->setVisible(false); | |
| 119 | |
| 120 // Auto-select last model: | |
| 121 QString model = settings->value("Interface/computerType").toString(); | |
| 122 _ui->computerType->setCurrentIndex(0); | |
| 123 | |
| 124 if( model == "ostc2c" ) | |
| 125 _ui->computerType->setCurrentIndex(0); | |
| 126 else if( model == "hwOS (Bluetooth)" ) | |
| 127 _ui->computerType->setCurrentIndex(1); | |
| 128 else if( model == "hwOS (USB)" ) | |
| 129 _ui->computerType->setCurrentIndex(2); | |
| 130 else if( model == "ostc4" ) | |
| 131 _ui->computerType->setCurrentIndex(3); | |
| 132 | |
| 133 changeTypeSlot(); | |
| 134 | |
| 135 #ifdef Q_OS_MAC | |
| 136 { | |
| 137 QMenuBar *menuBar = new QMenuBar(this); | |
| 138 QMenu* help = menuBar->addMenu(tr("&Help")); | |
| 139 help->addAction(tr("Preferences..."), this, SLOT(settingsSlot())); | |
| 140 } | |
| 141 #endif | |
| 142 | |
| 143 setWindowTitle(QString("OSTC Companion v%1.%2 %3") | |
| 144 .arg(MAJOR_VERSION) | |
| 145 .arg(MINOR_VERSION) // kein sprintf nötig, arg konvertiert automatisch | |
| 146 .arg(BETA_VERSION | |
| 147 ? QString(" beta %1").arg(PATCH_VERSION) | |
| 148 : QString::number(PATCH_VERSION)) | |
| 149 ); | |
| 150 | |
| 151 _ui->companionUrlL->setText( QString("<html>%1 " | |
| 152 "<a href='https://ostc-planner.net/wp/companion" | |
| 153 #if defined(Q_OS_IOS) | |
| 154 "?os=ios" | |
| 155 #elif defined(Q_OS_OSX) | |
| 156 "?os=mac" | |
| 157 #elif defined(Q_OS_WIN) | |
| 158 "?os=win" | |
| 159 #elif defined(Q_OS_ANDROID) | |
| 160 "?os=android" | |
| 161 #elif defined(Q_OS_LINUX) | |
| 162 "?os=linux" | |
| 163 #else | |
| 164 "?os=other" | |
| 165 #endif | |
| 166 #if defined(Q_OS_DARWIN64) || defined(Q_OS_WIN64) || defined(Q_OS_LINUX ) | |
| 167 "&arch=64" | |
| 168 #else | |
| 169 "&arch=32" | |
| 170 #endif | |
| 171 "&version=%2.%3.%4" | |
| 172 "&model=%6" | |
| 173 "'>ostc-planner.net/wp/companion" | |
| 174 "</a>" | |
| 175 "</html>") | |
| 176 .arg( tr("Official web site") ) | |
| 177 .arg(QString::number(MAJOR_VERSION)) | |
| 178 .arg(QString::number(MINOR_VERSION)) | |
| 179 .arg(BETA_VERSION ? QString("beta%1").arg(PATCH_VERSION) | |
| 180 : QString("%1") .arg(PATCH_VERSION)) | |
| 181 .arg(_op->model()) | |
| 182 ); | |
| 183 } | |
| 184 | |
| 185 | |
| 186 ////////////////////////////////////////////////////////////////////////////// | |
| 187 | |
| 188 MainWindow::~MainWindow() | |
| 189 { | |
| 190 delete _ui; | |
| 191 delete _op; | |
| 192 } | |
| 193 | |
| 194 ////////////////////////////////////////////////////////////////////////////// | |
| 195 | |
| 196 bool MainWindow::event(QEvent* e) | |
| 197 { | |
| 198 if( ProgressEvent* p = dynamic_cast<ProgressEvent*>(e) ) | |
| 199 { | |
| 200 QProgressBar* w = _ui->progressBar; | |
| 201 | |
| 202 if( p->current > p->maximum && p->maximum > 0) | |
| 203 { | |
| 204 w->setMaximum(p->maximum); // Remove throttling mode, if any. | |
| 205 w->reset(); | |
| 206 } | |
| 207 else | |
| 208 { | |
| 209 if( ! w->isEnabled() ) | |
| 210 w->setEnabled(true); | |
| 211 if( w->maximum() != p->maximum ) | |
| 212 w->setMaximum(p->maximum); // Start throttling if max==0 | |
| 213 w->setValue(p->current); | |
| 214 } | |
| 215 return true; | |
| 216 } | |
| 217 | |
| 218 return QMainWindow::event(e); | |
| 219 } | |
| 220 | |
| 221 ////////////////////////////////////////////////////////////////////////////// | |
| 222 | |
| 223 void MainWindow::changeTypeSlot() | |
| 224 { | |
| 225 QString name; | |
| 226 | |
| 227 //---- Setup a new driver ------------------------------------------------ | |
| 228 delete _op; | |
| 229 _op = 0; | |
| 230 switch( _ui->computerType->currentIndex() ) { | |
| 231 case 0: name = "ostc2c"; | |
| 232 _op = new OSTC2cOperations; | |
| 233 break; | |
| 234 case 1: name = "hwOS (USB)"; | |
| 235 _op = new OSTC3Operations; | |
| 236 break; | |
| 237 case 2: name = "hwOS (Bluetooth)"; | |
| 238 _op = new OSTC3pOperations; | |
| 239 break; | |
| 240 case 3: name = "ostc4"; | |
| 241 _op = new OSTC4Operations; | |
| 242 break; | |
| 243 | |
| 244 default: | |
| 245 qWarning("Internal error: unknown computer type"); | |
| 246 return; | |
| 247 } | |
| 248 LOG_INFO(tr("%1 selected.").arg(_op->model())); | |
| 249 | |
| 250 settings->setValue("Interface/computerType", name); | |
| 251 settings->sync(); | |
| 252 | |
| 253 // backword compatibility >= translate name if necessary | |
| 254 if ( name =="hwOS (Bluetooth)") name = "ostc3p"; | |
| 255 if( name =="hwOS (USB)") name = "ostc3"; | |
| 256 | |
| 257 | |
| 258 _ui->computerImage->setPixmap( QPixmap(":/Images/" + name + "_160x120.png")); | |
| 259 | |
| 260 updateStatus(); | |
| 261 } | |
| 262 | |
| 263 ////////////////////////////////////////////////////////////////////////////// | |
| 264 | |
| 265 void MainWindow::settingsSlot() | |
| 266 { | |
| 267 Settings* s = new Settings(this, _op); | |
| 268 s->exec(); | |
| 269 delete s; | |
| 270 } | |
| 271 | |
| 272 ////////////////////////////////////////////////////////////////////////////// | |
| 273 | |
| 274 void MainWindow::connectSlot() | |
| 275 { | |
| 276 Q_ASSERT( _op ); | |
| 277 | |
| 278 try { | |
| 279 LOG_INFO("Connecting..."); | |
| 280 | |
| 281 //---- Already connected ? ---------------------------------------------- | |
| 282 if( ! _op->description().isEmpty() ) | |
| 283 _op->disconnect(); | |
| 284 | |
| 285 //---- (Re)connect ------------------------------------------------------ | |
| 286 if( _op->connect() ) { | |
| 287 | |
| 288 if( Settings::autoSetDateTime ) | |
| 289 dateSlot(); | |
| 290 | |
| 291 LOG_INFO("Connected: " + _op->description() ); | |
| 292 } | |
| 293 updateStatus(); | |
| 294 } | |
| 295 catch(const std::exception& e) { | |
| 296 LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2") | |
| 297 .arg(tr("Error")) | |
| 298 .arg(e.what()) ); | |
| 299 } | |
| 300 } | |
| 301 | |
| 302 ////////////////////////////////////////////////////////////////////////////// | |
| 303 | |
| 304 void MainWindow::closeSlot() | |
| 305 { | |
| 306 Q_ASSERT( _op ); | |
| 307 | |
| 308 try { | |
| 309 LOG_INFO("Disconnecting..."); | |
| 310 if( _op->disconnect() ) | |
| 311 LOG_INFO("Disconnected."); | |
| 312 updateStatus(); | |
| 313 } | |
| 314 catch(const std::exception& e) { | |
| 315 LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2") | |
| 316 .arg(tr("Error")) | |
| 317 .arg(e.what()) ); | |
| 318 } | |
| 319 } | |
| 320 | |
| 321 ////////////////////////////////////////////////////////////////////////////// | |
| 322 | |
| 323 void MainWindow::updateStatus() | |
| 324 { | |
| 325 bool ok = _op; | |
| 326 _ui->connectButton->setEnabled( ok ); | |
| 327 | |
| 328 // ON when supported but NOT connected, OFF once connected. | |
| 329 _ui->upgradeButton->setEnabled( ok | |
| 330 && (_op->supported() & HardwareOperations::FIRMWARE) | |
| 331 && !_op->serial().isOpen()); | |
| 332 | |
| 333 // Only allow buttons when connected: | |
| 334 ok &= _op->serial().isOpen(); | |
| 335 _ui->dateButton ->setEnabled( ok && (_op->supported() & HardwareOperations::DATE) ); | |
| 336 _ui->nameButton ->setEnabled( ok && (_op->supported() & HardwareOperations::NAME) ); | |
| 337 _ui->iconButton ->setEnabled( ok && (_op->supported() & HardwareOperations::ICON) ); | |
| 338 _ui->signalButton ->setEnabled( ok && (_op->supported() & HardwareOperations::SIGNAL_CHECK) ); | |
| 339 _ui->closeButton ->setEnabled( ok ); | |
| 340 } | |
| 341 | |
| 342 ////////////////////////////////////////////////////////////////////////////// | |
| 343 | |
| 344 void MainWindow::dateSlot() | |
| 345 { | |
| 346 Q_ASSERT( _op ); | |
| 347 | |
| 348 try { | |
| 349 QDateTime date = QDateTime::currentDateTime(); | |
| 350 | |
| 351 LOG_INFO(tr("Settings date & time...")); | |
| 352 _op->setDate( date ); | |
| 353 LOG_INFO( QString("Date set to %1") | |
| 354 .arg(date.toString("yyyy/MM/dd hh:mm:ss")) ); | |
| 355 } | |
| 356 catch(const std::exception& e) { | |
| 357 LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2") | |
| 358 .arg(tr("Error")) | |
| 359 .arg(e.what()) ); | |
| 360 } | |
| 361 } | |
| 362 | |
| 363 | |
| 364 ////////////////////////////////////////////////////////////////////////////// | |
| 365 | |
| 366 void MainWindow::nameSlot() | |
| 367 { | |
| 368 Q_ASSERT( _op ); | |
| 369 try { | |
| 370 LOG_INFO(tr("Settings name...")); | |
| 371 | |
| 372 //---- Get old name, and reformat to multi-lines --------------------- | |
| 373 QString oldName = _op->description().section(", ", 2); | |
| 374 QString oldText; | |
| 375 QSize size = _op->nameSize(); | |
| 376 | |
| 377 for(int l=0; l<size.height(); ++l) { | |
| 378 QString line = oldName.left( size.width() ).leftJustified(size.width()); | |
| 379 if( line.contains("\n") ) { | |
| 380 line = line.section("\n", 0, 0); | |
| 381 oldName = oldName.mid(line.length()); | |
| 382 } | |
| 383 else { | |
| 384 oldName = oldName.mid(line.length()); | |
| 385 if( oldName[0] == '\n' ) | |
| 386 oldName = oldName.mid(1); | |
| 387 } | |
| 388 oldText += line + "|\n"; | |
| 389 } | |
| 390 | |
| 391 //---- Ask user ------------------------------------------------------ | |
| 392 | |
| 393 QInputDialog* d = new QInputDialog(this); | |
| 394 d->setWindowTitle("Set Computer Name..."); | |
| 395 d->setInputMode(QInputDialog::TextInput); | |
| 396 d->setOptions(QInputDialog::UsePlainTextEditForTextInput); | |
| 397 d->setTextValue(oldText); | |
| 398 | |
| 399 QPlainTextEdit* edit = d->findChild<QPlainTextEdit*>(); | |
| 400 assert(edit); | |
| 401 edit->setStyleSheet( | |
| 402 "background-color: black;" | |
| 403 "color: green;" | |
| 404 "font: 14pt 'Courier New';" | |
| 405 ); | |
| 406 | |
| 407 if( d->exec() != QDialog::Accepted ) | |
| 408 return; | |
| 409 | |
| 410 QString newText = d->textValue(); | |
| 411 delete d; | |
| 412 | |
| 413 //---- Reformat to single padded string ------------------------------ | |
| 414 QStringList lines = newText.split("\n"); | |
| 415 QString name; | |
| 416 for(int l=0; l<size.height(); ++l) { | |
| 417 if( l < lines.count() ) | |
| 418 name += lines[l].leftJustified(size.width(), ' ', true); | |
| 419 else | |
| 420 name += QString(size.width(), ' '); | |
| 421 } | |
| 422 | |
| 423 //---- Send result --------------------------------------------------- | |
| 424 _op->setName( name ); | |
| 425 _op->getIdentity(); | |
| 426 LOG_INFO( QString("Name set to '%1'") | |
| 427 .arg(_op->description().section(',', 2)).replace("\n", "|") ); | |
| 428 } | |
| 429 catch(const std::exception& e) { | |
| 430 LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2") | |
| 431 .arg(tr("Error")) | |
| 432 .arg(e.what()) ); | |
| 433 } | |
| 434 } | |
| 435 | |
| 436 ////////////////////////////////////////////////////////////////////////////// | |
| 437 | |
| 438 void MainWindow::iconSlot() | |
| 439 { | |
| 440 Q_ASSERT( _op ); | |
| 441 try { | |
| 442 LOG_INFO(tr("Settings icons...")); | |
| 443 | |
| 444 QString fileName = QFileDialog::getOpenFileName(this, | |
| 445 "Icon File...", | |
| 446 QString(), | |
| 447 "Images (*.gif *.png *.tif *.tiff *.jpg *.jpeg *.ico *.svg);;" | |
| 448 "GIF Image (*.gif);;" | |
| 449 "PNG Image (*.png);;" | |
| 450 "TIFF Image (*.tif *.tiff);;" | |
| 451 "JPEG Image (*.jpg *.jpeg);;" | |
| 452 "Icon Image (*.ico);;" | |
| 453 "SVG Image (*.svg);;" | |
| 454 "Anything (*.*)"); | |
| 455 if( ! fileName.isEmpty() ) | |
| 456 _op->setIcons(fileName); | |
| 457 } | |
| 458 catch(const std::exception& e) { | |
| 459 LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2") | |
| 460 .arg(tr("Error")) | |
| 461 .arg(e.what()) ); | |
| 462 } | |
| 463 } | |
| 464 | |
| 465 ////////////////////////////////////////////////////////////////////////////// | |
| 466 | |
| 467 void MainWindow::upgradeSlot() | |
| 468 { | |
| 469 Q_ASSERT( _op ); | |
| 470 | |
| 471 try { | |
| 472 LOG_INFO(tr("Upgrading firmware...")); | |
| 473 | |
| 474 QString hexFile = QFileDialog::getOpenFileName(0, | |
| 475 "Hex File...", | |
| 476 Settings::currentPath, | |
| 477 _op->firmwareTemplate()); | |
| 478 if( hexFile.isEmpty() ) | |
| 479 return; | |
| 480 | |
| 481 Settings::currentPath = QFileInfo(hexFile).absoluteDir().path(); | |
| 482 Settings::save(); | |
| 483 | |
| 484 if( _op ) | |
| 485 _op->upgradeFW(hexFile); | |
| 486 } | |
| 487 catch(const std::exception& e) { | |
| 488 LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2") | |
| 489 .arg(tr("Error")) | |
| 490 .arg(e.what()) ); | |
| 491 } | |
| 492 } | |
| 493 | |
| 494 ////////////////////////////////////////////////////////////////////////////// | |
| 495 | |
| 496 void MainWindow::statusMessage(const QString& msg) | |
| 497 { | |
| 498 { // Move cursor to end of document. | |
| 499 QTextCursor c = _ui->console->textCursor(); | |
| 500 c.movePosition(QTextCursor::End, QTextCursor::MoveAnchor); | |
| 501 _ui->console->setTextCursor(c); | |
| 502 } | |
| 503 _ui->console->appendHtml(msg); | |
| 504 _ui->console->ensureCursorVisible(); | |
| 505 | |
| 506 qApp->processEvents(QEventLoop::AllEvents, 50); | |
| 507 } | |
| 508 | |
| 509 void MainWindow::retranslate() | |
| 510 { | |
| 511 _ui->retranslateUi(this); | |
| 512 } | |
| 513 | |
| 514 ////////////////////////////////////////////////////////////////////////////// | |
| 515 void MainWindow::on_signalButton_clicked() | |
| 516 { | |
| 517 Q_ASSERT( _op ); | |
| 518 | |
| 519 try { | |
| 520 LOG_INFO(tr("Request Bluetooth signal strength...")); | |
| 521 _op->getSignal(); | |
| 522 } | |
| 523 catch(const std::exception& e) { | |
| 524 LOG_INFO( QString("<bg><font color='red'>%1</font></color>: %2") | |
| 525 .arg(tr("Error")) | |
| 526 .arg(e.what()) ); | |
| 527 } | |
| 528 } | |
| 529 | |
| 530 | |
| 531 void MainWindow::on_editLog_clicked() | |
| 532 { | |
| 533 EditLogDialog* eL = new EditLogDialog(this, _op); | |
| 534 eL->exec(); | |
| 535 delete eL; | |
| 536 } |
