Bsongis/x7d companion support (#4028)
[opentx.git] / companion / src / mdichild.cpp
blob76dd3fe625af997fe77ba3bc2a9eb7ff96e5382d
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the examples of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial Usage
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** GNU General Public License Usage
29 ** Alternatively, this file may be used under the terms of the GNU
30 ** General Public License version 3.0 as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL included in the
32 ** packaging of this file. Please review the following information to
33 ** ensure the GNU General Public License version 3.0 requirements will be
34 ** met: http://www.gnu.org/copyleft/gpl.html.
36 ** If you have questions regarding the use of this file, please contact
37 ** Nokia at qt-info@nokia.com.
38 ** $QT_END_LICENSE$
40 ****************************************************************************/
42 #include "mdichild.h"
43 #include "ui_mdichild.h"
44 #include "xmlinterface.h"
45 #include "hexinterface.h"
46 #include "mainwindow.h"
47 #include "modeledit/modeledit.h"
48 #include "generaledit/generaledit.h"
49 #include "burnconfigdialog.h"
50 #include "printdialog.h"
51 #include "flasheepromdialog.h"
52 #include "helpers.h"
53 #include "appdata.h"
54 #include "wizarddialog.h"
55 #include "flashfirmwaredialog.h"
56 #include <QFileInfo>
58 #if defined WIN32 || !defined __GNUC__
59 #include <windows.h>
60 #define sleep(x) Sleep(x*1000)
61 #else
62 #include <unistd.h>
63 #endif
65 MdiChild::MdiChild():
66 QWidget(),
67 ui(new Ui::mdiChild),
68 firmware(GetCurrentFirmware()),
69 isUntitled(true),
70 fileChanged(false)
72 ui->setupUi(this);
73 setWindowIcon(CompanionIcon("open.png"));
74 ui->SimulateTxButton->setIcon(CompanionIcon("simulate.png"));
75 setAttribute(Qt::WA_DeleteOnClose);
77 eepromInterfaceChanged();
79 if (!(this->isMaximized() || this->isMinimized())) {
80 adjustSize();
84 MdiChild::~MdiChild()
86 delete ui;
89 void MdiChild::qSleep(int ms)
91 if (ms<0)
92 return;
94 #if defined WIN32 || !defined __GNUC__
95 Sleep(uint(ms));
96 #else
97 struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
98 nanosleep(&ts, NULL);
99 #endif
102 void MdiChild::eepromInterfaceChanged()
104 ui->modelsList->refreshList();
105 ui->SimulateTxButton->setEnabled(GetCurrentFirmware()/*firmware*/->getCapability(Simulation));
106 updateTitle();
109 void MdiChild::cut()
111 ui->modelsList->cut();
114 void MdiChild::copy()
116 ui->modelsList->copy();
119 void MdiChild::paste()
121 ui->modelsList->paste();
124 bool MdiChild::hasPasteData()
126 return ui->modelsList->hasPasteData();
129 bool MdiChild::hasSelection()
131 return ui->modelsList->hasSelection();
134 void MdiChild::updateTitle()
136 QString title = userFriendlyCurrentFile() + "[*]" + " (" + GetCurrentFirmware()->getName() + QString(")");
137 if (!IS_SKY9X(GetCurrentFirmware()->getBoard()))
138 title += QString(" - %1 ").arg(EEPromAvail) + tr("free bytes");
139 setWindowTitle(title);
142 void MdiChild::setModified()
144 ui->modelsList->refreshList();
145 fileChanged = true;
146 updateTitle();
147 documentWasModified();
150 void MdiChild::on_SimulateTxButton_clicked()
152 startSimulation(this, radioData, -1);
155 void MdiChild::checkAndInitModel(int row)
157 ModelData &model = radioData.models[row - 1];
158 if (model.isEmpty()) {
159 model.setDefaultValues(row - 1, radioData.generalSettings);
160 setModified();
164 void MdiChild::generalEdit()
166 GeneralEdit *t = new GeneralEdit(this, radioData, GetCurrentFirmware()/*firmware*/);
167 connect(t, SIGNAL(modified()), this, SLOT(setModified()));
168 t->show();
171 void MdiChild::modelEdit()
173 int row = getCurrentRow();
175 if (row == 0){
176 generalEdit();
178 else {
179 QApplication::setOverrideCursor(Qt::WaitCursor);
180 checkAndInitModel( row );
181 ModelData & model = radioData.models[row - 1];
182 gStopwatch.restart();
183 gStopwatch.report("ModelEdit creation");
184 ModelEdit *t = new ModelEdit(this, radioData, (row - 1), GetCurrentFirmware()/*firmware*/);
185 gStopwatch.report("ModelEdit created");
186 t->setWindowTitle(tr("Editing model %1: ").arg(row) + model.name);
187 connect(t, SIGNAL(modified()), this, SLOT(setModified()));
188 gStopwatch.report("STARTING MODEL EDIT");
189 t->show();
190 QApplication::restoreOverrideCursor();
191 gStopwatch.report("ModelEdit shown");
195 void MdiChild::wizardEdit()
197 int row = getCurrentRow();
198 if (row > 0) {
199 checkAndInitModel(row);
200 WizardDialog * wizard = new WizardDialog(radioData.generalSettings, row, this);
201 wizard->exec();
202 if (wizard->mix.complete /*TODO rather test the exec() result?*/) {
203 radioData.models[row - 1] = wizard->mix;
204 setModified();
209 void MdiChild::openEditWindow()
211 int row = getCurrentRow();
212 if (row == 0){
213 generalEdit();
215 else{
216 ModelData & model = radioData.models[row - 1];
217 if (model.isEmpty() && g.useWizard()) {
218 wizardEdit();
220 else {
221 modelEdit();
226 void MdiChild::newFile()
228 static int sequenceNumber = 1;
230 isUntitled = true;
231 curFile = QString("document%1.eepe").arg(sequenceNumber++);
232 updateTitle();
235 bool MdiChild::loadFile(const QString &fileName, bool resetCurrentFile)
237 QFile file(fileName);
239 if (!file.exists()) {
240 QMessageBox::critical(this, tr("Error"), tr("Unable to find file %1!").arg(fileName));
241 return false;
244 int fileType = getFileType(fileName);
246 #if 0
247 if (fileType==FILE_TYPE_XML) {
248 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { //reading HEX TEXT file
249 QMessageBox::critical(this, tr("Error"),
250 tr("Error opening file %1:\n%2.")
251 .arg(fileName)
252 .arg(file.errorString()));
253 return false;
255 QTextStream inputStream(&file);
256 XmlInterface(inputStream).load(radioData);
258 else
259 #endif
260 if (fileType==FILE_TYPE_HEX || fileType==FILE_TYPE_EEPE) { //read HEX file
261 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { //reading HEX TEXT file
262 QMessageBox::critical(this, tr("Error"),
263 tr("Error opening file %1:\n%2.")
264 .arg(fileName)
265 .arg(file.errorString()));
266 return false;
269 QDomDocument doc(ER9X_EEPROM_FILE_TYPE);
270 bool xmlOK = doc.setContent(&file);
271 if(xmlOK) {
272 std::bitset<NUM_ERRORS> errors((unsigned long long)LoadEepromXml(radioData, doc));
273 if (errors.test(ALL_OK)) {
274 ui->modelsList->refreshList();
275 if(resetCurrentFile) setCurrentFile(fileName);
276 return true;
279 file.reset();
281 QTextStream inputStream(&file);
283 if (fileType==FILE_TYPE_EEPE) { // read EEPE file header
284 QString hline = inputStream.readLine();
285 if (hline!=EEPE_EEPROM_FILE_HEADER) {
286 file.close();
287 return false;
291 QByteArray eeprom(EESIZE_MAX, 0);
292 int eeprom_size = HexInterface(inputStream).load((uint8_t *)eeprom.data(), EESIZE_MAX);
293 if (!eeprom_size) {
294 QMessageBox::critical(this, tr("Error"),
295 tr("Invalid EEPROM File %1")
296 .arg(fileName));
297 file.close();
298 return false;
301 file.close();
303 std::bitset<NUM_ERRORS> errors((unsigned long long)LoadEeprom(radioData, (uint8_t *)eeprom.data(), eeprom_size));
304 if (!errors.test(ALL_OK)) {
305 ShowEepromErrors(this, tr("Error"), tr("Invalid EEPROM File %1").arg(fileName), errors.to_ulong());
306 return false;
308 if (errors.test(HAS_WARNINGS)) {
309 ShowEepromWarnings(this, tr("Warning"), errors.to_ulong());
312 ui->modelsList->refreshList();
313 if(resetCurrentFile) setCurrentFile(fileName);
315 return true;
317 else if (fileType==FILE_TYPE_BIN) { //read binary
318 int eeprom_size = file.size();
320 if (!file.open(QFile::ReadOnly)) { //reading binary file - TODO HEX support
321 QMessageBox::critical(this, tr("Error"),
322 tr("Error opening file %1:\n%2.")
323 .arg(fileName)
324 .arg(file.errorString()));
325 return false;
327 uint8_t *eeprom = (uint8_t *)malloc(eeprom_size);
328 memset(eeprom, 0, eeprom_size);
329 long result = file.read((char*)eeprom, eeprom_size);
330 file.close();
332 if (result != eeprom_size) {
333 QMessageBox::critical(this, tr("Error"),
334 tr("Error reading file %1:\n%2.")
335 .arg(fileName)
336 .arg(file.errorString()));
338 return false;
341 std::bitset<NUM_ERRORS> errorsEeprom((unsigned long long)LoadEeprom(radioData, eeprom, eeprom_size));
342 if (!errorsEeprom.test(ALL_OK)) {
343 std::bitset<NUM_ERRORS> errorsBackup((unsigned long long)LoadBackup(radioData, eeprom, eeprom_size, 0));
344 if (!errorsBackup.test(ALL_OK)) {
345 ShowEepromErrors(this, tr("Error"), tr("Invalid binary EEPROM File %1").arg(fileName), (errorsEeprom | errorsBackup).to_ulong());
346 return false;
348 if (errorsBackup.test(HAS_WARNINGS)) {
349 ShowEepromWarnings(this, tr("Warning"), errorsBackup.to_ulong());
351 } else if (errorsEeprom.test(HAS_WARNINGS)) {
352 ShowEepromWarnings(this, tr("Warning"), errorsEeprom.to_ulong());
355 ui->modelsList->refreshList();
356 if(resetCurrentFile) setCurrentFile(fileName);
358 free(eeprom);
359 return true;
362 return false;
365 bool MdiChild::save()
367 if (isUntitled) {
368 return saveAs(true);
370 else {
371 return saveFile(curFile);
375 bool MdiChild::saveAs(bool isNew)
377 QString fileName;
378 if (IS_SKY9X(GetEepromInterface()->getBoard())) {
379 curFile.replace(".eepe", ".bin");
380 QFileInfo fi(curFile);
381 #ifdef __APPLE__
382 fileName = QFileDialog::getSaveFileName(this, tr("Save As"), g.eepromDir() + "/" +fi.fileName());
383 #else
384 fileName = QFileDialog::getSaveFileName(this, tr("Save As"), g.eepromDir() + "/" +fi.fileName(), tr(BIN_FILES_FILTER));
385 #endif
387 else {
388 QFileInfo fi(curFile);
389 #ifdef __APPLE__
390 fileName = QFileDialog::getSaveFileName(this, tr("Save As"), g.eepromDir() + "/" +fi.fileName());
391 #else
392 fileName = QFileDialog::getSaveFileName(this, tr("Save As"), g.eepromDir() + "/" +fi.fileName(), tr(EEPROM_FILES_FILTER));
393 #endif
395 if (fileName.isEmpty())
396 return false;
397 g.eepromDir( QFileInfo(fileName).dir().absolutePath() );
398 if (isNew)
399 return saveFile(fileName);
400 else
401 return saveFile(fileName,true);
404 bool MdiChild::saveFile(const QString &fileName, bool setCurrent)
406 QString myFile;
407 myFile = fileName;
408 if (IS_SKY9X(GetEepromInterface()->getBoard())) {
409 myFile.replace(".eepe", ".bin");
411 QFile file(myFile);
413 int fileType = getFileType(myFile);
415 uint8_t *eeprom = (uint8_t*)malloc(GetEepromInterface()->getEEpromSize());
416 int eeprom_size = 0;
418 if (fileType != FILE_TYPE_XML) {
419 eeprom_size = GetEepromInterface()->save(eeprom, radioData, GetCurrentFirmware()->getVariantNumber(), 0/*last version*/);
420 if (!eeprom_size) {
421 QMessageBox::warning(this, tr("Error"),tr("Cannot write file %1:\n%2.").arg(myFile).arg(file.errorString()));
422 return false;
426 if (!file.open(fileType == FILE_TYPE_BIN ? QIODevice::WriteOnly : (QIODevice::WriteOnly | QIODevice::Text))) {
427 QMessageBox::warning(this, tr("Error"),tr("Cannot write file %1:\n%2.").arg(myFile).arg(file.errorString()));
428 return false;
431 QTextStream outputStream(&file);
433 #if 0
434 if (fileType==FILE_TYPE_XML) {
435 if (!XmlInterface(outputStream).save(radioData)) {
436 QMessageBox::warning(this, tr("Error"),tr("Cannot write file %1:\n%2.").arg(myFile).arg(file.errorString()));
437 file.close();
438 return false;
441 else
442 #endif
443 if (fileType==FILE_TYPE_HEX || fileType==FILE_TYPE_EEPE) { // write hex
444 if (fileType==FILE_TYPE_EEPE)
445 outputStream << EEPE_EEPROM_FILE_HEADER << "\n";
447 if (!HexInterface(outputStream).save(eeprom, eeprom_size)) {
448 QMessageBox::warning(this, tr("Error"),tr("Cannot write file %1:\n%2.").arg(myFile).arg(file.errorString()));
449 file.close();
450 return false;
453 else if (fileType==FILE_TYPE_BIN) // write binary
455 long result = file.write((char*)eeprom, eeprom_size);
456 if(result!=eeprom_size) {
457 QMessageBox::warning(this, tr("Error"),tr("Error writing file %1:\n%2.").arg(myFile).arg(file.errorString()));
458 return false;
461 else {
462 QMessageBox::warning(this, tr("Error"),tr("Error writing file %1:\n%2.").arg(myFile).arg("Unknown format"));
463 return false;
466 free(eeprom); // TODO free in all cases ...
467 file.close();
468 if(setCurrent) setCurrentFile(myFile);
470 return true;
473 QString MdiChild::userFriendlyCurrentFile()
475 return strippedName(curFile);
478 void MdiChild::closeEvent(QCloseEvent *event)
480 if (maybeSave()) {
481 event->accept();
483 else {
484 event->ignore();
488 void MdiChild::documentWasModified()
490 setWindowModified(fileChanged);
493 bool MdiChild::maybeSave()
495 if (fileChanged) {
496 QMessageBox::StandardButton ret;
497 ret = QMessageBox::warning(this, tr("Companion"),
498 tr("%1 has been modified.\n"
499 "Do you want to save your changes?").arg(userFriendlyCurrentFile()),
500 QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
502 if (ret == QMessageBox::Save)
503 return save();
504 else if (ret == QMessageBox::Cancel)
505 return false;
507 return true;
510 void MdiChild::setCurrentFile(const QString &fileName)
512 curFile = QFileInfo(fileName).canonicalFilePath();
513 isUntitled = false;
514 fileChanged = false;
515 setWindowModified(false);
516 updateTitle();
517 int MaxRecentFiles = g.historySize();
518 QStringList files = g.recentFiles();
519 files.removeAll(fileName);
520 files.prepend(fileName);
521 while (files.size() > MaxRecentFiles)
522 files.removeLast();
524 g.recentFiles( files );
527 QString MdiChild::strippedName(const QString &fullFileName)
529 return QFileInfo(fullFileName).fileName();
532 void MdiChild::writeEeprom() // write to Tx
534 QString tempFile = generateProcessUniqueTempFileName("temp.bin");
535 saveFile(tempFile, false);
536 if(!QFileInfo(tempFile).exists()) {
537 QMessageBox::critical(this, tr("Error"), tr("Cannot write temporary file!"));
538 return;
541 FlashEEpromDialog *cd = new FlashEEpromDialog(this, tempFile);
542 cd->exec();
545 void MdiChild::simulate()
547 if (getCurrentRow() > 0) {
548 startSimulation(this, radioData, getCurrentRow()-1);
552 void MdiChild::print(int model, QString filename)
554 PrintDialog * pd = NULL;
556 if (model>=0 && !filename.isEmpty()) {
557 pd = new PrintDialog(this, GetCurrentFirmware()/*firmware*/, radioData.generalSettings, radioData.models[model], filename);
559 else if (getCurrentRow() > 0) {
560 pd = new PrintDialog(this, GetCurrentFirmware()/*firmware*/, radioData.generalSettings, radioData.models[getCurrentRow()-1]);
563 if (pd) {
564 pd->setAttribute(Qt::WA_DeleteOnClose, true);
565 pd->show();
569 void MdiChild::viableModelSelected(bool viable)
571 emit copyAvailable(viable);
574 void MdiChild::setEEpromAvail(int eavail)
576 EEPromAvail=eavail;
579 int MdiChild::getCurrentRow() const
581 return ui->modelsList->currentRow();
584 bool MdiChild::loadBackup()
586 QString fileName = QFileDialog::getOpenFileName(this, tr("Open backup Models and Settings file"), g.eepromDir(),tr(EEPROM_FILES_FILTER));
587 if (fileName.isEmpty())
588 return false;
589 QFile file(fileName);
591 if (!file.exists()) {
592 QMessageBox::critical(this, tr("Error"), tr("Unable to find file %1!").arg(fileName));
593 return false;
595 if(getCurrentRow() < 1) return false;
596 int index = getCurrentRow() - 1;
598 int eeprom_size = file.size();
599 if (!file.open(QFile::ReadOnly)) { //reading binary file - TODO HEX support
600 QMessageBox::critical(this, tr("Error"),
601 tr("Error opening file %1:\n%2.")
602 .arg(fileName)
603 .arg(file.errorString()));
604 return false;
606 QByteArray eeprom(eeprom_size, 0);
607 long result = file.read((char*)eeprom.data(), eeprom_size);
608 file.close();
610 if (result != eeprom_size) {
611 QMessageBox::critical(this, tr("Error"),
612 tr("Error reading file %1:\n%2.")
613 .arg(fileName)
614 .arg(file.errorString()));
616 return false;
619 std::bitset<NUM_ERRORS> errorsEeprom((unsigned long long)LoadBackup(radioData, (uint8_t *)eeprom.data(), eeprom_size, index));
620 if (!errorsEeprom.test(ALL_OK)) {
621 ShowEepromErrors(this, tr("Error"), tr("Invalid binary backup File %1").arg(fileName), (errorsEeprom).to_ulong());
622 return false;
624 if (errorsEeprom.test(HAS_WARNINGS)) {
625 ShowEepromWarnings(this, tr("Warning"), errorsEeprom.to_ulong());
628 ui->modelsList->refreshList();
630 return true;