5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
22 #include "ui_mdichild.h"
23 #include "modeledit/modeledit.h"
24 #include "generaledit/generaledit.h"
25 #include "burnconfigdialog.h"
26 #include "printdialog.h"
27 #include "flasheepromdialog.h"
30 #include "wizarddialog.h"
31 #include "flashfirmwaredialog.h"
33 #include "radiointerface.h"
37 MdiChild::MdiChild(QWidget
* parent
, QWidget
* parentWin
, Qt::WindowFlags f
):
40 modelsListModel(NULL
),
41 parentWindow(parentWin
),
43 categoriesToolbar(NULL
),
45 firmware(getCurrentFirmware()),
46 lastSelectedModel(-1),
52 setWindowIcon(CompanionIcon("open.png"));
53 setAttribute(Qt::WA_DeleteOnClose
);
54 setContextMenuPolicy(Qt::CustomContextMenu
);
56 parentWindow
->setWindowIcon(windowIcon());
61 ui
->modelsList
->setContextMenuPolicy(Qt::CustomContextMenu
);
62 ui
->modelsList
->setSelectionBehavior(QAbstractItemView::SelectRows
);
63 ui
->modelsList
->setSelectionMode(QAbstractItemView::ExtendedSelection
);
64 ui
->modelsList
->setEditTriggers(QAbstractItemView::DoubleClicked
| QAbstractItemView::EditKeyPressed
);
65 ui
->modelsList
->setDragEnabled(true);
66 ui
->modelsList
->setAcceptDrops(true);
67 ui
->modelsList
->setDragDropOverwriteMode(false);
68 ui
->modelsList
->setDropIndicatorShown(true);
69 ui
->modelsList
->setDragDropMode(QAbstractItemView::DragDrop
);
70 ui
->modelsList
->setStyle(new ItemViewProxyStyle(ui
->modelsList
->style()));
71 ui
->modelsList
->setStyleSheet("QTreeView::item {margin: 2px 0;}"); // a little more space for our drop indicators
75 connect(this, &MdiChild::customContextMenuRequested
, this, &MdiChild::showContextMenu
);
76 connect(ui
->modelsList
, &QTreeView::activated
, this, &MdiChild::onItemActivated
);
77 connect(ui
->modelsList
, &QTreeView::customContextMenuRequested
, this, &MdiChild::showModelsListContextMenu
);
78 connect(ui
->modelsList
, &QTreeView::pressed
, this, &MdiChild::onItemSelected
);
79 connect(ui
->modelsList
->selectionModel(), &QItemSelectionModel::currentRowChanged
, this, &MdiChild::onCurrentItemChanged
);
81 if (!(isMaximized() || isMinimized())) {
82 QByteArray geo
= g
.mdiWinGeo();
85 else if (geo
.size() < 10 && geo
== "maximized") {
86 if (!parentWindow
) // otherwise we let the MdiArea manage the window maximizing
87 setWindowState(windowState() ^ Qt::WindowMaximized
);
89 else if (parentWindow
)
90 parentWindow
->restoreGeometry(geo
);
94 if (!g
.mdiWinState().isEmpty()) {
95 QByteArray state
= g
.mdiWinState();
96 QDataStream
stream(&state
, QIODevice::ReadOnly
);
99 if (ver
<= stateDataVersion
) {
101 stream
>> showCatToolbar
>> visMdl
>> visGen
;
102 categoriesToolbar
->setVisible(showCatToolbar
);
103 modelsToolbar
->setVisible(visMdl
);
104 radioToolbar
->setVisible(visGen
);
109 MdiChild::~MdiChild()
114 void MdiChild::closeEvent(QCloseEvent
*event
)
120 if (!isMinimized()) {
123 geo
.append("maximized");
124 else if (parentWindow
)
125 geo
= parentWindow
->saveGeometry();
127 geo
= saveGeometry();
132 QDataStream
stream(&state
, QIODevice::WriteOnly
);
133 stream
<< stateDataVersion
134 << (firmware
->getCapability(Capability::HasModelCategories
) ? categoriesToolbar
->isVisible() : showCatToolbar
)
135 << modelsToolbar
->isVisible()
136 << radioToolbar
->isVisible();
137 g
.mdiWinState(state
);
142 void MdiChild::resizeEvent(QResizeEvent
* event
)
144 QWidget::resizeEvent(event
);
145 adjustToolbarLayout();
148 QSize
MdiChild::sizeHint() const
152 p
= parentWindow
->parentWidget();
156 return QWidget::sizeHint();
157 // use toolbar as a gauge for width, and take all the height availabe.
158 int w
= qMax(ui
->topToolbarLayout
->sizeHint().width(), ui
->botToolbarLayout
->sizeHint().width());
159 return QSize(w
+ 30, qMin(p
->height(), 1000));
162 void MdiChild::changeEvent(QEvent
* event
)
164 QWidget::changeEvent(event
);
165 switch (event
->type()) {
166 case QEvent::LanguageChange
:
174 QAction
* MdiChild::addAct(Actions actId
, const QString
& icon
, const char * slot
, const QKeySequence
& shortcut
, QObject
* slotObj
)
176 QAction
* newAction
= new QAction(this);
177 newAction
->setMenuRole(QAction::NoRole
);
179 newAction
->setIcon(CompanionIcon(icon
));
180 if (!shortcut
.isEmpty())
181 newAction
->setShortcut(shortcut
);
185 connect(newAction
, SIGNAL(triggered()), slotObj
, slot
);
186 action
.replace(actId
, newAction
);
190 void MdiChild::setupNavigation()
192 foreach (QAction
* act
, action
) {
197 action
.fill(NULL
, ACT_ENUM_END
);
199 addAct(ACT_GEN_EDT
, "edit.png", SLOT(generalEdit()), tr("Alt+Shift+E"));
200 addAct(ACT_GEN_CPY
, "copy.png", SLOT(copyGeneralSettings()), tr("Ctrl+Alt+C"));
201 addAct(ACT_GEN_PST
, "paste.png", SLOT(pasteGeneralSettings()), tr("Ctrl+Alt+V"));
202 addAct(ACT_GEN_SIM
, "simulate.png", SLOT(radioSimulate()), tr("Alt+Shift+S"));
204 addAct(ACT_ITM_EDT
, "edit.png", SLOT(edit()), Qt::Key_Enter
);
205 addAct(ACT_ITM_DEL
, "clear.png", SLOT(confirmDelete()), QKeySequence::Delete
);
207 addAct(ACT_CAT_ADD
, "add.png", SLOT(categoryAdd()), tr("Alt+C"));
208 //addAct(ACT_CAT_EDT, "edit.png", SLOT(edit()), Qt::Key_Enter);
209 //addAct(ACT_CAT_DEL, "clear.png", SLOT(confirmDelete()), QKeySequence::Delete);
210 action
[ACT_CAT_SEP
] = new QAction(this);
211 action
[ACT_CAT_SEP
]->setSeparator(true);
213 addAct(ACT_MDL_ADD
, "add.png", SLOT(modelAdd()), tr("Alt+A"));
214 addAct(ACT_MDL_RTR
, "open.png", SLOT(loadBackup()), tr("Alt+R"));
215 addAct(ACT_MDL_WIZ
, "wizard.png", SLOT(wizardEdit()), tr("Alt+W"));
217 addAct(ACT_MDL_DFT
, "currentmodel.png", SLOT(setDefault()), tr("Alt+U"));
218 addAct(ACT_MDL_PRT
, "print.png", SLOT(print()), QKeySequence::Print
);
219 addAct(ACT_MDL_SIM
, "simulate.png", SLOT(modelSimulate()), tr("Alt+S"));
220 addAct(ACT_MDL_DUP
, "duplicate.png", SLOT(modelDuplicate()), QKeySequence::Underline
);
222 addAct(ACT_MDL_CUT
, "cut.png", SLOT(cut()), QKeySequence::Cut
);
223 addAct(ACT_MDL_CPY
, "copy.png", SLOT(copy()), QKeySequence::Copy
);
224 addAct(ACT_MDL_PST
, "paste.png", SLOT(paste()), QKeySequence::Paste
);
225 addAct(ACT_MDL_INS
, "list.png", SLOT(insert()), QKeySequence::Italic
);
227 addAct(ACT_MDL_MOV
, "arrow-right.png");
228 QMenu
* catsMenu
= new QMenu(this);
229 action
[ACT_MDL_MOV
]->setMenu(catsMenu
);
231 // set up the toolbars
234 QSize
tbIcnSz(16, 16);
235 QString tbCss
= "QToolBar {border: 1px solid palette(midlight);}";
237 if (categoriesToolbar
)
238 categoriesToolbar
->deleteLater();
239 categoriesToolbar
= new QToolBar(this);
240 categoriesToolbar
->setObjectName("TB_CATEGORIES");
241 categoriesToolbar
->setToolButtonStyle(Qt::ToolButtonTextBesideIcon
);
242 categoriesToolbar
->setFloatable(false);
243 categoriesToolbar
->setIconSize(tbIcnSz
);
244 categoriesToolbar
->setStyleSheet(tbCss
);
245 categoriesToolbar
->addAction(getAction(ACT_CAT_ADD
));
246 ui
->topToolbarLayout
->addWidget(categoriesToolbar
);
249 radioToolbar
->deleteLater();
250 radioToolbar
= new QToolBar(this);
251 radioToolbar
->setObjectName("TB_GENERAL");
252 radioToolbar
->setFloatable(false);
253 radioToolbar
->setIconSize(tbIcnSz
);
254 radioToolbar
->setStyleSheet(tbCss
);
255 radioToolbar
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Fixed
);
256 radioToolbar
->addActions(getGeneralActions());
257 if ((btn
= qobject_cast
<QToolButton
*>(radioToolbar
->widgetForAction(action
[ACT_GEN_EDT
])))) {
258 btn
->setToolButtonStyle(Qt::ToolButtonTextBesideIcon
);
260 if ((btn
= qobject_cast
<QToolButton
*>(radioToolbar
->widgetForAction(action
[ACT_GEN_SIM
])))) {
261 btn
->setToolButtonStyle(Qt::ToolButtonTextBesideIcon
);
263 // add spacer to right-align the buttons
264 QWidget
* sp
= new QWidget(this);
265 sp
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Fixed
);
266 radioToolbar
->insertWidget(radioToolbar
->actions().first(), sp
);
267 ui
->topToolbarLayout
->addWidget(radioToolbar
);
270 modelsToolbar
->deleteLater();
271 modelsToolbar
= new QToolBar(this);
272 modelsToolbar
->setObjectName("TB_MODELS");
273 modelsToolbar
->setFloatable(false);
274 modelsToolbar
->setIconSize(tbIcnSz
);
275 modelsToolbar
->setStyleSheet(tbCss
);
276 modelsToolbar
->addActions(getEditActions(false));
277 modelsToolbar
->addSeparator();
278 modelsToolbar
->addActions(getModelActions());
279 if ((btn
= qobject_cast
<QToolButton
*>(modelsToolbar
->widgetForAction(action
[ACT_MDL_ADD
])))) {
280 btn
->setToolButtonStyle(Qt::ToolButtonTextBesideIcon
);
282 if ((btn
= qobject_cast
<QToolButton
*>(modelsToolbar
->widgetForAction(action
[ACT_MDL_MOV
])))) {
283 btn
->setPopupMode(QToolButton::InstantPopup
);
285 ui
->botToolbarLayout
->addWidget(modelsToolbar
);
287 connect(categoriesToolbar
, &QToolBar::visibilityChanged
, this, &MdiChild::adjustToolbarLayout
);
288 connect(radioToolbar
, &QToolBar::visibilityChanged
, this, &MdiChild::adjustToolbarLayout
);
289 connect(modelsToolbar
, &QToolBar::visibilityChanged
, this, &MdiChild::adjustToolbarLayout
);
292 void MdiChild::updateNavigation()
294 const int modelsSelected
= countSelectedModels();
295 const int catsSelected
= countSelectedCats();
296 const bool singleModelSelected
= (modelsSelected
== 1);
297 const bool hasModelSlotSelcted
= (getCurrentModel() > -1);
298 const bool hasCats
= firmware
->getCapability(Capability::HasModelCategories
);
299 const bool hasCatSelected
= hasCats
&& modelsListModel
->isCategoryType(getCurrentIndex());
300 const int numOnClipbrd
= modelsListModel
->countModelsInMimeData(QApplication::clipboard()->mimeData());
301 const QString modelsRemvTxt
= tr("%n Model(s)", "As in \"Copy 3 Models\" or \"Cut 1 Model\" or \"Delete 3 Models\" action).", modelsSelected
);
302 const QString modelsAddTxt
= tr("%n Model(s)", "As in \"Paste 3 Models\" or \"Insert 1 Model.\"", numOnClipbrd
);
303 const QString catsRemvTxt
= tr("%n Category(ies)", "As in \"Delete 3 Categories\" or \"Delete 1 Category.\"", catsSelected
);
304 static const QString noSelection
= tr("Nothing selected");
305 static const QString sp
= " ";
306 static const QString ns
;
308 categoriesToolbar
->setVisible(hasCats
&& showCatToolbar
);
310 action
[ACT_GEN_PST
]->setEnabled(hasClipboardData(1));
312 action
[ACT_ITM_EDT
]->setEnabled(singleModelSelected
|| catsSelected
== 1);
313 action
[ACT_ITM_EDT
]->setText((hasCatSelected
? tr("Rename Category") : modelsSelected
? tr("Edit Model") : noSelection
));
314 action
[ACT_ITM_DEL
]->setEnabled(modelsSelected
|| catsSelected
);
315 action
[ACT_ITM_DEL
]->setText((action
[ACT_ITM_DEL
]->isEnabled() ? tr("Delete") % " " % (hasCatSelected
? catsRemvTxt
: modelsRemvTxt
) : noSelection
));
317 action
[ACT_CAT_ADD
]->setVisible(hasCats
);
318 action
[ACT_CAT_SEP
]->setVisible(hasCats
);
319 // action[ACT_CAT_EDT]->setVisible(hasCats);
320 // action[ACT_CAT_EDT]->setEnabled(catsSelected == 1);
321 // action[ACT_CAT_DEL]->setVisible(hasCats);
322 // action[ACT_CAT_DEL]->setEnabled(catsSelected);
323 // action[ACT_CAT_DEL]->setText((hasCatSelected ? tr("Delete") % " " % catsRemTxt : noSelection));
325 action
[ACT_MDL_CUT
]->setEnabled(modelsSelected
);
326 action
[ACT_MDL_CUT
]->setText(tr("Cut") % (modelsSelected
? sp
% modelsRemvTxt
: ns
));
327 action
[ACT_MDL_CPY
]->setEnabled(modelsSelected
);
328 action
[ACT_MDL_CPY
]->setText(tr("Copy") % (modelsSelected
? sp
% modelsRemvTxt
: ns
));
329 action
[ACT_MDL_PST
]->setEnabled(numOnClipbrd
);
330 action
[ACT_MDL_PST
]->setText(tr("Paste") % (numOnClipbrd
? sp
% modelsAddTxt
: ns
));
331 action
[ACT_MDL_INS
]->setEnabled(numOnClipbrd
&& (hasModelSlotSelcted
|| catsSelected
));
332 action
[ACT_MDL_INS
]->setText(tr("Insert") % QString(action
[ACT_MDL_INS
]->isEnabled() ? sp
% modelsAddTxt
: ns
));
334 if (hasCats
&& action
[ACT_MDL_MOV
]->menu()) {
335 action
[ACT_MDL_MOV
]->setVisible(true);
336 QModelIndex modelIndex
= getCurrentIndex();
337 QMenu
* catsMenu
= action
[ACT_MDL_MOV
]->menu();
339 if (modelsSelected
&& modelsListModel
&& radioData
.categories
.size() > 1) {
340 action
[ACT_MDL_MOV
]->setEnabled(true);
341 for (unsigned i
=0; i
< radioData
.categories
.size(); ++i
) {
342 QAction
* act
= catsMenu
->addAction(QString(radioData
.categories
[i
].name
), this, SLOT(onModelMoveToCategory()));
343 act
->setProperty("categoryId", i
);
344 if ((int)i
< modelsListModel
->getCategoryIndex(modelIndex
))
345 act
->setIcon(CompanionIcon("moveup.png"));
346 else if ((int)i
> modelsListModel
->getCategoryIndex(modelIndex
))
347 act
->setIcon(CompanionIcon("movedown.png"));
349 act
->setEnabled(false);
353 action
[ACT_MDL_MOV
]->setDisabled(true);
357 action
[ACT_MDL_MOV
]->setVisible(false);
360 action
[ACT_MDL_DUP
]->setEnabled(singleModelSelected
);
361 action
[ACT_MDL_RTR
]->setEnabled(singleModelSelected
);
362 action
[ACT_MDL_WIZ
]->setEnabled(singleModelSelected
);
363 action
[ACT_MDL_DFT
]->setEnabled(singleModelSelected
&& getCurrentModel() != (int)radioData
.generalSettings
.currModelIndex
);
364 action
[ACT_MDL_PRT
]->setEnabled(singleModelSelected
);
365 action
[ACT_MDL_SIM
]->setEnabled(singleModelSelected
);
368 void MdiChild::retranslateUi()
370 action
[ACT_GEN_EDT
]->setText(tr("Edit Radio Settings"));
371 action
[ACT_GEN_CPY
]->setText(tr("Copy Radio Settings"));
372 action
[ACT_GEN_PST
]->setText(tr("Paste Radio Settings"));
373 action
[ACT_GEN_SIM
]->setText(tr("Simulate Radio"));
375 action
[ACT_CAT_ADD
]->setText(tr("Add Category"));
376 action
[ACT_CAT_ADD
]->setIconText(tr("Category"));
377 //action[ACT_CAT_EDT]->setText(tr("Rename Category"));
379 action
[ACT_MDL_ADD
]->setText(tr("Add Model"));
380 action
[ACT_MDL_ADD
]->setIconText(tr("Model"));
381 action
[ACT_MDL_RTR
]->setText(tr("Restore from Backup"));
382 action
[ACT_MDL_WIZ
]->setText(tr("Model Wizard"));
383 action
[ACT_MDL_DFT
]->setText(tr("Set as Default"));
384 action
[ACT_MDL_PRT
]->setText(tr("Print Model"));
385 action
[ACT_MDL_SIM
]->setText(tr("Simulate Model"));
386 action
[ACT_MDL_DUP
]->setText(tr("Duplicate Model"));
388 action
[ACT_MDL_MOV
]->setText(tr("Move to Category"));
390 categoriesToolbar
->setWindowTitle(tr("Show Category Actions Toolbar"));
391 radioToolbar
->setWindowTitle(tr("Show Radio Actions Toolbar"));
392 modelsToolbar
->setWindowTitle(tr("Show Model Actions Toolbar"));
395 QList
<QAction
*> MdiChild::getGeneralActions()
397 QList
<QAction
*> actGrp
;
398 actGrp
.append(getAction(ACT_GEN_SIM
));
399 actGrp
.append(getAction(ACT_GEN_EDT
));
400 actGrp
.append(getAction(ACT_GEN_CPY
));
401 actGrp
.append(getAction(ACT_GEN_PST
));
405 QList
<QAction
*> MdiChild::getEditActions(bool incCatNew
)
407 QList
<QAction
*> actGrp
;
409 actGrp
.append(getAction(ACT_CAT_ADD
));
410 actGrp
.append(getAction(ACT_CAT_SEP
));
412 actGrp
.append(action
[ACT_MDL_ADD
]);
413 QAction
* sep2
= new QAction(this);
414 sep2
->setSeparator(true);
416 actGrp
.append(getAction(ACT_ITM_EDT
));
417 actGrp
.append(getAction(ACT_ITM_DEL
));
418 actGrp
.append(getAction(ACT_MDL_CUT
));
419 actGrp
.append(getAction(ACT_MDL_CPY
));
420 actGrp
.append(getAction(ACT_MDL_PST
));
421 actGrp
.append(getAction(ACT_MDL_INS
));
422 actGrp
.append(getAction(ACT_MDL_DUP
));
423 actGrp
.append(getAction(ACT_MDL_MOV
));
427 QList
<QAction
*> MdiChild::getModelActions()
429 QList
<QAction
*> actGrp
;
430 actGrp
.append(getAction(ACT_MDL_RTR
));
431 actGrp
.append(getAction(ACT_MDL_WIZ
));
432 actGrp
.append(getAction(ACT_MDL_DFT
));
433 actGrp
.append(getAction(ACT_MDL_PRT
));
434 actGrp
.append(getAction(ACT_MDL_SIM
));
438 //QList<QAction *> MdiChild::getCategoryActions()
440 // QList<QAction *> actGrp;
441 // actGrp.append(getAction(ACT_CAT_ADD));
442 // actGrp.append(getAction(ACT_CAT_EDT));
443 // actGrp.append(getAction(ACT_CAT_DEL));
444 // actGrp.append(getAction(ACT_CAT_SEP));
448 QAction
* MdiChild::getAction(const MdiChild::Actions type
)
450 if (type
< ACT_ENUM_END
)
456 void MdiChild::showModelsListContextMenu(const QPoint
& pos
)
458 QModelIndex modelIndex
= ui
->modelsList
->indexAt(pos
);
463 if (firmware
->getCapability(Capability::HasModelCategories
)) {
464 contextMenu
.addAction(action
[ACT_CAT_ADD
]);
465 if(modelsListModel
->isCategoryType(modelIndex
)) {
466 contextMenu
.addAction(action
[ACT_ITM_EDT
]);
467 contextMenu
.addAction(action
[ACT_ITM_DEL
]);
469 contextMenu
.addSeparator();
472 if (modelsListModel
->isModelType(modelIndex
)) {
473 contextMenu
.addActions(getEditActions());
474 if (countSelectedModels() == 1) {
475 contextMenu
.addSeparator();
476 contextMenu
.addActions(getModelActions());
480 contextMenu
.addAction(action
[ACT_MDL_ADD
]);
481 if (hasClipboardData())
482 contextMenu
.addAction(action
[ACT_MDL_PST
]);
485 if (!contextMenu
.isEmpty())
486 contextMenu
.exec(ui
->modelsList
->mapToGlobal(pos
));
489 void MdiChild::showContextMenu(const QPoint
& pos
)
492 if (firmware
->getCapability(Capability::HasModelCategories
))
493 contextMenu
.addAction(categoriesToolbar
->toggleViewAction());
494 contextMenu
.addAction(modelsToolbar
->toggleViewAction());
495 contextMenu
.addAction(radioToolbar
->toggleViewAction());
496 if (!contextMenu
.isEmpty())
497 contextMenu
.exec(mapToGlobal(pos
));
500 void MdiChild::adjustToolbarLayout()
502 if (size().width() > ui
->topToolbarLayout
->sizeHint().width() + ui
->botToolbarLayout
->sizeHint().width() + 30) {
503 ui
->botToolbarLayout
->removeWidget(modelsToolbar
);
504 ui
->topToolbarLayout
->insertWidget(1, modelsToolbar
);
507 ui
->topToolbarLayout
->removeWidget(modelsToolbar
);
508 ui
->botToolbarLayout
->insertWidget(0, modelsToolbar
);
516 void MdiChild::initModelsList()
518 Board::Type board
= firmware
->getBoard();
521 delete modelsListModel
;
523 modelsListModel
= new TreeModel(&radioData
, this);
524 connect(modelsListModel
, &TreeModel::modelsDropped
, this, &MdiChild::pasteModelData
);
525 connect(modelsListModel
, &TreeModel::modelsRemoved
, this, &MdiChild::deleteModels
);
526 connect(modelsListModel
, &TreeModel::refreshRequested
, this, &MdiChild::refresh
);
527 connect(modelsListModel
, &QAbstractItemModel::dataChanged
, this, &MdiChild::onDataChanged
);
529 ui
->modelsList
->setModel(modelsListModel
);
530 ui
->modelsList
->header()->setVisible(!firmware
->getCapability(Capability::HasModelCategories
));
531 if (IS_HORUS(board
)) {
532 ui
->modelsList
->setIndentation(20);
533 // ui->modelsList->resetIndentation(); // introduced in next Qt versions ...
536 ui
->modelsList
->setIndentation(0);
541 void MdiChild::refresh()
543 bool expand
= true; // TODO: restore user-preferred state
545 modelsListModel
->refresh();
547 ui
->modelsList
->expandAll();
548 if (lastSelectedModel
> -1) {
549 setSelectedModel(lastSelectedModel
);
550 lastSelectedModel
= -1;
556 void MdiChild::onItemActivated(const QModelIndex index
)
558 if (modelsListModel
->isModelType(index
)) {
559 int mIdx
= modelsListModel
->getModelIndex(index
);
560 if (mIdx
< 0 || mIdx
>= (int)radioData
.models
.size())
562 if (radioData
.models
[mIdx
].isEmpty())
565 openModelEditWindow(mIdx
);
567 else if (modelsListModel
->isCategoryType(index
)) {
568 ui
->modelsList
->edit(index
);
572 void MdiChild::onItemSelected(const QModelIndex
&)
577 void MdiChild::onCurrentItemChanged(const QModelIndex
&, const QModelIndex
&)
582 void MdiChild::onDataChanged(const QModelIndex
& index
)
584 if (!modelsListModel
->isCategoryType(index
))
587 int categoryIndex
= modelsListModel
->getCategoryIndex(index
);
588 if (categoryIndex
< 0 || categoryIndex
>= (int)radioData
.categories
.size()) {
591 strcpy(radioData
.categories
[categoryIndex
].name
, modelsListModel
->data(index
, 0).toString().left(sizeof(CategoryData::name
)-1).toStdString().c_str());
593 setWindowModified(true);
598 * Get info from data model
601 QModelIndex
MdiChild::getCurrentIndex() const
603 return ui
->modelsList
->selectionModel()->currentIndex();
606 int MdiChild::getCurrentCategory() const
608 return modelsListModel
->getCategoryIndex(getCurrentIndex());
611 int MdiChild::countSelectedCats() const
615 foreach (QModelIndex index
, ui
->modelsList
->selectionModel()->selectedRows()) {
616 if (index
.isValid() && modelsListModel
->isCategoryType(index
))
622 bool MdiChild::hasSelectedCat()
624 return modelsListModel
->isCategoryType(getCurrentIndex());
627 QVector
<int> MdiChild::getSelectedCategories() const
630 foreach (QModelIndex index
, ui
->modelsList
->selectionModel()->selectedRows()) {
631 if (index
.isValid() && modelsListModel
->isCategoryType(index
))
632 cats
.append(modelsListModel
->getCategoryIndex(index
));
637 int MdiChild::getCurrentModel() const
639 return modelsListModel
->getModelIndex(getCurrentIndex());
642 int MdiChild::countSelectedModels() const
646 foreach (QModelIndex index
, ui
->modelsList
->selectionModel()->selectedRows()) {
647 if (index
.isValid() && modelsListModel
->isModelType(index
) && !radioData
.models
.at(modelsListModel
->getModelIndex(index
)).isEmpty())
653 bool MdiChild::hasSelectedModel()
655 return modelsListModel
->isModelType(getCurrentIndex());
658 bool MdiChild::setSelectedModel(const int modelIndex
)
660 QModelIndex idx
= modelsListModel
->getIndexForModel(modelIndex
);
662 ui
->modelsList
->scrollTo(idx
);
663 ui
->modelsList
->setCurrentIndex(idx
);
669 QVector
<int> MdiChild::getSelectedModels() const
672 foreach (QModelIndex index
, ui
->modelsList
->selectionModel()->selectedRows()) {
673 if (index
.isValid() && modelsListModel
->isModelType(index
))
674 models
.append(modelsListModel
->getModelIndex(index
));
680 * Misc. internal event handlers
683 void MdiChild::updateTitle()
685 QString title
= "[*]" + userFriendlyCurrentFile(); // + " (" + firmware->getName() + QString(")");
686 int availableEEpromSize
= modelsListModel
->getAvailableEEpromSize();
687 if (availableEEpromSize
>= 0) {
688 title
+= QString(" - %1 ").arg(availableEEpromSize
) + tr("free bytes");
690 setWindowTitle(title
);
693 void MdiChild::setModified()
696 setWindowModified(true);
700 void MdiChild::onFirmwareChanged()
702 Firmware
* previous
= firmware
;
703 firmware
= getCurrentFirmware();
704 //qDebug() << "onFirmwareChanged" << previous->getName() << "=>" << firmware->getName();
705 if (StorageFormat::getFourCC(previous
->getBoard()) != StorageFormat::getFourCC(firmware
->getBoard())) {
706 convertStorage(previous
->getBoard(), firmware
->getBoard());
715 void MdiChild::categoryAdd()
717 /*: Translators do NOT use accent for this, this is the default category name on Horus. */
718 CategoryData
category(qPrintable(tr("New category")));
719 radioData
.categories
.push_back(category
);
721 QModelIndex idx
= modelsListModel
->getIndexForCategory(radioData
.categories
.size()-1);
723 ui
->modelsList
->scrollTo(idx
);
724 ui
->modelsList
->setCurrentIndex(idx
);
725 ui
->modelsList
->edit(idx
);
729 // NOTE: this does not refresh the data/view, need to call setModified() at some point afterwards.
730 bool MdiChild::deleteCategory(int categoryIndex
, QString
* error
)
732 if (categoryIndex
< 0)
733 categoryIndex
= modelsListModel
->getCategoryIndex(getCurrentIndex());
734 if (categoryIndex
< 0 || categoryIndex
>= (int)radioData
.categories
.size()) {
736 *error
= tr("Category index out of range.");
739 if (radioData
.categories
.size() <= 1) {
741 *error
= tr("Cannot delete the last category.");
744 if (countUsedModels(categoryIndex
)) {
746 *error
= tr("This category is not empty!");
750 radioData
.categories
.erase(radioData
.categories
.begin() + categoryIndex
);
752 for (unsigned i
=0; i
< radioData
.models
.size(); ++i
) {
753 ModelData
& model
= radioData
.models
.at(i
);
754 if (model
.used
&& model
.category
> categoryIndex
) {
755 --radioData
.models
[i
].category
;
762 void MdiChild::deleteSelectedCats()
764 bool modified
= false;
766 QVector
<int> cats
= getSelectedCategories();
767 std::sort(cats
.begin(), cats
.end());
768 std::reverse(cats
.begin(), cats
.end());
769 foreach (const int cat
, cats
) {
771 if (deleteCategory(cat
, &error
))
784 void MdiChild::checkAndInitModel(int row
)
786 if (row
< (int)radioData
.models
.size() && radioData
.models
[row
].isEmpty()) {
787 radioData
.models
[row
].setDefaultValues(row
, radioData
.generalSettings
);
792 void MdiChild::findNewDefaultModel(const unsigned startAt
)
794 for (unsigned i
= startAt
; i
< radioData
.models
.size(); ++i
) {
795 if (!radioData
.models
[i
].isEmpty()) {
796 radioData
.setCurrentModel(i
);
801 findNewDefaultModel(0); // restart search from beginning
803 radioData
.setCurrentModel(0);
806 // NOTE: insertModelRows() does not update the TreeModel, only modifies radioData.models[] array by inserting row(s) of blank model(s).
807 // TreeModel::refresh() needs to be called at some point afterwards to sync the data.
808 // This invalidates any model indices stored previously.
809 bool MdiChild::insertModelRows(int atModelIdx
, int count
)
811 const unsigned maxModels
= firmware
->getCapability(Models
);
815 for (int i
=0; i
< count
; ++i
) {
816 //qDebug() << atRow << atRow.row() + i << modelIdx + i << maxModels << radioData.models.size();
817 if (maxModels
> 0 && radioData
.models
.size() >= maxModels
) {
818 // trim the array, unless the last model slot is being used.
819 if (radioData
.models
[maxModels
-1].isEmpty()) {
820 radioData
.models
.pop_back();
823 showWarning(tr("Cannot insert model, last model in list would be deleted."));
824 return false; // TODO: perhaps something more elegant...
827 // add a placeholder model
828 radioData
.models
.insert(radioData
.models
.begin() + atModelIdx
+ i
, ModelData());
829 // adjust current model index if needed
830 if ((int)radioData
.generalSettings
.currModelIndex
>= atModelIdx
+ i
)
831 findNewDefaultModel(radioData
.generalSettings
.currModelIndex
+ 1);
836 // Finds the first empty slot and inserts the model into it. In case of category-style models, will append to end of list.
837 // Return -1 if no slot was found, otherwise new array index.
838 // TreeModel::refresh() needs to be called at some point afterwards to sync the data.
839 int MdiChild::modelAppend(const ModelData model
)
843 // try to find the next empty slot
844 for ( ; trySlot
< (int)radioData
.models
.size(); ++trySlot
) {
845 if (radioData
.models
[trySlot
].isEmpty()) {
847 radioData
.models
[newIdx
] = model
;
851 // if no empty slots then check if we can append it
852 if (newIdx
< 0 && (firmware
->getCapability(Models
) == 0 || trySlot
< firmware
->getCapability(Models
) - 1)) {
853 radioData
.models
.push_back(model
);
854 newIdx
= radioData
.models
.size() - 1;
859 int MdiChild::newModel(int modelIndex
, int categoryIndex
)
862 modelIndex
= modelAppend(ModelData());
864 if (modelIndex
< 0 || modelIndex
>= (int)radioData
.models
.size()) {
865 showWarning(tr("Cannot add model, could not find an available model slot."));
869 if (categoryIndex
< 0)
870 categoryIndex
= modelsListModel
->getCategoryIndex(getCurrentIndex());
872 bool isNewModel
= radioData
.models
[modelIndex
].isEmpty();
873 checkAndInitModel(modelIndex
);
874 if (isNewModel
&& firmware
->getCapability(Capability::HasModelCategories
) && categoryIndex
> -1) {
875 radioData
.models
[modelIndex
].category
= categoryIndex
;
876 strcpy(radioData
.models
[modelIndex
].filename
, radioData
.getNextModelFilename().toStdString().c_str());
877 /*: Translators: do NOT use accents here, this is a default model name. */
878 strcpy(radioData
.models
[modelIndex
].name
, qPrintable(tr("New model"))); // TODO: Why not just use existing default model name?
880 // Only set the default model if we just added the first one.
881 if (countUsedModels() == 1) {
882 radioData
.setCurrentModel(modelIndex
);
885 setSelectedModel(modelIndex
);
886 //qDebug() << modelIndex << categoryIndex << isNewModel;
888 if (isNewModel
&& g
.newModelAction() == 1)
889 openModelWizard(modelIndex
);
890 else if (g
.newModelAction() == 2)
891 openModelEditWindow(modelIndex
);
897 // NOTE: deleteModelss() does not update the TreeModel, only modifies radioData.models[] array by clearing the model data.
898 // If (removeModelSlotsWhenDeleting == true) then removes array rows entirely (and pads w/blank model at the end if needed).
899 // TreeModel::refresh() needs to be called at some point afterwards to sync the data
900 // We delete using stored indexes because actual indexes may change during inserts/deletes.
901 // Obviously this only works before the stored indexes get updated in TreeModel::refresh().
902 unsigned MdiChild::deleteModels(const QVector
<int> modelIndices
)
904 unsigned deletes
= 0;
907 for (int i
= (int)radioData
.models
.size() - 1; i
> -1; --i
) {
908 idx
= radioData
.models
.at(i
).modelIndex
;
909 if (idx
> -1 && modelIndices
.contains(idx
)) {
910 radioData
.models
[i
].clear();
911 if (g
.removeModelSlots() || firmware
->getCapability(Models
) == 0) {
912 radioData
.models
.erase(radioData
.models
.begin() + i
);
913 // append padding rows at the end if needed
914 if (firmware
->getCapability(Models
) > 0)
915 insertModelRows(radioData
.models
.size(), 1);
918 // adjust current model index if needed
919 if ((int)radioData
.generalSettings
.currModelIndex
>= idx
)
920 findNewDefaultModel(qMax((int)radioData
.generalSettings
.currModelIndex
- 1, 0));
923 //qDebug() << "i:" << i << "modelIndex:" << idx << "deletes:" << deletes;
932 bool MdiChild::deleteModel(const int modelIndex
)
934 QVector
<int> list
= QVector
<int>() << modelIndex
;
935 if (deleteModels(list
) == 1)
941 void MdiChild::deleteSelectedModels()
943 deleteModels(getSelectedModels());
946 void MdiChild::moveModelsToCategory(const QVector
<int> models
, const int toCategoryId
)
948 if (toCategoryId
< 0 || !models
.size())
951 bool modified
= false;
952 //QVector<int> models = getSelectedModels();
953 foreach(const int model
, models
) {
954 if (model
< 0 || model
>= (int)radioData
.models
.size())
957 if (radioData
.models
[model
].category
!= toCategoryId
) {
958 radioData
.models
[model
].category
= toCategoryId
;
966 void MdiChild::moveSelectedModelsToCat(const int toCategoryId
)
968 moveModelsToCategory(getSelectedModels(), toCategoryId
);
971 unsigned MdiChild::countUsedModels(const int categoryId
)
974 for (unsigned i
=0; i
< radioData
.models
.size(); ++i
) {
975 ModelData
& model
= radioData
.models
.at(i
);
976 if (!model
.isEmpty() && (categoryId
< 0 || model
.category
== categoryId
))
982 void MdiChild::pasteModelData(const QMimeData
* mimeData
, const QModelIndex row
, bool insert
, bool move
)
984 QVector
<ModelData
> modelsList
;
985 if (!TreeModel::decodeMimeData(mimeData
, &modelsList
))
988 bool modified
= false;
989 int modelIdx
= modelsListModel
->getModelIndex(row
);
990 int categoryIdx
= modelsListModel
->getCategoryIndex(row
);
991 unsigned inserts
= 0;
992 QVector
<int> deletesList
;
994 // Force DnD moves from other file windows to be copy actions because we don't want to delete our models.
995 bool hasOwnData
= modelsListModel
->hasOwnMimeData(mimeData
);
996 move
= (move
&& hasOwnData
);
998 //qDebug().nospace() << "row: " << row << "; ins: " << insert << "; mv: " << move << "; row modelIdx: " << modelIdx << "; row categoryIdx: " << categoryIdx << "; removeSlots: " << removeModelSlotsWhenDeleting;
1001 for (int i
=0; i
< modelsList
.size(); ++i
) {
1002 int origMdlIdx
= hasOwnData
? modelsList
.at(i
).modelIndex
: -1; // where is the modeul in *our* current array?
1003 bool doMove
= (origMdlIdx
> -1 && origMdlIdx
< (int)radioData
.models
.size() && (move
|| cutModels
.contains(origMdlIdx
))); // DnD-moved or clipboard cut
1006 if (modelIdx
== -1 || (!insert
&& modelIdx
>= (int)radioData
.models
.size())) {
1007 // This handles pasting onto a category label or pasting past the end
1008 // of a category when pasting multiple models.
1009 modelIdx
= modelAppend(modelsList
[i
]);
1012 showWarning(tr("Cannot paste model, out of available model slots."));
1016 ok
= insertModelRows(modelIdx
, 1);
1018 radioData
.models
[modelIdx
] = modelsList
[i
];
1022 else { // pasting on top of a slot
1023 if (!radioData
.models
[modelIdx
].isEmpty() && !deletesList
.contains(modelIdx
))
1024 ok
= askQuestion(tr("You are replacing an existing model, are you sure?")) == QMessageBox::Yes
;
1026 radioData
.models
[modelIdx
] = modelsList
[i
];
1030 // We don't want to create an index value conflict so use an invalid one (it will get updated after we're done here)
1031 // this is esp. important because otherwise we may delete this model during a move operation (eg. after a cut)
1032 radioData
.models
[modelIdx
].modelIndex
= -modelIdx
;
1033 // Set the destination category, so a user can copy/paste across categories.
1034 if (categoryIdx
> -1)
1035 radioData
.models
[modelIdx
].category
= categoryIdx
;
1036 strcpy(radioData
.models
[modelIdx
].filename
, radioData
.getNextModelFilename().toStdString().c_str());
1037 lastSelectedModel
= modelIdx
; // after refresh the last pasted model will be selected
1040 deletesList
.append(origMdlIdx
);
1041 removeModelFromCutList(origMdlIdx
);
1044 //qDebug().nospace() << "i: " << i << "; modelIdx:" << modelIdx << "; origMdlIdx: " << origMdlIdx << "; doMove: " << doMove << "; inserts:" << inserts << "; deletes: " << deletesList;
1049 if (deletesList
.size()) {
1050 deleteModels(deletesList
);
1058 * General settings CRUD
1061 void MdiChild::pasteGeneralData(const QMimeData
* mimeData
)
1064 bool hasGenSettings
= false;
1066 if (!TreeModel::decodeMimeData(mimeData
, NULL
, &gs
, &hasGenSettings
))
1069 if (hasGenSettings
&& askQuestion(tr("Do you want to overwrite radio general settings?")) == QMessageBox::Yes
) {
1070 radioData
.generalSettings
= gs
;
1075 void MdiChild::generalEdit()
1077 GeneralEdit
* t
= new GeneralEdit(this, radioData
, firmware
);
1078 connect(t
, &GeneralEdit::modified
, this, &MdiChild::setModified
);
1082 void MdiChild::copyGeneralSettings()
1084 QMimeData
* mimeData
= modelsListModel
->getGeneralMimeData();
1085 modelsListModel
->getHeaderMimeData(mimeData
);
1086 QApplication::clipboard()->setMimeData(mimeData
, QClipboard::Clipboard
);
1090 void MdiChild::pasteGeneralSettings()
1092 if (hasClipboardData(1)) {
1093 pasteGeneralData(QApplication::clipboard()->mimeData());
1101 void MdiChild::copy()
1103 QMimeData
* mimeData
= modelsListModel
->getModelsMimeData(ui
->modelsList
->selectionModel()->selectedRows());
1104 modelsListModel
->getHeaderMimeData(mimeData
);
1105 QApplication::clipboard()->setMimeData(mimeData
, QClipboard::Clipboard
);
1107 clearCutList(); // clear the list by default, populate afterwards, eg. in cut().
1111 void MdiChild::cut()
1114 cutModels
= getSelectedModels();
1115 modelsListModel
->markItemsForCut(ui
->modelsList
->selectionModel()->selectedIndexes());
1118 void MdiChild::removeModelFromCutList(const int modelIndex
)
1120 int idx
= cutModels
.indexOf(modelIndex
);
1122 cutModels
.remove(idx
);
1123 modelsListModel
->markItemForCut(modelsListModel
->getIndexForModel(modelIndex
), false);
1127 void MdiChild::clearCutList()
1129 foreach (const int id
, cutModels
) {
1130 removeModelFromCutList(id
);
1134 // type = 0 for models (default), 1 for general radio data
1135 bool MdiChild::hasClipboardData(const quint8 type
) const
1138 return modelsListModel
->hasModelsMimeData(QApplication::clipboard()->mimeData());
1141 return modelsListModel
->hasGenralMimeData(QApplication::clipboard()->mimeData());
1145 void MdiChild::paste()
1147 if (hasClipboardData()) {
1148 pasteModelData(QApplication::clipboard()->mimeData(), getCurrentIndex());
1152 void MdiChild::insert()
1154 if (hasClipboardData()) {
1155 pasteModelData(QApplication::clipboard()->mimeData(), getCurrentIndex(), true);
1159 void MdiChild::edit()
1161 onItemActivated(getCurrentIndex());
1164 void MdiChild::confirmDelete()
1166 if (hasSelectedModel()) {
1167 if (!countSelectedModels() || askQuestion(tr("Delete %n selected model(s)?", 0, countSelectedModels())) == QMessageBox::Yes
) {
1168 deleteSelectedModels();
1171 else if (hasSelectedCat()) {
1172 if (askQuestion(tr("Delete %n selected category(ies)?", 0, countSelectedCats())) == QMessageBox::Yes
) {
1173 deleteSelectedCats();
1178 void MdiChild::modelAdd()
1181 // add to currently selected empty slot?
1182 if (modelsListModel
->isModelType(getCurrentIndex())) {
1183 int mIdx
= modelsListModel
->getModelIndex(getCurrentIndex());
1184 if (mIdx
> -1 && mIdx
< (int)radioData
.models
.size() && radioData
.models
[mIdx
].isEmpty()) {
1191 void MdiChild::modelDuplicate()
1193 int srcModelIndex
= getCurrentModel();
1194 if (srcModelIndex
< 0) {
1198 int newIdx
= modelAppend(ModelData(radioData
.models
[srcModelIndex
]));
1203 showWarning(tr("Cannot duplicate model, could not find an available model slot."));
1207 void MdiChild::onModelMoveToCategory()
1213 int toCatId
= sender()->property("categoryId").toInt(&ok
);
1214 if (ok
&& toCatId
>= 0) {
1215 moveSelectedModelsToCat(toCatId
);
1219 void MdiChild::modelEdit()
1221 openModelEditWindow(getCurrentModel());
1224 void MdiChild::wizardEdit()
1226 openModelWizard(getCurrentModel());
1229 void MdiChild::openModelWizard(int row
)
1231 if (row
< 0 && (row
= getCurrentModel()) < 0)
1234 WizardDialog
* wizard
= new WizardDialog(radioData
.generalSettings
, row
+1, radioData
.models
[row
], this);
1235 int res
= wizard
->exec();
1236 if (res
== QDialog::Accepted
&& wizard
->mix
.complete
/*TODO rather test the exec() result?*/) {
1237 radioData
.models
[row
] = wizard
->mix
;
1238 radioData
.fixModelFilenames();
1240 setSelectedModel(row
);
1244 void MdiChild::openModelEditWindow(int row
)
1246 if (row
< 0 && (row
= getCurrentModel()) < 0)
1249 QApplication::setOverrideCursor(Qt::WaitCursor
);
1250 checkAndInitModel(row
);
1251 ModelData
& model
= radioData
.models
[row
];
1252 gStopwatch
.restart();
1253 gStopwatch
.report("ModelEdit creation");
1254 ModelEdit
* t
= new ModelEdit(this, radioData
, (row
), firmware
);
1255 gStopwatch
.report("ModelEdit created");
1256 t
->setWindowTitle(tr("Editing model %1: ").arg(row
+1) + model
.name
);
1257 connect(t
, &ModelEdit::modified
, this, &MdiChild::setModified
);
1258 gStopwatch
.report("STARTING MODEL EDIT");
1260 QApplication::restoreOverrideCursor();
1261 gStopwatch
.report("ModelEdit shown");
1265 void MdiChild::print(int model
, const QString
& filename
)
1268 PrintDialog
* pd
= NULL
;
1270 if (model
>=0 && !filename
.isEmpty()) {
1271 pd
= new PrintDialog(this, firmware
, radioData
.generalSettings
, radioData
.models
[model
], filename
);
1274 pd
= new PrintDialog(this, firmware
, radioData
.generalSettings
, radioData
.models
[getCurrentModel()]);
1278 pd
->setAttribute(Qt::WA_DeleteOnClose
, true);
1283 void MdiChild::setDefault()
1285 int row
= getCurrentModel();
1286 if (!radioData
.models
[row
].isEmpty() && radioData
.generalSettings
.currModelIndex
!= (unsigned)row
) {
1287 radioData
.setCurrentModel(row
);
1291 void MdiChild::radioSimulate()
1293 startSimulation(this, radioData
, -1);
1296 void MdiChild::modelSimulate()
1298 startSimulation(this, radioData
, getCurrentModel());
1301 void MdiChild::newFile(bool createDefaults
)
1303 static int sequenceNumber
= 1;
1305 curFile
= QString("document%1.otx").arg(sequenceNumber
++);
1308 if (createDefaults
&& firmware
->getCapability(Capability::HasModelCategories
)) {
1313 bool MdiChild::loadFile(const QString
& filename
, bool resetCurrentFile
)
1315 Storage
storage(filename
);
1316 if (!storage
.load(radioData
)) {
1317 QMessageBox::critical(this, tr("Error"), storage
.error());
1321 QString warning
= storage
.warning();
1322 if (!warning
.isEmpty()) {
1323 // TODO ShowEepromWarnings(this, tr("Warning"), warning);
1326 if (resetCurrentFile
) {
1327 setCurrentFile(filename
);
1330 if (!storage
.isBoardCompatible(getCurrentBoard())) {
1331 convertStorage(storage
.getBoard(), getCurrentBoard());
1341 bool MdiChild::save()
1344 return saveAs(true);
1347 return saveFile(curFile
);
1351 bool MdiChild::saveAs(bool isNew
)
1354 QFileInfo
fi(curFile
);
1358 QString
filter(OTX_FILES_FILTER
);
1361 QString fileName
= QFileDialog::getSaveFileName(this, tr("Save As"), g
.eepromDir() + "/" +fi
.fileName(), filter
);
1362 if (fileName
.isEmpty())
1364 g
.eepromDir( QFileInfo(fileName
).dir().absolutePath() );
1366 return saveFile(fileName
);
1368 return saveFile(fileName
, true);
1371 bool MdiChild::saveFile(const QString
& filename
, bool setCurrent
)
1373 radioData
.fixModelFilenames();
1374 Storage
storage(filename
);
1375 bool result
= storage
.write(radioData
);
1381 setCurrentFile(filename
);
1387 bool MdiChild::maybeSave()
1389 if (isWindowModified()) {
1390 int ret
= askQuestion(tr("%1 has been modified.\nDo you want to save your changes?").arg(userFriendlyCurrentFile()),
1391 QMessageBox::Save
, QMessageBox::Discard
, QMessageBox::Cancel
| QMessageBox::Default
);
1393 if (ret
== QMessageBox::Save
)
1395 else if (ret
== QMessageBox::Cancel
)
1401 QString
MdiChild::currentFile() const
1406 QString
MdiChild::userFriendlyCurrentFile() const
1408 return QFileInfo(curFile
).fileName();
1411 void MdiChild::setCurrentFile(const QString
& fileName
)
1413 curFile
= QFileInfo(fileName
).canonicalFilePath();
1415 setWindowModified(false);
1417 int MaxRecentFiles
= g
.historySize();
1418 QStringList files
= g
.recentFiles();
1419 files
.removeAll(fileName
);
1420 files
.prepend(fileName
);
1421 while (files
.size() > MaxRecentFiles
)
1423 g
.recentFiles(files
);
1426 void MdiChild::forceNewFilename(const QString
& suffix
, const QString
& ext
)
1428 curFile
.replace(QRegExp("\\.(eepe|bin|hex|otx)$"), suffix
+ "." + ext
);
1431 void MdiChild::convertStorage(Board::Type from
, Board::Type to
)
1433 showWarning(tr("Models and settings will be automatically converted.\nIf that is not what you intended, please close the file\nand choose the correct radio type/profile before reopening it."));
1434 radioData
.convert(from
, to
);
1435 forceNewFilename("_converted");
1440 void MdiChild::showWarning(const QString
& msg
)
1443 QMessageBox::warning(this, "Companion", msg
);
1446 int MdiChild::askQuestion(const QString
& msg
, int button0
, int button1
, int button2
)
1448 return QMessageBox::question(this, tr("Companion"), msg
, button0
, button1
, button2
);
1451 void MdiChild::writeEeprom() // write to Tx
1453 Board::Type board
= getCurrentBoard();
1454 if (IS_HORUS(board
)) {
1455 QString radioPath
= findMassstoragePath("RADIO", true);
1456 qDebug() << "Searching for SD card, found" << radioPath
;
1457 if (radioPath
.isEmpty()) {
1458 qDebug() << "MdiChild::writeEeprom(): Horus radio not found";
1459 QMessageBox::critical(this, tr("Error"), tr("Unable to find Horus radio SD card!"));
1462 if (saveFile(radioPath
, false)) {
1463 emit
newStatusMessage(tr("Models and Settings written"), 2000);
1466 qDebug() << "MdiChild::writeEeprom(): saveFile error";
1470 QString tempFile
= generateProcessUniqueTempFileName("temp.bin");
1471 saveFile(tempFile
, false);
1472 if (!QFileInfo(tempFile
).exists()) {
1473 QMessageBox::critical(this, tr("Error"), tr("Cannot write temporary file!"));
1476 FlashEEpromDialog
* cd
= new FlashEEpromDialog(this, tempFile
);
1481 bool MdiChild::loadBackup()
1483 QString fileName
= QFileDialog::getOpenFileName(this, tr("Open backup Models and Settings file"), g
.eepromDir(), EEPROM_FILES_FILTER
);
1484 if (fileName
.isEmpty())
1486 QFile
file(fileName
);
1488 if (!file
.exists()) {
1489 QMessageBox::critical(this, tr("Error"), tr("Unable to find file %1!").arg(fileName
));
1493 // TODO int index = getCurrentModel();
1495 int eeprom_size
= file
.size();
1496 if (!file
.open(QFile::ReadOnly
)) { //reading binary file - TODO HEX support
1497 QMessageBox::critical(this, tr("Error"),
1498 tr("Error opening file %1:\n%2.")
1500 .arg(file
.errorString()));
1503 QByteArray
eeprom(eeprom_size
, 0);
1504 long result
= file
.read((char*)eeprom
.data(), eeprom_size
);
1507 if (result
!= eeprom_size
) {
1508 QMessageBox::critical(this, tr("Error"),
1509 tr("Error reading file %1:\n%2.")
1511 .arg(file
.errorString()));
1517 std::bitset
<NUM_ERRORS
> errorsEeprom((unsigned long long)LoadBackup(radioData
, (uint8_t *)eeprom
.data(), eeprom_size
, index
));
1518 if (!errorsEeprom
.test(ALL_OK
)) {
1519 ShowEepromErrors(this, tr("Error"), tr("Invalid binary backup File %1").arg(fileName
), (errorsEeprom
).to_ulong());
1522 if (errorsEeprom
.test(HAS_WARNINGS
)) {
1523 ShowEepromWarnings(this, tr("Warning"), errorsEeprom
.to_ulong());