Merged in f5soh/librepilot/update_credits (pull request #529)
[librepilot.git] / ground / gcs / src / plugins / uavobjectwidgetutils / configtaskwidget.cpp
blobe17e1c621252b07c38f9a6f7383ae9a9e2de35ee
1 /**
2 ******************************************************************************
4 * @file configtaskwidget.cpp
5 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015.
6 * The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
7 * @addtogroup GCSPlugins GCS Plugins
8 * @{
9 * @addtogroup UAVObjectWidgetUtils Plugin
10 * @{
11 * @brief Utility plugin for UAVObject to Widget relation management
12 *****************************************************************************/
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "configtaskwidget.h"
30 #include <coreplugin/generalsettings.h>
31 #include "uavobjectmanager.h"
32 #include "uavobject.h"
33 #include "uavobjectutilmanager.h"
34 #include "uavtalk/telemetrymanager.h"
35 #include "uavtalk/oplinkmanager.h"
36 #include "uavsettingsimportexport/uavsettingsimportexportfactory.h"
37 #include "smartsavebutton.h"
38 #include "mixercurvewidget.h"
40 #include <QCheckBox>
41 #include <QComboBox>
42 #include <QDesktopServices>
43 #include <QLabel>
44 #include <QLineEdit>
45 #include <QSpinBox>
46 #include <QProgressBar>
47 #include <QTableWidget>
48 #include <QToolButton>
49 #include <QUrl>
50 #include <QWidget>
52 ConfigTaskWidget::ConfigTaskWidget(QWidget *parent, ConfigTaskType configType) : QWidget(parent),
53 m_currentBoardId(-1), m_isConnected(false), m_isWidgetUpdatesAllowed(true), m_isDirty(false), m_refreshing(false),
54 m_wikiURL("Welcome"), m_saveButton(NULL), m_outOfLimitsStyle("background-color: rgb(255, 0, 0);"), m_realtimeUpdateTimer(NULL)
56 m_configType = configType;
58 m_pluginManager = ExtensionSystem::PluginManager::instance();
60 m_objectUtilManager = m_pluginManager->getObject<UAVObjectUtilManager>();
62 if (m_configType != Child) {
63 UAVSettingsImportExportFactory *importexportplugin = m_pluginManager->getObject<UAVSettingsImportExportFactory>();
64 connect(importexportplugin, SIGNAL(importAboutToBegin()), this, SLOT(invalidateObjects()));
66 m_saveButton = new SmartSaveButton(this);
67 connect(m_saveButton, SIGNAL(beginOp()), this, SLOT(disableObjectUpdates()));
68 connect(m_saveButton, SIGNAL(preProcessOperations()), this, SLOT(updateObjectsFromWidgets()));
69 connect(m_saveButton, SIGNAL(endOp()), this, SLOT(enableObjectUpdates()));
70 connect(m_saveButton, SIGNAL(saveSuccessful()), this, SLOT(saveSuccessful()));
73 switch (m_configType) {
74 case AutoPilot:
76 // connect to telemetry manager
77 TelemetryManager *tm = m_pluginManager->getObject<TelemetryManager>();
78 connect(tm, SIGNAL(connected()), this, SLOT(onConnect()));
79 connect(tm, SIGNAL(disconnected()), this, SLOT(onDisconnect()));
80 break;
82 case OPLink:
84 // connect to oplink manager
85 OPLinkManager *om = m_pluginManager->getObject<OPLinkManager>();
86 connect(om, SIGNAL(connected()), this, SLOT(onConnect()));
87 connect(om, SIGNAL(disconnected()), this, SLOT(onDisconnect()));
88 break;
90 case Child:
91 default:
92 // do nothing
93 break;
97 ConfigTaskWidget::~ConfigTaskWidget()
99 if (m_saveButton) {
100 delete m_saveButton;
102 QSet<WidgetBinding *> deleteSet = m_widgetBindingsPerWidget.values().toSet();
103 foreach(WidgetBinding * binding, deleteSet) {
104 if (binding) {
105 delete binding;
108 if (m_realtimeUpdateTimer) {
109 delete m_realtimeUpdateTimer;
110 m_realtimeUpdateTimer = NULL;
114 bool ConfigTaskWidget::expertMode() const
116 Core::Internal::GeneralSettings *settings = m_pluginManager->getObject<Core::Internal::GeneralSettings>();
118 return settings->useExpertMode();
122 void ConfigTaskWidget::addWidget(QWidget *widget)
124 addWidgetBinding("", "", widget);
127 void ConfigTaskWidget::addUAVObject(QString objectName, QList<int> *reloadGroups)
129 addWidgetBinding(objectName, "", NULL, 0, 1, false, reloadGroups);
132 void ConfigTaskWidget::addUAVObject(UAVObject *objectName, QList<int> *reloadGroups)
134 addUAVObject(objectName ? objectName->getName() : QString(), reloadGroups);
137 int ConfigTaskWidget::fieldIndexFromElementName(QString objectName, QString fieldName, QString elementName)
139 if (elementName.isEmpty() || objectName.isEmpty()) {
140 return 0;
143 QString singleObjectName = mapObjectName(objectName).split(",").at(0);
144 UAVObject *object = getObject(singleObjectName);
145 Q_ASSERT(object);
147 UAVObjectField *field = object->getField(fieldName);
148 Q_ASSERT(field);
150 return field->getElementNames().indexOf(elementName);
153 void ConfigTaskWidget::addWidgetBinding(QString objectName, QString fieldName, QWidget *widget, QString elementName)
155 addWidgetBinding(objectName, fieldName, widget, fieldIndexFromElementName(objectName, fieldName, elementName));
158 void ConfigTaskWidget::addWidgetBinding(UAVObject *object, UAVObjectField *field, QWidget *widget, QString elementName)
160 addWidgetBinding(object ? object->getName() : QString(), field ? field->getName() : QString(), widget, elementName);
163 void ConfigTaskWidget::addWidgetBinding(QString objectName, QString fieldName, QWidget *widget, QString elementName, double scale,
164 bool isLimited, QList<int> *reloadGroupIDs, quint32 instID)
166 addWidgetBinding(objectName, fieldName, widget, fieldIndexFromElementName(objectName, fieldName, elementName),
167 scale, isLimited, reloadGroupIDs, instID);
170 void ConfigTaskWidget::addWidgetBinding(UAVObject *object, UAVObjectField *field, QWidget *widget, QString elementName, double scale,
171 bool isLimited, QList<int> *reloadGroupIDs, quint32 instID)
173 addWidgetBinding(object ? object->getName() : QString(), field ? field->getName() : QString(), widget, elementName, scale,
174 isLimited, reloadGroupIDs, instID);
177 void ConfigTaskWidget::addWidgetBinding(UAVObject *object, UAVObjectField *field, QWidget *widget, int index, double scale,
178 bool isLimited, QList<int> *reloadGroupIDs, quint32 instID)
180 addWidgetBinding(object ? object->getName() : QString(), field ? field->getName() : QString(), widget, index, scale,
181 isLimited, reloadGroupIDs, instID);
184 void ConfigTaskWidget::addWidgetBinding(QString objectName, QString fieldName, QWidget *widget, int index, double scale,
185 bool isLimited, QList<int> *reloadGroupIDs, quint32 instID)
187 QString mappedObjectName = mapObjectName(objectName);
189 // If object name is comma separated list of objects, call one time per objectName
190 foreach(QString singleObjectName, mappedObjectName.split(",")) {
191 doAddWidgetBinding(singleObjectName, fieldName, widget, index, scale, isLimited, reloadGroupIDs, instID);
195 void ConfigTaskWidget::doAddWidgetBinding(QString objectName, QString fieldName, QWidget *widget, int index, double scale,
196 bool isLimited, QList<int> *reloadGroupIDs, quint32 instID)
198 // add a shadow binding to an existing binding (if any)
199 if (addShadowWidgetBinding(objectName, fieldName, widget, index, scale, isLimited, reloadGroupIDs, instID)) {
200 // no need to go further if successful
201 return;
204 // qDebug() << "ConfigTaskWidget::doAddWidgetBinding - add binding for " << objectName << fieldName << widget;
206 UAVObject *object = NULL;
207 UAVObjectField *field = NULL;
208 if (!objectName.isEmpty()) {
209 object = getObject(QString(objectName), instID);
210 Q_ASSERT(object);
211 m_updatedObjects.insert(object, true);
212 connect(object, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(objectUpdated(UAVObject *)));
213 connect(object, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(refreshWidgetsValues(UAVObject *)), Qt::UniqueConnection);
216 if (!fieldName.isEmpty() && object) {
217 field = object->getField(QString(fieldName));
218 Q_ASSERT(field);
221 WidgetBinding *binding = new WidgetBinding(widget, object, field, index, scale, isLimited);
222 // Only the first binding per widget can be enabled.
223 binding->setIsEnabled(m_widgetBindingsPerWidget.count(widget) == 0);
224 m_widgetBindingsPerWidget.insert(widget, binding);
226 if (object) {
227 m_widgetBindingsPerObject.insert(object, binding);
228 if (m_saveButton) {
229 m_saveButton->addObject((UAVDataObject *)object);
233 if (!widget) {
234 if (reloadGroupIDs && object) {
235 foreach(int groupId, *reloadGroupIDs) {
236 m_reloadGroups.insert(groupId, binding);
239 } else {
240 connectWidgetUpdatesToSlot(widget, SLOT(widgetsContentsChanged()));
241 if (reloadGroupIDs) {
242 addWidgetToReloadGroups(widget, reloadGroupIDs);
244 if (binding->isEnabled()) {
245 loadWidgetLimits(widget, field, index, isLimited, scale);
250 void ConfigTaskWidget::setWidgetBindingObjectEnabled(QString objectName, bool enabled)
252 UAVObject *object = getObject(objectName);
254 Q_ASSERT(object);
256 bool isRefreshing = m_refreshing;
257 m_refreshing = true;
259 foreach(WidgetBinding * binding, m_widgetBindingsPerObject.values(object)) {
260 binding->setIsEnabled(enabled);
261 if (enabled) {
262 if (binding->value().isValid() && !binding->value().isNull()) {
263 setWidgetFromVariant(binding->widget(), binding->value(), binding);
264 } else {
265 setWidgetFromField(binding->widget(), binding->field(), binding);
270 m_refreshing = isRefreshing;
273 bool ConfigTaskWidget::isComboboxOptionSelected(QComboBox *combo, int optionValue)
275 bool ok;
276 int value = combo->currentData().toInt(&ok);
278 return ok ? value == optionValue : false;
281 int ConfigTaskWidget::getComboboxSelectedOption(QComboBox *combo)
283 bool ok;
284 int index = combo->currentData().toInt(&ok);
286 return ok ? index : combo->currentIndex();
289 void ConfigTaskWidget::setComboboxSelectedOption(QComboBox *combo, int optionValue)
291 int index = combo->findData(QVariant(optionValue));
293 if (index != -1) {
294 combo->setCurrentIndex(index);
295 } else {
296 combo->setCurrentIndex(optionValue);
300 int ConfigTaskWidget::getComboboxIndexForOption(QComboBox *combo, int optionValue)
302 return combo->findData(QVariant(optionValue));
305 void ConfigTaskWidget::enableComboBoxOptionItem(QComboBox *combo, int optionValue, bool enable)
307 combo->model()->setData(combo->model()->index(getComboboxIndexForOption(combo, optionValue), 0),
308 !enable ? QVariant(0) : QVariant(1 | 32), Qt::UserRole - 1);
311 UAVObjectManager *ConfigTaskWidget::getObjectManager()
313 UAVObjectManager *objMngr = m_pluginManager->getObject<UAVObjectManager>();
315 Q_ASSERT(objMngr);
316 return objMngr;
319 bool ConfigTaskWidget::isConnected() const
321 bool connected = false;
323 switch (m_configType) {
324 case AutoPilot:
326 TelemetryManager *tm = m_pluginManager->getObject<TelemetryManager>();
327 connected = tm->isConnected();
328 break;
330 case OPLink:
332 OPLinkManager *om = m_pluginManager->getObject<OPLinkManager>();
333 connected = om->isConnected();
334 break;
336 case Child:
337 default:
338 // do nothing
339 break;
342 return connected;
345 // dynamic widgets don't receive the connected signal. This should be called instead.
346 void ConfigTaskWidget::bind()
348 if (isConnected()) {
349 onConnect();
350 } else {
351 refreshWidgetsValues();
352 updateEnableControls();
356 void ConfigTaskWidget::onConnect()
358 if (m_configType == AutoPilot) {
359 m_currentBoardId = m_objectUtilManager->getBoardModel();
362 m_isConnected = true;
364 invalidateObjects();
366 resetLimits();
367 updateEnableControls();
369 emit connected();
371 refreshWidgetsValues();
373 clearDirty();
376 void ConfigTaskWidget::onDisconnect()
378 m_isConnected = false;
380 emit disconnected();
382 updateEnableControls();
383 invalidateObjects();
385 // reset board ID
386 m_currentBoardId = -1;
389 void ConfigTaskWidget::refreshWidgetsValues(UAVObject *obj)
391 if (!m_isWidgetUpdatesAllowed) {
392 return;
395 bool isRefreshing = m_refreshing;
396 m_refreshing = true;
398 QList<WidgetBinding *> bindings = obj == NULL ? m_widgetBindingsPerObject.values() : m_widgetBindingsPerObject.values(obj);
399 foreach(WidgetBinding * binding, bindings) {
400 if (binding->field() && binding->widget()) {
401 if (binding->isEnabled()) {
402 setWidgetFromField(binding->widget(), binding->field(), binding);
403 } else {
404 binding->updateValueFromObjectField();
409 // call specific implementation
410 refreshWidgetsValuesImpl(obj);
412 m_refreshing = isRefreshing;
415 void ConfigTaskWidget::updateObjectsFromWidgets()
417 foreach(WidgetBinding * binding, m_widgetBindingsPerObject) {
418 if (binding->object() && binding->field()) {
419 binding->updateObjectFieldFromValue();
423 // call specific implementation
424 updateObjectsFromWidgetsImpl();
427 void ConfigTaskWidget::saveSuccessful()
429 // refresh values to reflect saved values
430 refreshWidgetsValues(NULL);
431 clearDirty();
432 // in case of failure to save we do nothing, config stays "dirty" (unsaved changes are kept)
435 void ConfigTaskWidget::helpButtonPressed()
437 QString url = m_helpButtons.value((QPushButton *)sender(), QString());
439 if (!url.isEmpty()) {
440 QDesktopServices::openUrl(QUrl(url, QUrl::StrictMode));
444 void ConfigTaskWidget::addApplyButton(QPushButton *button)
446 button->setVisible(expertMode());
447 m_saveButton->addApplyButton(button);
450 void ConfigTaskWidget::addSaveButton(QPushButton *button)
452 m_saveButton->addSaveButton(button);
455 void ConfigTaskWidget::enableControls(bool enable)
457 if (m_saveButton) {
458 m_saveButton->enableControls(enable);
461 foreach(QPushButton * button, m_reloadButtons) {
462 button->setEnabled(enable);
465 foreach(WidgetBinding * binding, m_widgetBindingsPerWidget) {
466 if (binding->isEnabled() && binding->widget()) {
467 binding->widget()->setEnabled(enable);
468 foreach(ShadowWidgetBinding * shadow, binding->shadows()) {
469 shadow->widget()->setEnabled(enable);
474 emit enableControlsChanged(enable);
477 bool ConfigTaskWidget::shouldObjectBeSaved(UAVObject *object)
479 Q_UNUSED(object);
480 return true;
483 void ConfigTaskWidget::widgetsContentsChanged()
485 QWidget *emitter = ((QWidget *)sender());
486 emit widgetContentsChanged(emitter);
487 QVariant value;
489 foreach(WidgetBinding * binding, m_widgetBindingsPerWidget.values(emitter)) {
490 if (binding && binding->isEnabled()) {
491 if (binding->widget() == emitter) {
492 value = getVariantFromWidget(emitter, binding);
493 checkWidgetsLimits(emitter, binding->field(), binding->index(), binding->isLimited(), value, binding->scale());
494 } else {
495 foreach(ShadowWidgetBinding * shadow, binding->shadows()) {
496 if (shadow->widget() == emitter) {
497 WidgetBinding tmpBinding(shadow->widget(), binding->object(), binding->field(),
498 binding->index(), shadow->scale(), shadow->isLimited());
499 value = getVariantFromWidget(emitter, &tmpBinding);
500 checkWidgetsLimits(emitter, binding->field(), binding->index(), shadow->isLimited(), value, shadow->scale());
504 binding->setValue(value);
506 if (binding->widget() != emitter) {
507 disconnectWidgetUpdatesToSlot(binding->widget(), SLOT(widgetsContentsChanged()));
509 checkWidgetsLimits(binding->widget(), binding->field(), binding->index(), binding->isLimited(),
510 value, binding->scale());
511 setWidgetFromVariant(binding->widget(), value, binding);
512 emit widgetContentsChanged(binding->widget());
514 connectWidgetUpdatesToSlot(binding->widget(), SLOT(widgetsContentsChanged()));
516 foreach(ShadowWidgetBinding * shadow, binding->shadows()) {
517 if (shadow->widget() != emitter) {
518 disconnectWidgetUpdatesToSlot(shadow->widget(), SLOT(widgetsContentsChanged()));
520 checkWidgetsLimits(shadow->widget(), binding->field(), binding->index(), shadow->isLimited(),
521 value, shadow->scale());
522 WidgetBinding tmp(shadow->widget(), binding->object(), binding->field(), binding->index(), shadow->scale(), shadow->isLimited());
523 setWidgetFromVariant(shadow->widget(), value, &tmp);
524 emit widgetContentsChanged(shadow->widget());
526 connectWidgetUpdatesToSlot(shadow->widget(), SLOT(widgetsContentsChanged()));
531 if (m_saveButton) {
532 m_saveButton->resetIcons();
534 setDirty(true);
537 bool ConfigTaskWidget::isDirty()
539 return m_isConnected ? m_isDirty : false;
542 void ConfigTaskWidget::setDirty(bool value)
544 if (m_refreshing) {
545 return;
547 m_isDirty = value;
550 void ConfigTaskWidget::clearDirty()
552 m_isDirty = false;
555 void ConfigTaskWidget::disableObjectUpdates()
557 m_isWidgetUpdatesAllowed = false;
558 foreach(WidgetBinding * binding, m_widgetBindingsPerWidget) {
559 if (binding->object()) {
560 disconnect(binding->object(), SIGNAL(objectUpdated(UAVObject *)), this, SLOT(refreshWidgetsValues(UAVObject *)));
565 void ConfigTaskWidget::enableObjectUpdates()
567 m_isWidgetUpdatesAllowed = true;
568 foreach(WidgetBinding * binding, m_widgetBindingsPerWidget) {
569 if (binding->object()) {
570 connect(binding->object(), SIGNAL(objectUpdated(UAVObject *)), this, SLOT(refreshWidgetsValues(UAVObject *)), Qt::UniqueConnection);
575 void ConfigTaskWidget::objectUpdated(UAVObject *object)
577 m_updatedObjects[object] = true;
580 bool ConfigTaskWidget::allObjectsUpdated()
582 bool result = true;
584 foreach(UAVObject * object, m_updatedObjects.keys()) {
585 result &= m_updatedObjects[object];
586 if (!result) {
587 break;
590 return result;
593 void ConfigTaskWidget::addHelpButton(QPushButton *button, QString url)
595 m_helpButtons.insert(button, url);
596 connect(button, SIGNAL(clicked()), this, SLOT(helpButtonPressed()));
599 void ConfigTaskWidget::setWikiURL(QString url)
601 m_wikiURL = url;
604 void ConfigTaskWidget::invalidateObjects()
606 foreach(UAVObject * obj, m_updatedObjects.keys()) {
607 m_updatedObjects[obj] = false;
611 void ConfigTaskWidget::apply()
613 if (m_saveButton) {
614 m_saveButton->apply();
618 void ConfigTaskWidget::save()
620 if (m_saveButton) {
621 m_saveButton->save();
625 bool ConfigTaskWidget::addShadowWidgetBinding(QString objectName, QString fieldName, QWidget *widget, int index, double scale, bool isLimited,
626 QList<int> *defaultReloadGroups, quint32 instID)
628 foreach(WidgetBinding * binding, m_widgetBindingsPerWidget) {
629 if (!binding->object() || !binding->widget() || !binding->field()) {
630 continue;
632 if (binding->matches(objectName, fieldName, index, instID)) {
633 // qDebug() << "ConfigTaskWidget::addShadowWidgetBinding - add shadow binding for " << objectName << fieldName << widget;
634 binding->addShadow(widget, scale, isLimited);
636 m_widgetBindingsPerWidget.insert(widget, binding);
637 connectWidgetUpdatesToSlot(widget, SLOT(widgetsContentsChanged()));
638 if (defaultReloadGroups) {
639 addWidgetToReloadGroups(widget, defaultReloadGroups);
641 if (binding->isEnabled()) {
642 loadWidgetLimits(widget, binding->field(), binding->index(), isLimited, scale);
644 return true;
647 return false;
650 void ConfigTaskWidget::addAutoBindings()
652 // qDebug() << "ConfigTaskWidget::addAutoBindings() - auto binding" << this;
654 foreach(QWidget * widget, this->findChildren<QWidget *>()) {
655 QVariant info = widget->property("objrelation");
657 if (info.isValid()) {
658 BindingStruct uiRelation;
659 uiRelation.buttonType = None;
660 uiRelation.scale = 1;
661 uiRelation.index = -1;
662 uiRelation.elementName = QString();
663 uiRelation.haslimits = false;
664 foreach(QString str, info.toStringList()) {
665 QString prop = str.split(":").at(0);
666 QString value = str.split(":").at(1);
668 if (prop == "objname") {
669 uiRelation.objectName = value;
670 } else if (prop == "fieldname") {
671 uiRelation.fieldName = value;
672 } else if (prop == "element") {
673 uiRelation.elementName = value;
674 } else if (prop == "index") {
675 uiRelation.index = value.toInt();
676 } else if (prop == "scale") {
677 if (value == "null") {
678 uiRelation.scale = 1;
679 } else {
680 uiRelation.scale = value.toDouble();
682 } else if (prop == "haslimits") {
683 if (value == "yes") {
684 uiRelation.haslimits = true;
685 } else {
686 uiRelation.haslimits = false;
688 } else if (prop == "button") {
689 if (value == "save") {
690 uiRelation.buttonType = SaveButton;
691 } else if (value == "apply") {
692 uiRelation.buttonType = ApplyButton;
693 } else if (value == "reload") {
694 uiRelation.buttonType = ReloadButton;
695 } else if (value == "default") {
696 uiRelation.buttonType = DefaultButton;
697 } else if (value == "help") {
698 uiRelation.buttonType = HelpButton;
700 } else if (prop == "buttongroup") {
701 foreach(QString s, value.split(",")) {
702 uiRelation.buttonGroup.append(s.toInt());
704 } else if (prop == "url") {
705 uiRelation.url = str.mid(str.indexOf(":") + 1);
708 if (uiRelation.buttonType != None) {
709 QPushButton *button = NULL;
710 switch (uiRelation.buttonType) {
711 case SaveButton:
712 button = qobject_cast<QPushButton *>(widget);
713 if (button) {
714 addSaveButton(button);
716 break;
717 case ApplyButton:
718 button = qobject_cast<QPushButton *>(widget);
719 if (button) {
720 addApplyButton(button);
722 break;
723 case DefaultButton:
724 button = qobject_cast<QPushButton *>(widget);
725 if (button) {
726 addDefaultButton(button, uiRelation.buttonGroup.at(0));
728 break;
729 case ReloadButton:
730 button = qobject_cast<QPushButton *>(widget);
731 if (button) {
732 addReloadButton(button, uiRelation.buttonGroup.at(0));
734 break;
735 case HelpButton:
736 button = qobject_cast<QPushButton *>(widget);
737 if (button) {
738 addHelpButton(button, WIKI_URL_ROOT + m_wikiURL);
740 break;
742 default:
743 break;
745 } else {
746 QWidget *wid = qobject_cast<QWidget *>(widget);
747 if (wid) {
748 if (uiRelation.index != -1) {
749 addWidgetBinding(uiRelation.objectName, uiRelation.fieldName, wid, uiRelation.index, uiRelation.scale, uiRelation.haslimits, &uiRelation.buttonGroup);
750 } else {
751 addWidgetBinding(uiRelation.objectName, uiRelation.fieldName, wid, uiRelation.elementName, uiRelation.scale, uiRelation.haslimits, &uiRelation.buttonGroup);
756 foreach(WidgetBinding * binding, m_widgetBindingsPerWidget) {
757 if (binding->object()) {
758 m_saveButton->addObject((UAVDataObject *)binding->object());
762 // qDebug() << "ConfigTaskWidget::addAutoBindings() - auto binding done for" << this;
765 void ConfigTaskWidget::dumpBindings()
767 foreach(WidgetBinding * binding, m_widgetBindingsPerObject) {
768 if (binding->widget()) {
769 qDebug() << "Binding :" << binding->widget()->objectName();
770 qDebug() << " Object :" << binding->object()->getName();
771 qDebug() << " Field :" << binding->field()->getName();
772 qDebug() << " Scale :" << binding->scale();
773 qDebug() << " Enabled:" << binding->isEnabled();
775 foreach(ShadowWidgetBinding * shadow, binding->shadows()) {
776 if (shadow->widget()) {
777 qDebug() << " Shadow:" << shadow->widget()->objectName();
778 qDebug() << " Scale :" << shadow->scale();
784 void ConfigTaskWidget::addWidgetToReloadGroups(QWidget *widget, QList<int> *reloadGroupIDs)
786 foreach(WidgetBinding * binding, m_widgetBindingsPerWidget) {
787 bool addBinding = false;
789 if (binding->widget() == widget) {
790 addBinding = true;
791 } else {
792 foreach(ShadowWidgetBinding * shadow, binding->shadows()) {
793 if (shadow->widget() == widget) {
794 addBinding = true;
798 if (addBinding) {
799 foreach(int groupID, *reloadGroupIDs) {
800 m_reloadGroups.insert(groupID, binding);
806 void ConfigTaskWidget::addDefaultButton(QPushButton *button, int buttonGroup)
808 button->setProperty("group", buttonGroup);
809 connect(button, SIGNAL(clicked()), this, SLOT(defaultButtonClicked()));
812 void ConfigTaskWidget::addReloadButton(QPushButton *button, int buttonGroup)
814 button->setProperty("group", buttonGroup);
815 m_reloadButtons.append(button);
816 connect(button, SIGNAL(clicked()), this, SLOT(reloadButtonClicked()));
819 void ConfigTaskWidget::defaultButtonClicked()
821 int groupID = sender()->property("group").toInt();
822 emit defaultRequested(groupID);
824 QList<WidgetBinding *> bindings = m_reloadGroups.values(groupID);
825 foreach(WidgetBinding * binding, bindings) {
826 if (!binding->isEnabled() || !binding->object() || !binding->field()) {
827 continue;
829 UAVDataObject *temp = ((UAVDataObject *)binding->object())->dirtyClone();
830 setWidgetFromField(binding->widget(), temp->getField(binding->field()->getName()), binding);
834 void ConfigTaskWidget::reloadButtonClicked()
836 if (m_realtimeUpdateTimer) {
837 return;
839 int groupID = sender()->property("group").toInt();
840 QList<WidgetBinding *> bindings = m_reloadGroups.values(groupID);
841 if (bindings.isEmpty()) {
842 return;
844 ObjectPersistence *objper = dynamic_cast<ObjectPersistence *>(getObjectManager()->getObject(ObjectPersistence::NAME));
845 m_realtimeUpdateTimer = new QTimer(this);
846 QEventLoop *eventLoop = new QEventLoop(this);
847 connect(m_realtimeUpdateTimer, SIGNAL(timeout()), eventLoop, SLOT(quit()));
848 connect(objper, SIGNAL(objectUpdated(UAVObject *)), eventLoop, SLOT(quit()));
850 QList<ObjectComparator> temp;
851 foreach(WidgetBinding * binding, bindings) {
852 if (binding->isEnabled() && binding->object()) {
853 ObjectComparator value;
854 value.objid = binding->object()->getObjID();
855 value.objinstid = binding->object()->getInstID();
856 if (temp.contains(value)) {
857 continue;
858 } else {
859 temp.append(value);
861 ObjectPersistence::DataFields data;
862 data.Operation = ObjectPersistence::OPERATION_LOAD;
863 data.Selection = ObjectPersistence::SELECTION_SINGLEOBJECT;
864 data.ObjectID = binding->object()->getObjID();
865 data.InstanceID = binding->object()->getInstID();
866 objper->setData(data);
867 objper->updated();
868 m_realtimeUpdateTimer->start(500);
869 eventLoop->exec();
870 if (m_realtimeUpdateTimer->isActive()) {
871 binding->object()->requestUpdate();
872 if (binding->widget()) {
873 setWidgetFromField(binding->widget(), binding->field(), binding);
876 m_realtimeUpdateTimer->stop();
879 if (eventLoop) {
880 delete eventLoop;
881 eventLoop = NULL;
883 if (m_realtimeUpdateTimer) {
884 delete m_realtimeUpdateTimer;
885 m_realtimeUpdateTimer = NULL;
889 void ConfigTaskWidget::connectWidgetUpdatesToSlot(QWidget *widget, const char *function)
891 if (!widget) {
892 return;
894 if (QComboBox * cb = qobject_cast<QComboBox *>(widget)) {
895 connect(cb, SIGNAL(currentIndexChanged(int)), this, function, Qt::UniqueConnection);
896 } else if (QSlider * cb = qobject_cast<QSlider *>(widget)) {
897 connect(cb, SIGNAL(valueChanged(int)), this, function, Qt::UniqueConnection);
898 } else if (MixerCurveWidget * cb = qobject_cast<MixerCurveWidget *>(widget)) {
899 connect(cb, SIGNAL(curveUpdated()), this, function, Qt::UniqueConnection);
900 } else if (QTableWidget * cb = qobject_cast<QTableWidget *>(widget)) {
901 connect(cb, SIGNAL(cellChanged(int, int)), this, function, Qt::UniqueConnection);
902 } else if (QSpinBox * cb = qobject_cast<QSpinBox *>(widget)) {
903 connect(cb, SIGNAL(valueChanged(int)), this, function, Qt::UniqueConnection);
904 } else if (QDoubleSpinBox * cb = qobject_cast<QDoubleSpinBox *>(widget)) {
905 connect(cb, SIGNAL(valueChanged(double)), this, function, Qt::UniqueConnection);
906 } else if (QLineEdit * cb = qobject_cast<QLineEdit *>(widget)) {
907 connect(cb, SIGNAL(textChanged(QString)), this, function, Qt::UniqueConnection);
908 } else if (QCheckBox * cb = qobject_cast<QCheckBox *>(widget)) {
909 connect(cb, SIGNAL(stateChanged(int)), this, function, Qt::UniqueConnection);
910 } else if (QPushButton * cb = qobject_cast<QPushButton *>(widget)) {
911 connect(cb, SIGNAL(clicked()), this, function, Qt::UniqueConnection);
912 } else if (QToolButton * cb = qobject_cast<QToolButton *>(widget)) {
913 connect(cb, SIGNAL(clicked()), this, function, Qt::UniqueConnection);
914 } else if (qobject_cast<QLabel *>(widget)) {
915 // read only
916 } else if (qobject_cast<QProgressBar *>(widget)) {
917 // read only
918 } else {
919 qDebug() << __FUNCTION__ << "widget binding not implemented for" << widget->metaObject()->className();
923 void ConfigTaskWidget::disconnectWidgetUpdatesToSlot(QWidget *widget, const char *function)
925 if (!widget) {
926 return;
928 if (QComboBox * cb = qobject_cast<QComboBox *>(widget)) {
929 disconnect(cb, SIGNAL(currentIndexChanged(int)), this, function);
930 } else if (QSlider * cb = qobject_cast<QSlider *>(widget)) {
931 disconnect(cb, SIGNAL(valueChanged(int)), this, function);
932 } else if (MixerCurveWidget * cb = qobject_cast<MixerCurveWidget *>(widget)) {
933 disconnect(cb, SIGNAL(curveUpdated()), this, function);
934 } else if (QTableWidget * cb = qobject_cast<QTableWidget *>(widget)) {
935 disconnect(cb, SIGNAL(cellChanged(int, int)), this, function);
936 } else if (QSpinBox * cb = qobject_cast<QSpinBox *>(widget)) {
937 disconnect(cb, SIGNAL(valueChanged(int)), this, function);
938 } else if (QDoubleSpinBox * cb = qobject_cast<QDoubleSpinBox *>(widget)) {
939 disconnect(cb, SIGNAL(valueChanged(double)), this, function);
940 } else if (QLineEdit * cb = qobject_cast<QLineEdit *>(widget)) {
941 disconnect(cb, SIGNAL(textChanged(double)), this, function);
942 } else if (QCheckBox * cb = qobject_cast<QCheckBox *>(widget)) {
943 disconnect(cb, SIGNAL(stateChanged(int)), this, function);
944 } else if (QPushButton * cb = qobject_cast<QPushButton *>(widget)) {
945 disconnect(cb, SIGNAL(clicked()), this, function);
946 } else if (QToolButton * cb = qobject_cast<QToolButton *>(widget)) {
947 disconnect(cb, SIGNAL(clicked()), this, function);
948 } else if (qobject_cast<QLabel *>(widget)) {
949 // read only
950 } else if (qobject_cast<QProgressBar *>(widget)) {
951 // read only
952 } else {
953 qDebug() << __FUNCTION__ << "widget binding not implemented for" << widget->metaObject()->className();
957 QVariant ConfigTaskWidget::getVariantFromWidget(QWidget *widget, WidgetBinding *binding)
959 double scale = binding->scale();
961 if (QComboBox * cb = qobject_cast<QComboBox *>(widget)) {
962 if (binding->isInteger()) {
963 return QVariant(getComboboxSelectedOption(cb));
965 return cb->currentText();
966 } else if (QDoubleSpinBox * cb = qobject_cast<QDoubleSpinBox *>(widget)) {
967 return (double)(cb->value() * scale);
968 } else if (QSpinBox * cb = qobject_cast<QSpinBox *>(widget)) {
969 return (double)(cb->value() * scale);
970 } else if (QSlider * cb = qobject_cast<QSlider *>(widget)) {
971 return (double)(cb->value() * scale);
972 } else if (QCheckBox * cb = qobject_cast<QCheckBox *>(widget)) {
973 return cb->isChecked() ? "True" : "False";
974 } else if (QLineEdit * cb = qobject_cast<QLineEdit *>(widget)) {
975 QString value = cb->displayText();
976 if (binding->units() == "hex") {
977 bool ok;
978 return value.toUInt(&ok, 16);
979 } else {
980 return value;
983 return QVariant();
986 bool ConfigTaskWidget::setWidgetFromVariant(QWidget *widget, QVariant value, WidgetBinding *binding)
988 double scale = binding->scale();
990 if (QComboBox * cb = qobject_cast<QComboBox *>(widget)) {
991 bool ok = true;
992 if (binding->isInteger()) {
993 setComboboxSelectedOption(cb, value.toInt(&ok));
994 } else {
995 cb->setCurrentIndex(cb->findText(value.toString()));
997 return ok;
998 } else if (QLabel * cb = qobject_cast<QLabel *>(widget)) {
999 if ((scale == 0) || (scale == 1)) {
1000 if (binding->units() == "hex") {
1001 if (value.toUInt()) {
1002 cb->setText(QString::number(value.toUInt(), 16).toUpper());
1003 } else {
1004 // display 0 as an empty string
1005 cb->setText("");
1007 } else {
1008 cb->setText(value.toString());
1010 } else {
1011 cb->setText(QString::number(value.toDouble() / scale));
1013 return true;
1014 } else if (QDoubleSpinBox * cb = qobject_cast<QDoubleSpinBox *>(widget)) {
1015 cb->setValue((double)(value.toDouble() / scale));
1016 return true;
1017 } else if (QSpinBox * cb = qobject_cast<QSpinBox *>(widget)) {
1018 cb->setValue((int)qRound(value.toDouble() / scale));
1019 return true;
1020 } else if (QSlider * cb = qobject_cast<QSlider *>(widget)) {
1021 cb->setValue((int)qRound(value.toDouble() / scale));
1022 return true;
1023 } else if (QCheckBox * cb = qobject_cast<QCheckBox *>(widget)) {
1024 cb->setChecked(value.toString() == "True");
1025 return true;
1026 } else if (QLineEdit * cb = qobject_cast<QLineEdit *>(widget)) {
1027 if ((scale == 0) || (scale == 1)) {
1028 if (binding->units() == "hex") {
1029 if (value.toUInt()) {
1030 cb->setText(QString::number(value.toUInt(), 16).toUpper());
1031 } else {
1032 // display 0 as an empty string
1033 cb->setText("");
1035 } else {
1036 cb->setText(value.toString());
1038 } else {
1039 cb->setText(QString::number(value.toDouble() / scale));
1041 return true;
1043 return false;
1046 bool ConfigTaskWidget::setWidgetFromField(QWidget *widget, UAVObjectField *field, WidgetBinding *binding)
1048 if (!widget || !field) {
1049 return false;
1051 if (QComboBox * cb = qobject_cast<QComboBox *>(widget)) {
1052 if (cb->count() == 0) {
1053 loadWidgetLimits(cb, field, binding->index(), binding->isLimited(), binding->scale());
1056 QVariant value = field->getValue(binding->index());
1057 checkWidgetsLimits(widget, field, binding->index(), binding->isLimited(), value, binding->scale());
1058 bool result = setWidgetFromVariant(widget, value, binding);
1059 if (result) {
1060 return true;
1061 } else {
1062 qDebug() << __FUNCTION__ << "widget to uavobject relation not implemented for" << widget->metaObject()->className();
1063 return false;
1067 void ConfigTaskWidget::resetLimits()
1069 // clear bound combo box lists to force repopulation
1070 // when we get another connected signal. This means that we get the
1071 // correct values in combo boxes bound to fields with limits applied.
1072 foreach(WidgetBinding * binding, m_widgetBindingsPerObject) {
1073 QComboBox *cb;
1075 if (binding->widget() && (cb = qobject_cast<QComboBox *>(binding->widget()))) {
1076 cb->clear();
1081 void ConfigTaskWidget::checkWidgetsLimits(QWidget *widget, UAVObjectField *field, int index, bool hasLimits, QVariant value, double scale)
1083 if (!hasLimits) {
1084 return;
1086 if (!field->isWithinLimits(value, index, m_currentBoardId)) {
1087 if (!widget->property("styleBackup").isValid()) {
1088 widget->setProperty("styleBackup", widget->styleSheet());
1090 widget->setStyleSheet(m_outOfLimitsStyle);
1091 widget->setProperty("wasOverLimits", (bool)true);
1092 if (QComboBox * cb = qobject_cast<QComboBox *>(widget)) {
1093 if (cb->findText(value.toString()) == -1) {
1094 cb->addItem(value.toString());
1096 } else if (QDoubleSpinBox * cb = qobject_cast<QDoubleSpinBox *>(widget)) {
1097 if ((double)(value.toDouble() / scale) > cb->maximum()) {
1098 cb->setMaximum((double)(value.toDouble() / scale));
1099 } else if ((double)(value.toDouble() / scale) < cb->minimum()) {
1100 cb->setMinimum((double)(value.toDouble() / scale));
1102 } else if (QSpinBox * cb = qobject_cast<QSpinBox *>(widget)) {
1103 if ((int)qRound(value.toDouble() / scale) > cb->maximum()) {
1104 cb->setMaximum((int)qRound(value.toDouble() / scale));
1105 } else if ((int)qRound(value.toDouble() / scale) < cb->minimum()) {
1106 cb->setMinimum((int)qRound(value.toDouble() / scale));
1108 } else if (QSlider * cb = qobject_cast<QSlider *>(widget)) {
1109 if ((int)qRound(value.toDouble() / scale) > cb->maximum()) {
1110 cb->setMaximum((int)qRound(value.toDouble() / scale));
1111 } else if ((int)qRound(value.toDouble() / scale) < cb->minimum()) {
1112 cb->setMinimum((int)qRound(value.toDouble() / scale));
1115 } else if (widget->property("wasOverLimits").isValid()) {
1116 if (widget->property("wasOverLimits").toBool()) {
1117 widget->setProperty("wasOverLimits", (bool)false);
1118 if (widget->property("styleBackup").isValid()) {
1119 QString style = widget->property("styleBackup").toString();
1120 widget->setStyleSheet(style);
1122 loadWidgetLimits(widget, field, index, hasLimits, scale);
1127 void ConfigTaskWidget::loadWidgetLimits(QWidget *widget, UAVObjectField *field, int index, bool applyLimits, double scale)
1129 if (!widget || !field) {
1130 return;
1132 if (QComboBox * cb = qobject_cast<QComboBox *>(widget)) {
1133 cb->clear();
1134 buildOptionComboBox(cb, field, index, applyLimits);
1136 if (!applyLimits) {
1137 return;
1138 } else if (QDoubleSpinBox * cb = qobject_cast<QDoubleSpinBox *>(widget)) {
1139 if (field->getMaxLimit(index).isValid()) {
1140 cb->setMaximum((double)(field->getMaxLimit(index, m_currentBoardId).toDouble() / scale));
1142 if (field->getMinLimit(index, m_currentBoardId).isValid()) {
1143 cb->setMinimum((double)(field->getMinLimit(index, m_currentBoardId).toDouble() / scale));
1145 } else if (QSpinBox * cb = qobject_cast<QSpinBox *>(widget)) {
1146 if (field->getMaxLimit(index, m_currentBoardId).isValid()) {
1147 cb->setMaximum((int)qRound(field->getMaxLimit(index, m_currentBoardId).toDouble() / scale));
1149 if (field->getMinLimit(index, m_currentBoardId).isValid()) {
1150 cb->setMinimum((int)qRound(field->getMinLimit(index, m_currentBoardId).toDouble() / scale));
1152 } else if (QSlider * cb = qobject_cast<QSlider *>(widget)) {
1153 if (field->getMaxLimit(index, m_currentBoardId).isValid()) {
1154 cb->setMaximum((int)qRound(field->getMaxLimit(index, m_currentBoardId).toDouble() / scale));
1156 if (field->getMinLimit(index, m_currentBoardId).isValid()) {
1157 cb->setMinimum((int)(field->getMinLimit(index, m_currentBoardId).toDouble() / scale));
1162 UAVObject *ConfigTaskWidget::getObject(const QString name, quint32 instId)
1164 return m_pluginManager->getObject<UAVObjectManager>()->getObject(name, instId);
1167 QString ConfigTaskWidget::mapObjectName(const QString objectName)
1169 return objectName;
1172 void ConfigTaskWidget::updateEnableControls()
1174 enableControls(isConnected());
1177 void ConfigTaskWidget::buildOptionComboBox(QComboBox *combo, UAVObjectField *field, int index, bool applyLimits)
1179 QStringList options = field->getOptions();
1181 // qDebug() << "buildOptionComboBox" << field << applyLimits << m_currentBoardId;
1182 for (int optionIndex = 0; optionIndex < options.count(); optionIndex++) {
1183 if (applyLimits) {
1184 // qDebug() << " " << options.at(optionIndex) << field->isWithinLimits(options.at(optionIndex), index, m_currentBoardId);
1185 if (m_currentBoardId > -1 && field->isWithinLimits(options.at(optionIndex), index, m_currentBoardId)) {
1186 combo->addItem(options.at(optionIndex), QVariant(optionIndex));
1188 } else {
1189 combo->addItem(options.at(optionIndex), QVariant(optionIndex));
1194 void ConfigTaskWidget::disableMouseWheelEvents()
1196 // Disable mouse wheel events
1197 foreach(QSpinBox * sp, findChildren<QSpinBox *>()) {
1198 sp->installEventFilter(this);
1200 foreach(QDoubleSpinBox * sp, findChildren<QDoubleSpinBox *>()) {
1201 sp->installEventFilter(this);
1203 foreach(QSlider * sp, findChildren<QSlider *>()) {
1204 sp->installEventFilter(this);
1206 foreach(QComboBox * sp, findChildren<QComboBox *>()) {
1207 sp->installEventFilter(this);
1211 bool ConfigTaskWidget::eventFilter(QObject *obj, QEvent *evt)
1213 // Filter all wheel events, and ignore them
1214 if (evt->type() == QEvent::Wheel &&
1215 (qobject_cast<QAbstractSpinBox *>(obj) ||
1216 qobject_cast<QComboBox *>(obj) ||
1217 qobject_cast<QAbstractSlider *>(obj))) {
1218 evt->ignore();
1219 return true;
1221 return QWidget::eventFilter(obj, evt);
1224 WidgetBinding::WidgetBinding(QWidget *widget, UAVObject *object, UAVObjectField *field, int index, double scale, bool isLimited) :
1225 ShadowWidgetBinding(widget, scale, isLimited), m_isEnabled(true)
1227 m_object = object;
1228 m_field = field;
1229 m_index = index;
1232 WidgetBinding::~WidgetBinding()
1235 QString WidgetBinding::units() const
1237 if (m_field) {
1238 return m_field->getUnits();
1240 return QString();
1243 QString WidgetBinding::type() const
1245 if (m_field) {
1246 return m_field->getTypeAsString();
1248 return QString();
1251 bool WidgetBinding::isInteger() const
1253 if (m_field) {
1254 return m_field->isInteger();
1256 return false;
1259 UAVObject *WidgetBinding::object() const
1261 return m_object;
1264 UAVObjectField *WidgetBinding::field() const
1266 return m_field;
1269 int WidgetBinding::index() const
1271 return m_index;
1274 QList<ShadowWidgetBinding *> WidgetBinding::shadows() const
1276 return m_shadows;
1279 void WidgetBinding::addShadow(QWidget *widget, double scale, bool isLimited)
1281 ShadowWidgetBinding *shadow = NULL;
1283 // Prefer anything else to QLabel and prefer QDoubleSpinBox to anything else
1284 if ((qobject_cast<QLabel *>(m_widget) && !qobject_cast<QLabel *>(widget)) ||
1285 (!qobject_cast<QDoubleSpinBox *>(m_widget) && qobject_cast<QDoubleSpinBox *>(widget))) {
1286 shadow = new ShadowWidgetBinding(m_widget, m_scale, m_isLimited);
1287 m_isLimited = isLimited;
1288 m_scale = scale;
1289 m_widget = widget;
1290 } else {
1291 shadow = new ShadowWidgetBinding(widget, scale, isLimited);
1293 m_shadows.append(shadow);
1296 bool WidgetBinding::matches(QString objectName, QString fieldName, int index, quint32 instanceId)
1298 if (m_object && m_field) {
1299 return m_object->getName() == objectName && m_object->getInstID() == instanceId &&
1300 m_field->getName() == fieldName && m_index == index;
1301 } else {
1302 return false;
1306 bool WidgetBinding::isEnabled() const
1308 return m_isEnabled;
1311 void WidgetBinding::setIsEnabled(bool isEnabled)
1313 m_isEnabled = isEnabled;
1316 QVariant WidgetBinding::value() const
1318 return m_value;
1321 void WidgetBinding::setValue(const QVariant &value)
1323 m_value = value;
1326 void WidgetBinding::updateObjectFieldFromValue()
1328 if (m_value.isValid()) {
1329 m_field->setValue(m_value, m_index);
1333 void WidgetBinding::updateValueFromObjectField()
1335 if (m_field->getValue(m_index).isValid()) {
1336 m_value = m_field->getValue(m_index);
1340 ShadowWidgetBinding::ShadowWidgetBinding(QWidget *widget, double scale, bool isLimited)
1342 m_widget = widget;
1343 m_scale = scale;
1344 m_isLimited = isLimited;
1347 ShadowWidgetBinding::~ShadowWidgetBinding()
1350 QWidget *ShadowWidgetBinding::widget() const
1352 return m_widget;
1355 double ShadowWidgetBinding::scale() const
1357 return m_scale;
1360 bool ShadowWidgetBinding::isLimited() const
1362 return m_isLimited;