LP-106 Setup Wizard refresh : Add dual servo setup (dual aileron or
[librepilot.git] / ground / gcs / src / plugins / setupwizard / vehicletemplateselectorwidget.cpp
blob23ffe1ad01b93b9ea463c0ee90b92269c9bdce88
1 /**
2 ******************************************************************************
4 * @file vehicletemplateselectorwidget.cpp
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
6 * @addtogroup [Group]
7 * @{
8 * @addtogroup VehicleTemplateSelectorWidget
9 * @{
10 * @brief [Brief]
11 *****************************************************************************/
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * for more details.
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "vehicletemplateselectorwidget.h"
29 #include "ui_vehicletemplateselectorwidget.h"
30 #include <QJsonDocument>
31 #include <QJsonArray>
32 #include <QDir>
33 #include <QDebug>
34 #include <QMessageBox>
35 #include <QFileDialog>
36 #include "vehicletemplateexportdialog.h"
37 #include "utils/pathutils.h"
39 VehicleTemplateSelectorWidget::VehicleTemplateSelectorWidget(QWidget *parent) :
40 QWidget(parent),
41 ui(new Ui::VehicleTemplateSelectorWidget), m_photoItem(NULL)
43 ui->setupUi(this);
44 ui->templateImage->setScene(new QGraphicsScene());
45 connect(ui->templateList, SIGNAL(itemSelectionChanged()), this, SLOT(templateSelectionChanged()));
46 connect(ui->deleteTemplateButton, SIGNAL(clicked()), this, SLOT(deleteSelectedTemplate()));
47 connect(ui->addTemplateButton, SIGNAL(clicked()), this, SLOT(addTemplate()));
50 VehicleTemplateSelectorWidget::~VehicleTemplateSelectorWidget()
52 ui->templateList->clear();
53 foreach(VehicleTemplate * templ, m_templates.values()) {
54 delete templ;
56 m_templates.clear();
58 delete ui;
61 void VehicleTemplateSelectorWidget::setTemplateInfo(int vehicleType, int vehicleSubType, bool showTemplateControls)
63 ui->buttonFrame->setVisible(showTemplateControls);
64 m_vehicleType = vehicleType;
65 m_vehicleSubType = vehicleSubType;
66 updateTemplates();
69 QJsonObject *VehicleTemplateSelectorWidget::selectedTemplate() const
71 if (ui->templateList->currentRow() >= 0) {
72 return ui->templateList->item(ui->templateList->currentRow())->data(Qt::UserRole + 1).value<QJsonObject *>();
74 return NULL;
77 bool VehicleTemplateSelectorWidget::selectedTemplateEditable() const
79 if (ui->templateList->currentRow() >= 0) {
80 return ui->templateList->item(ui->templateList->currentRow())->data(Qt::UserRole + 2).value<bool>();
82 return false;
85 QString VehicleTemplateSelectorWidget::selectedTemplatePath() const
87 if (ui->templateList->currentRow() >= 0) {
88 return ui->templateList->item(ui->templateList->currentRow())->data(Qt::UserRole + 3).value<QString>();
90 return "";
93 void VehicleTemplateSelectorWidget::updateTemplates()
95 loadValidFiles();
96 setupTemplateList();
99 void VehicleTemplateSelectorWidget::deleteSelectedTemplate()
101 if (selectedTemplateEditable()) {
102 if (QMessageBox::question(this, tr("Delete Vehicle Template"),
103 "Are you sure you want to delete the selected template?",
104 QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
105 QFile fileToDelete(selectedTemplatePath());
106 if (fileToDelete.remove()) {
107 QJsonObject *templObj = selectedTemplate();
108 if (templObj) {
109 VehicleTemplate *templ = m_templates[templObj->value("uuid").toString()];
110 m_templates.remove(templObj->value("uuid").toString());
111 delete templ;
113 delete ui->templateList->item(ui->templateList->currentRow());
119 void VehicleTemplateSelectorWidget::addTemplate()
121 QString path = QFileDialog::getOpenFileName(this, tr("Add settings"), QDir::homePath(),
122 tr("Vehicle Template Files (*.vtmpl *.optmpl)"));
124 if (path != NULL) {
125 QFile file(path);
127 if (file.open(QFile::ReadOnly)) {
128 QByteArray jsonData = file.readAll();
129 QJsonParseError error;
130 QJsonDocument templateDoc = QJsonDocument::fromJson(jsonData, &error);
131 if (error.error == QJsonParseError::NoError) {
132 QJsonObject json = templateDoc.object();
133 if (airframeIsCompatible(json["type"].toInt(), json["subtype"].toInt())) {
134 QFileInfo fInfo(file);
135 QString destinationFilePath = QString("%1/%2").arg(Utils::InsertStoragePath("%%STOREPATH%%vehicletemplates"))
136 .arg(getTemplatePath());
137 QDir dir;
138 if (dir.mkpath(destinationFilePath) && file.copy(QString("%1/%2").arg(destinationFilePath).arg(fInfo.fileName()))) {
139 updateTemplates();
140 } else {
141 QMessageBox::critical(this, tr("Error"), tr("The selected template file could not be added."));
143 } else {
144 QMessageBox::critical(this, tr("Error"), tr("The selected template file is not compatible with the current vehicle type."));
146 } else {
147 QMessageBox::critical(this, tr("Error"), tr("The selected template file is corrupt or of an unknown version."));
149 } else {
150 QMessageBox::critical(this, tr("Error"), tr("The selected template file could not be opened."));
155 void VehicleTemplateSelectorWidget::updatePhoto(QJsonObject *templ)
157 QPixmap photo;
159 if (m_photoItem != NULL) {
160 ui->templateImage->scene()->removeItem(m_photoItem);
162 if (templ != NULL && !templ->value("photo").isUndefined()) {
163 QByteArray imageData = QByteArray::fromBase64(templ->value("photo").toString().toLatin1());
164 photo.loadFromData(imageData, "PNG");
165 } else {
166 photo.load(":/core/images/librepilot_logo_500.png");
168 m_photoItem = ui->templateImage->scene()->addPixmap(photo);
169 ui->templateImage->setSceneRect(ui->templateImage->scene()->itemsBoundingRect());
170 ui->templateImage->fitInView(ui->templateImage->scene()->itemsBoundingRect(), Qt::KeepAspectRatio);
173 void VehicleTemplateSelectorWidget::updateDescription(QJsonObject *templ)
175 if (templ != NULL) {
176 QString description;
177 description.append("<b>").append(tr("Name of Vehicle: ")).append("</b>").append(templ->value("name").toString()).append("<br>");
178 description.append("<b>").append(tr("Name of Owner: ")).append("</b>").append(templ->value("owner").toString());
179 if (templ->value("nick") != QStringLiteral("")) {
180 description.append(" (").append(templ->value("nick").toString()).append(")");
182 description.append("<br>");
183 description.append("<b>").append(tr("Size: ")).append("</b>").append(templ->value("size").toString()).append("<br>");
184 description.append("<b>").append(tr("Weight: ")).append("</b>").append(templ->value("weight").toString()).append("<br>");
185 description.append("<b>").append(tr("Motor(s): ")).append("</b>").append(templ->value("motor").toString()).append("<br>");
186 description.append("<b>").append(tr("ESC(s): ")).append("</b>").append(templ->value("esc").toString()).append("<br>");
187 description.append("<b>").append(tr("Servo(s): ")).append("</b>").append(templ->value("servo").toString()).append("<br>");
188 description.append("<b>").append(tr("Battery: ")).append("</b>").append(templ->value("battery").toString()).append("<br>");
189 description.append("<b>").append(tr("Propellers(s): ")).append("</b>").append(templ->value("propeller").toString()).append("<br>");
190 description.append("<b>").append(tr("Controller: ")).append("</b>").append(templ->value("controller").toString()).append("<br>");
191 description.append("<b>").append(tr("Comments: ")).append("</b>").append(templ->value("comment").toString());
192 ui->templateDescription->setText(description);
193 } else {
194 ui->templateDescription->setText(tr("This option will use the current tuning settings saved on the controller, if your controller "
195 "is currently unconfigured, then the pre-configured firmware defaults will be used.\n\n"
196 "It is suggested that if this is a first time configuration of your controller, rather than "
197 "use this option, instead select a tuning set that matches your own airframe as close as "
198 "possible from the list above or if you are not able to fine one, then select the generic item "
199 "from the list."));
203 void VehicleTemplateSelectorWidget::templateSelectionChanged()
205 if (ui->templateList->currentRow() >= 0) {
206 QJsonObject *templ = selectedTemplate();
207 updatePhoto(templ);
208 updateDescription(templ);
209 ui->deleteTemplateButton->setEnabled(selectedTemplateEditable());
213 bool VehicleTemplateSelectorWidget::airframeIsCompatible(int vehicleType, int vehicleSubType)
215 if (vehicleType != m_vehicleType) {
216 return false;
219 return vehicleSubType == m_vehicleSubType;
222 QString VehicleTemplateSelectorWidget::getTemplatePath()
224 switch (m_vehicleType) {
225 case VehicleConfigurationSource::VEHICLE_FIXEDWING:
226 return VehicleTemplateExportDialog::EXPORT_FIXEDWING_NAME;
228 case VehicleConfigurationSource::VEHICLE_MULTI:
229 return VehicleTemplateExportDialog::EXPORT_MULTI_NAME;
231 case VehicleConfigurationSource::VEHICLE_HELI:
232 return VehicleTemplateExportDialog::EXPORT_HELI_NAME;
234 case VehicleConfigurationSource::VEHICLE_SURFACE:
235 return VehicleTemplateExportDialog::EXPORT_SURFACE_NAME;
237 default:
238 return NULL;
242 void VehicleTemplateSelectorWidget::loadFilesInDir(QString templateBasePath, bool local)
244 QDir templateDir(templateBasePath);
246 qDebug() << "Loading templates from base path:" << templateBasePath;
247 QStringList names;
248 names << "*.optmpl";
249 names << "*.vtmpl"; // Vehicle template
250 templateDir.setNameFilters(names);
251 templateDir.setSorting(QDir::Name);
252 QStringList files = templateDir.entryList();
253 foreach(QString fileName, files) {
254 QString fullPathName = QString("%1/%2").arg(templateDir.absolutePath()).arg(fileName);
255 QFile file(fullPathName);
257 if (file.open(QFile::ReadOnly)) {
258 QByteArray jsonData = file.readAll();
259 QJsonParseError error;
260 QJsonDocument templateDoc = QJsonDocument::fromJson(jsonData, &error);
261 if (error.error == QJsonParseError::NoError) {
262 QJsonObject json = templateDoc.object();
263 if (airframeIsCompatible(json["type"].toInt(), json["subtype"].toInt())) {
264 QString uuid = json["uuid"].toString();
265 if (!m_templates.contains(uuid)) {
266 m_templates[json["uuid"].toString()] = new VehicleTemplate(new QJsonObject(json), local, fullPathName);
269 } else {
270 qDebug() << "Error parsing json file: "
271 << fileName << ". Error was:" << error.errorString();
274 file.close();
278 void VehicleTemplateSelectorWidget::loadValidFiles()
280 ui->templateList->clear();
281 foreach(VehicleTemplate * templ, m_templates.values()) {
282 delete templ;
284 m_templates.clear();
285 QString path = getTemplatePath();
286 loadFilesInDir(QString("%1/%2/").arg(Utils::InsertDataPath("%%DATAPATH%%vehicletemplates")).arg(path), false);
287 loadFilesInDir(QString("%1/%2/").arg(Utils::InsertStoragePath("%%STOREPATH%%vehicletemplates")).arg(path), true);
290 void VehicleTemplateSelectorWidget::setupTemplateList()
292 QListWidgetItem *item;
294 foreach(QString templ, m_templates.keys()) {
295 VehicleTemplate *vtemplate = m_templates[templ];
297 item = new QListWidgetItem(vtemplate->templateObject()->value("name").toString(), ui->templateList);
298 item->setData(Qt::UserRole + 1, QVariant::fromValue(vtemplate->templateObject()));
299 item->setData(Qt::UserRole + 2, QVariant::fromValue(vtemplate->editable()));
300 if (vtemplate->editable()) {
301 item->setData(Qt::ForegroundRole, QVariant::fromValue(QColor(Qt::darkGreen)));
302 item->setData(Qt::ToolTipRole, QVariant::fromValue(tr("Local template.")));
303 } else {
304 item->setData(Qt::ToolTipRole, QVariant::fromValue(tr("Built-in template.")));
306 item->setData(Qt::UserRole + 3, QVariant::fromValue(vtemplate->templatePath()));
308 ui->templateList->sortItems(Qt::AscendingOrder);
310 item = new QListWidgetItem(tr("Current Tuning"));
311 item->setData(Qt::UserRole + 1, QVariant::fromValue((QJsonObject *)NULL));
312 ui->templateList->insertItem(0, item);
313 ui->templateList->setCurrentRow(0);
314 // TODO Add generics to top under item Current tuning
317 QString VehicleTemplateSelectorWidget::getTemplateKey(QJsonObject *templ)
319 return QString(templ->value("name").toString());
322 void VehicleTemplateSelectorWidget::resizeEvent(QResizeEvent *)
324 ui->templateImage->setSceneRect(ui->templateImage->scene()->itemsBoundingRect());
325 ui->templateImage->fitInView(ui->templateImage->scene()->itemsBoundingRect(), Qt::KeepAspectRatio);
328 void VehicleTemplateSelectorWidget::showEvent(QShowEvent *)
330 ui->templateImage->setSceneRect(ui->templateImage->scene()->itemsBoundingRect());
331 ui->templateImage->fitInView(ui->templateImage->scene()->itemsBoundingRect(), Qt::KeepAspectRatio);