diff --git a/ground/gcs/src/plugins/uavobjectbrowser/browseritemdelegate.cpp b/ground/gcs/src/plugins/uavobjectbrowser/browseritemdelegate.cpp index 017dd8b40..7af9b2f46 100644 --- a/ground/gcs/src/plugins/uavobjectbrowser/browseritemdelegate.cpp +++ b/ground/gcs/src/plugins/uavobjectbrowser/browseritemdelegate.cpp @@ -31,7 +31,9 @@ BrowserItemDelegate::BrowserItemDelegate(QObject *parent) : QStyledItemDelegate(parent) -{} +{ + _sizeHint = QSpinBox().sizeHint(); +} QWidget *BrowserItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & option, @@ -75,5 +77,5 @@ QSize BrowserItemDelegate::sizeHint(const QStyleOptionViewItem & option, const Q { Q_UNUSED(option); Q_UNUSED(index); - return QSpinBox().sizeHint(); + return _sizeHint; } diff --git a/ground/gcs/src/plugins/uavobjectbrowser/browseritemdelegate.h b/ground/gcs/src/plugins/uavobjectbrowser/browseritemdelegate.h index b9d12af04..3a9a1c3d7 100644 --- a/ground/gcs/src/plugins/uavobjectbrowser/browseritemdelegate.h +++ b/ground/gcs/src/plugins/uavobjectbrowser/browseritemdelegate.h @@ -47,6 +47,9 @@ public: const QStyleOptionViewItem &option, const QModelIndex &index) const; QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex &index) const; + +private: + QSize _sizeHint; }; #endif // BROWSERITEMDELEGATE_H diff --git a/ground/gcs/src/plugins/uavobjectbrowser/fieldtreeitem.cpp b/ground/gcs/src/plugins/uavobjectbrowser/fieldtreeitem.cpp deleted file mode 100644 index 2c1407d06..000000000 --- a/ground/gcs/src/plugins/uavobjectbrowser/fieldtreeitem.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/** - ****************************************************************************** - * - * @file fieldtreeitem.cpp - * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. - * @addtogroup GCSPlugins GCS Plugins - * @{ - * @addtogroup UAVObjectBrowserPlugin UAVObject Browser Plugin - * @{ - * @brief The UAVObject Browser gadget plugin - *****************************************************************************/ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "fieldtreeitem.h" diff --git a/ground/gcs/src/plugins/uavobjectbrowser/fieldtreeitem.h b/ground/gcs/src/plugins/uavobjectbrowser/fieldtreeitem.h index df1247cf7..c21b76807 100644 --- a/ground/gcs/src/plugins/uavobjectbrowser/fieldtreeitem.h +++ b/ground/gcs/src/plugins/uavobjectbrowser/fieldtreeitem.h @@ -29,12 +29,15 @@ #define FIELDTREEITEM_H #include "treeitem.h" -#include + +#include +#include #include #include #include #include #include + #include #define QINT8MIN std::numeric_limits::min() @@ -49,58 +52,72 @@ #define QUINT32MAX std::numeric_limits::max() class FieldTreeItem : public TreeItem { - Q_OBJECT public: - FieldTreeItem(int index, const QList &data, UAVObjectField *field, TreeItem *parent = 0) : - TreeItem(data, parent), m_index(index), m_field(field) + FieldTreeItem(int index, const QList &data, UAVObjectField *field) : + TreeItem(data), m_index(index), m_field(field) + {} + FieldTreeItem(int index, const QVariant &data, UAVObjectField *field) : + TreeItem(data), m_index(index), m_field(field) {} - FieldTreeItem(int index, const QVariant &data, UAVObjectField *field, TreeItem *parent = 0) : - TreeItem(data, parent), m_index(index), m_field(field) - {} - - bool isEditable() + bool isEditable() const { return true; } - virtual QWidget *createEditor(QWidget *parent) = 0; - virtual QVariant getEditorValue(QWidget *editor) = 0; - virtual void setEditorValue(QWidget *editor, QVariant value) = 0; - virtual void apply() {} - virtual bool isKnown() + virtual QWidget *createEditor(QWidget *parent) const = 0; + virtual QVariant getEditorValue(QWidget *editor) const = 0; + virtual void setEditorValue(QWidget *editor, QVariant value) const = 0; + + void setData(QVariant value, int column) { - return parent()->isKnown(); + QVariant currentValue = fieldToData(); + + setChanged(currentValue != value); + TreeItem::setData(value, column); } + void update(const QTime &ts) + { + bool updated = false; + + if (!changed()) { + QVariant currentValue = fieldToData(); + if (data() != currentValue) { + updated = true; + TreeItem::setData(currentValue); + } + } + if (changed() || updated) { + setHighlighted(true, ts); + } + } + + void apply() + { + m_field->setValue(dataToField(), m_index); + setChanged(false); + } protected: + virtual QVariant fieldToData() const = 0; + virtual QVariant dataToField() const = 0; + int m_index; UAVObjectField *m_field; }; class EnumFieldTreeItem : public FieldTreeItem { - Q_OBJECT public: - EnumFieldTreeItem(UAVObjectField *field, int index, const QList &data, TreeItem *parent = 0) : - FieldTreeItem(index, data, field, parent), m_enumOptions(field->getOptions()) + EnumFieldTreeItem(UAVObjectField *field, int index, const QList &data) : + FieldTreeItem(index, data, field), m_enumOptions(field->getOptions()) {} - EnumFieldTreeItem(UAVObjectField *field, int index, const QVariant &data, TreeItem *parent = 0) : - FieldTreeItem(index, data, field, parent), m_enumOptions(field->getOptions()) + EnumFieldTreeItem(UAVObjectField *field, int index, const QVariant &data) : + FieldTreeItem(index, data, field), m_enumOptions(field->getOptions()) {} - void setData(QVariant value, int column) - { - QStringList options = m_field->getOptions(); - QVariant tmpValue = m_field->getValue(m_index); - int tmpValIndex = options.indexOf(tmpValue.toString()); - - setChanged(tmpValIndex != value); - TreeItem::setData(value, column); - } - QString enumOptions(int index) { if ((index < 0) || (index >= m_enumOptions.length())) { @@ -109,28 +126,24 @@ public: return m_enumOptions.at(index); } - void apply() - { - int value = data().toInt(); - QStringList options = m_field->getOptions(); - - m_field->setValue(options[value], m_index); - setChanged(false); - } - - void update() + QVariant fieldToData() const { QStringList options = m_field->getOptions(); QVariant value = m_field->getValue(m_index); int valIndex = options.indexOf(value.toString()); - if (data() != valIndex || changed()) { - TreeItem::setData(valIndex); - setHighlight(true); - } + return valIndex; } - QWidget *createEditor(QWidget *parent) + QVariant dataToField() const + { + int value = data().toInt(); + QStringList options = m_field->getOptions(); + + return options[value]; + } + + QWidget *createEditor(QWidget *parent) const { QComboBox *editor = new QComboBox(parent); @@ -142,14 +155,14 @@ public: return editor; } - QVariant getEditorValue(QWidget *editor) + QVariant getEditorValue(QWidget *editor) const { QComboBox *comboBox = static_cast(editor); return comboBox->currentIndex(); } - void setEditorValue(QWidget *editor, QVariant value) + void setEditorValue(QWidget *editor, QVariant value) const { QComboBox *comboBox = static_cast(editor); @@ -161,16 +174,14 @@ private: }; class IntFieldTreeItem : public FieldTreeItem { - Q_OBJECT public: - IntFieldTreeItem(UAVObjectField *field, int index, const QList &data, TreeItem *parent = 0) : - FieldTreeItem(index, data, field, parent) + IntFieldTreeItem(UAVObjectField *field, int index, const QList &data) : + FieldTreeItem(index, data, field) { setMinMaxValues(); } - - IntFieldTreeItem(UAVObjectField *field, int index, const QVariant &data, TreeItem *parent = 0) : - FieldTreeItem(index, data, field, parent) + IntFieldTreeItem(UAVObjectField *field, int index, const QVariant &data) : + FieldTreeItem(index, data, field) { setMinMaxValues(); } @@ -208,7 +219,17 @@ public: } } - QWidget *createEditor(QWidget *parent) + QVariant fieldToData() const + { + return m_field->getValue(m_index).toInt(); + } + + QVariant dataToField() const + { + return data().toInt(); + } + + QWidget *createEditor(QWidget *parent) const { QSpinBox *editor = new QSpinBox(parent); @@ -217,7 +238,7 @@ public: return editor; } - QVariant getEditorValue(QWidget *editor) + QVariant getEditorValue(QWidget *editor) const { QSpinBox *spinBox = static_cast(editor); @@ -225,74 +246,41 @@ public: return spinBox->value(); } - void setEditorValue(QWidget *editor, QVariant value) + void setEditorValue(QWidget *editor, QVariant value) const { QSpinBox *spinBox = static_cast(editor); spinBox->setValue(value.toInt()); } - void setData(QVariant value, int column) - { - setChanged(m_field->getValue(m_index) != value); - TreeItem::setData(value, column); - } - - void apply() - { - m_field->setValue(data().toInt(), m_index); - setChanged(false); - } - - void update() - { - int value = m_field->getValue(m_index).toInt(); - - if (data() != value || changed()) { - TreeItem::setData(value); - setHighlight(true); - } - } - private: int m_minValue; int m_maxValue; }; class FloatFieldTreeItem : public FieldTreeItem { - Q_OBJECT public: - FloatFieldTreeItem(UAVObjectField *field, int index, const QList &data, bool scientific = false, TreeItem *parent = 0) : - FieldTreeItem(index, data, field, parent), m_useScientificNotation(scientific) {} + FloatFieldTreeItem(UAVObjectField *field, int index, const QList &data, const QSettings &settings) : + FieldTreeItem(index, data, field), m_settings(settings) {} - FloatFieldTreeItem(UAVObjectField *field, int index, const QVariant &data, bool scientific = false, TreeItem *parent = 0) : - FieldTreeItem(index, data, field, parent), m_useScientificNotation(scientific) {} + FloatFieldTreeItem(UAVObjectField *field, int index, const QVariant &data, const QSettings &settings) : + FieldTreeItem(index, data, field), m_settings(settings) {} - void setData(QVariant value, int column) + QVariant fieldToData() const { - setChanged(m_field->getValue(m_index) != value); - TreeItem::setData(value, column); + return m_field->getValue(m_index).toDouble(); } - void apply() + QVariant dataToField() const { - m_field->setValue(data().toDouble(), m_index); - setChanged(false); + return data().toDouble(); } - void update() + QWidget *createEditor(QWidget *parent) const { - double value = m_field->getValue(m_index).toDouble(); + bool useScientificNotation = m_settings.value("useScientificNotation", false).toBool(); - if (data() != value || changed()) { - TreeItem::setData(value); - setHighlight(true); - } - } - - QWidget *createEditor(QWidget *parent) - { - if (m_useScientificNotation) { + if (useScientificNotation) { QScienceSpinBox *editor = new QScienceSpinBox(parent); editor->setDecimals(6); editor->setMinimum(-std::numeric_limits::max()); @@ -307,9 +295,11 @@ public: } } - QVariant getEditorValue(QWidget *editor) + QVariant getEditorValue(QWidget *editor) const { - if (m_useScientificNotation) { + bool useScientificNotation = m_settings.value("useScientificNotation", false).toBool(); + + if (useScientificNotation) { QScienceSpinBox *spinBox = static_cast(editor); spinBox->interpretText(); return spinBox->value(); @@ -320,9 +310,11 @@ public: } } - void setEditorValue(QWidget *editor, QVariant value) + void setEditorValue(QWidget *editor, QVariant value) const { - if (m_useScientificNotation) { + bool useScientificNotation = m_settings.value("useScientificNotation", false).toBool(); + + if (useScientificNotation) { QScienceSpinBox *spinBox = static_cast(editor); spinBox->setValue(value.toDouble()); } else { @@ -332,21 +324,30 @@ public: } private: - bool m_useScientificNotation; + const QSettings &m_settings; }; class HexFieldTreeItem : public FieldTreeItem { - Q_OBJECT public: - HexFieldTreeItem(UAVObjectField *field, int index, const QList &data, TreeItem *parent = 0) : - FieldTreeItem(index, data, field, parent) + HexFieldTreeItem(UAVObjectField *field, int index, const QList &data) : + FieldTreeItem(index, data, field) {} - HexFieldTreeItem(UAVObjectField *field, int index, const QVariant &data, TreeItem *parent = 0) : - FieldTreeItem(index, data, field, parent) + HexFieldTreeItem(UAVObjectField *field, int index, const QVariant &data) : + FieldTreeItem(index, data, field) {} - QWidget *createEditor(QWidget *parent) + QVariant fieldToData() const + { + return toHexString(m_field->getValue(m_index)); + } + + QVariant dataToField() const + { + return toUInt(data()); + } + + QWidget *createEditor(QWidget *parent) const { QLineEdit *lineEdit = new QLineEdit(parent); @@ -355,44 +356,22 @@ public: return lineEdit; } - QVariant getEditorValue(QWidget *editor) + QVariant getEditorValue(QWidget *editor) const { QLineEdit *lineEdit = static_cast(editor); return lineEdit->text(); } - void setEditorValue(QWidget *editor, QVariant value) + void setEditorValue(QWidget *editor, QVariant value) const { QLineEdit *lineEdit = static_cast(editor); lineEdit->setText(value.toString()); } - void setData(QVariant value, int column) - { - setChanged(m_field->getValue(m_index) != toUInt(value)); - TreeItem::setData(value, column); - } - - void apply() - { - m_field->setValue(toUInt(data()), m_index); - setChanged(false); - } - - void update() - { - QVariant value = toHexString(m_field->getValue(m_index)); - - if (data() != value || changed()) { - TreeItem::setData(value); - setHighlight(true); - } - } - private: - QVariant toHexString(QVariant value) + QVariant toHexString(QVariant value) const { QString str; bool ok; @@ -400,7 +379,7 @@ private: return str.setNum(value.toUInt(&ok), 16).toUpper(); } - QVariant toUInt(QVariant str) + QVariant toUInt(QVariant str) const { bool ok; @@ -409,17 +388,26 @@ private: }; class CharFieldTreeItem : public FieldTreeItem { - Q_OBJECT public: - CharFieldTreeItem(UAVObjectField *field, int index, const QList &data, TreeItem *parent = 0) : - FieldTreeItem(index, data, field, parent) + CharFieldTreeItem(UAVObjectField *field, int index, const QList &data) : + FieldTreeItem(index, data, field) {} - CharFieldTreeItem(UAVObjectField *field, int index, const QVariant &data, TreeItem *parent = 0) : - FieldTreeItem(index, data, field, parent) + CharFieldTreeItem(UAVObjectField *field, int index, const QVariant &data) : + FieldTreeItem(index, data, field) {} - QWidget *createEditor(QWidget *parent) + QVariant fieldToData() const + { + return toChar(m_field->getValue(m_index)); + } + + QVariant dataToField() const + { + return toUInt(data()); + } + + QWidget *createEditor(QWidget *parent) const { QLineEdit *lineEdit = new QLineEdit(parent); @@ -428,49 +416,27 @@ public: return lineEdit; } - QVariant getEditorValue(QWidget *editor) + QVariant getEditorValue(QWidget *editor) const { QLineEdit *lineEdit = static_cast(editor); return lineEdit->text(); } - void setEditorValue(QWidget *editor, QVariant value) + void setEditorValue(QWidget *editor, QVariant value) const { QLineEdit *lineEdit = static_cast(editor); lineEdit->setText(value.toString()); } - void setData(QVariant value, int column) - { - setChanged(m_field->getValue(m_index) != toUInt(value)); - TreeItem::setData(value, column); - } - - void apply() - { - m_field->setValue(toUInt(data()), m_index); - setChanged(false); - } - - void update() - { - QVariant value = toChar(m_field->getValue(m_index)); - - if (data() != value || changed()) { - TreeItem::setData(value); - setHighlight(true); - } - } - private: - QVariant toChar(QVariant value) + QVariant toChar(QVariant value) const { return value.toChar(); } - QVariant toUInt(QVariant str) + QVariant toUInt(QVariant str) const { return QVariant(str.toString().at(0).toLatin1()); } diff --git a/ground/gcs/src/plugins/uavobjectbrowser/treeitem.cpp b/ground/gcs/src/plugins/uavobjectbrowser/treeitem.cpp index 71b9cf690..584c25881 100644 --- a/ground/gcs/src/plugins/uavobjectbrowser/treeitem.cpp +++ b/ground/gcs/src/plugins/uavobjectbrowser/treeitem.cpp @@ -27,26 +27,30 @@ #include "treeitem.h" +#include + /* Constructor */ -HighLightManager::HighLightManager(long checkingInterval) +HighlightManager::HighlightManager() { - // Start the timer and connect it to the callback - m_expirationTimer.start(checkingInterval); - connect(&m_expirationTimer, SIGNAL(timeout()), this, SLOT(checkItemsExpired())); + // Initialize the timer and connect it to the callback + m_expirationTimer.setTimerType(Qt::PreciseTimer); + m_expirationTimer.setSingleShot(true); + connect(&m_expirationTimer, &QTimer::timeout, this, &HighlightManager::checkItemsExpired); } /* * Called to add item to list. Item is only added if absent. * Returns true if item was added, otherwise false. */ -bool HighLightManager::add(TreeItem *itemToAdd) +bool HighlightManager::add(TreeItem *item) { // Lock to ensure thread safety QMutexLocker locker(&m_mutex); // Check so that the item isn't already in the list - if (!m_items.contains(itemToAdd)) { - m_items.insert(itemToAdd); + if (!m_items.contains(item)) { + m_items.insert(item); + emit updateHighlight(item); return true; } return false; @@ -56,13 +60,57 @@ bool HighLightManager::add(TreeItem *itemToAdd) * Called to remove item from list. * Returns true if item was removed, otherwise false. */ -bool HighLightManager::remove(TreeItem *itemToRemove) +bool HighlightManager::remove(TreeItem *item) { // Lock to ensure thread safety QMutexLocker locker(&m_mutex); // Remove item and return result - return m_items.remove(itemToRemove); + const bool removed = m_items.remove(item); + + if (removed) { + emit updateHighlight(item); + } + return removed; +} + +/* + * Called to remove item from list. + * Will not emit a signal. Called when destroying an item + * Returns true if item was removed, otherwise false. + */ +bool HighlightManager::reset(TreeItem *item) +{ + // Lock to ensure thread safety + QMutexLocker locker(&m_mutex); + + // Remove item and return result + return m_items.remove(item); +} + +bool HighlightManager::startTimer(QTime expirationTime) +{ + // Lock to ensure thread safety + QMutexLocker locker(&m_mutex); + + if (!m_expirationTimer.isActive()) { + int msec = QTime::currentTime().msecsTo(expirationTime); + // qDebug() << "start" << msec; + m_expirationTimer.start((msec < 10) ? 10 : msec); + return true; + } + return false; +} + + +void HighlightManager::reset() +{ + // Lock to ensure thread safety + QMutexLocker locker(&m_mutex); + + m_expirationTimer.stop(); + + m_items.clear(); } /* @@ -71,7 +119,7 @@ bool HighLightManager::remove(TreeItem *itemToRemove) * removes them if they are expired. * Expired highlights are restored. */ -void HighLightManager::checkItemsExpired() +void HighlightManager::checkItemsExpired() { // Lock to ensure thread safety QMutexLocker locker(&m_mutex); @@ -81,72 +129,102 @@ void HighLightManager::checkItemsExpired() // This is the timestamp to compare with QTime now = QTime::currentTime(); + QTime next; // Loop over all items, check if they expired. while (iter.hasNext()) { TreeItem *item = iter.next(); - if (item->getHiglightExpires() < now) { - // If expired, call removeHighlight - item->removeHighlight(); + if (item->getHighlightExpires() <= now) { + // expired, call removeHighlight + item->resetHighlight(); // Remove from list since it is restored. iter.remove(); + + emit updateHighlight(item); + } else { + // not expired, check if next to expire + if (!next.isValid() || (next > item->getHighlightExpires())) { + next = item->getHighlightExpires(); + } } } + if (next.isValid()) { + int msec = QTime::currentTime().msecsTo(next); + // qDebug() << "restart" << msec; + m_expirationTimer.start((msec < 10) ? 10 : msec); + } } -int TreeItem::m_highlightTimeMs = 500; +int TreeItem::m_highlightTimeMs = 300; -TreeItem::TreeItem(const QList &data, TreeItem *parent) : - QObject(0), - m_data(data), - m_parent(parent), - m_highlight(false), - m_changed(false) +TreeItem::TreeItem(const QList &data) : + m_itemData(data), + m_parentItem(0), + m_changed(false), + m_highlighted(false), + m_highlightManager(0) {} -TreeItem::TreeItem(const QVariant &data, TreeItem *parent) : - QObject(0), - m_parent(parent), - m_highlight(false), - m_changed(false) +TreeItem::TreeItem(const QVariant &data) : + m_parentItem(0), + m_changed(false), + m_highlighted(false), + m_highlightManager(0) { - m_data << data << "" << ""; + m_itemData << data << "" << ""; } TreeItem::~TreeItem() { - qDeleteAll(m_children); + if (m_highlightManager) { + m_highlightManager->reset(this); + } + qDeleteAll(m_childItems); } -void TreeItem::appendChild(TreeItem *child) +void TreeItem::setParentItem(TreeItem *parentItem) { - m_children.append(child); - child->setParentTree(this); + if (m_parentItem) { + m_parentItem->removeChild(this, false); + } + m_parentItem = parentItem; } -void TreeItem::insertChild(TreeItem *child) +void TreeItem::appendChild(TreeItem *childItem) { - int index = nameIndex(child->data(0).toString()); - - m_children.insert(index, child); - child->setParentTree(this); + m_childItems.append(childItem); + childItem->setParentItem(this); } -TreeItem *TreeItem::getChild(int index) +void TreeItem::insertChild(TreeItem *childItem, int index) { - return m_children.value(index); + m_childItems.insert(index, childItem); + childItem->setParentItem(this); +} + +void TreeItem::removeChild(TreeItem *childItem, bool reparent) +{ + m_childItems.removeOne(childItem); + if (reparent) { + childItem->setParentItem(0); + } +} + +TreeItem *TreeItem::child(int index) const +{ + return m_childItems.value(index); } int TreeItem::childCount() const { - return m_children.count(); + return m_childItems.count(); } int TreeItem::row() const { - if (m_parent) { - return m_parent->m_children.indexOf(const_cast(this)); + if (m_parentItem) { + return m_parentItem->m_childItems.indexOf(const_cast(this)); } return 0; @@ -154,79 +232,140 @@ int TreeItem::row() const int TreeItem::columnCount() const { - return m_data.count(); + return m_itemData.count(); +} + +void TreeItem::setDescription(QString desc) +{ + // Split around 40 characters + int idx = desc.indexOf(" ", 40); + + desc.insert(idx, QString("
")); + desc.remove("@Ref", Qt::CaseInsensitive); + m_description = desc; } QVariant TreeItem::data(int column) const { - return m_data.value(column); + return m_itemData.value(column); } void TreeItem::setData(QVariant value, int column) { - m_data.replace(column, value); + m_itemData.replace(column, value); } -void TreeItem::update() +void TreeItem::update(const QTime &ts) { - foreach(TreeItem * child, treeChildren()) - child->update(); + foreach(TreeItem * child, children()) { + child->update(ts); + } } void TreeItem::apply() { - foreach(TreeItem * child, treeChildren()) - child->apply(); + foreach(TreeItem * child, children()) { + child->apply(); + } } /* - * Called after a value has changed to trigger highlightning of tree item. + * Called after a value has changed to trigger highlighting of tree item. */ -void TreeItem::setHighlight(bool highlight) +void TreeItem::setHighlighted(bool highlighted, const QTime &ts) { - m_highlight = highlight; - m_changed = false; - if (highlight) { - // Update the expires timestamp - m_highlightExpires = QTime::currentTime().addMSecs(m_highlightTimeMs); - - // Add to highlightmanager - if (m_highlightManager->add(this)) { - // Only emit signal if it was added - emit updateHighlight(this); + m_changed = false; + if (m_highlighted != highlighted) { + m_highlighted = highlighted; + if (highlighted) { + // Add to highlight manager + m_highlightManager->add(this); + // Update expiration timeout + m_highlightExpires = ts.addMSecs(m_highlightTimeMs); + // start expiration timer if necessary + m_highlightManager->startTimer(m_highlightExpires); + } else { + m_highlightManager->remove(this); } - } else if (m_highlightManager->remove(this)) { - // Only emit signal if it was removed - emit updateHighlight(this); } // If we have a parent, call recursively to update highlight status of parents. - // This will ensure that the root of a leaf that is changed also is highlighted. + // This will ensure that the root of a leaf that is changed is also highlighted. // Only updates that really changes values will trigger highlight of parents. - if (m_parent) { - m_parent->setHighlight(highlight); + if (m_parentItem) { + m_parentItem->setHighlighted(highlighted, ts); } } -void TreeItem::removeHighlight() +void TreeItem::resetHighlight() { - m_highlight = false; - emit updateHighlight(this); + m_highlighted = false; } -void TreeItem::setHighlightManager(HighLightManager *mgr) +void TreeItem::setHighlightManager(HighlightManager *mgr) { m_highlightManager = mgr; } -QTime TreeItem::getHiglightExpires() +QTime TreeItem::getHighlightExpires() const { return m_highlightExpires; } -QList TopTreeItem::getMetaObjectItems() +int TreeItem::childIndex(QString name) const { - return m_metaObjectTreeItemsPerObjectIds.values(); + for (int i = 0; i < childCount(); ++i) { + if (name == child(i)->data(0).toString()) { + return i; + } + } + return -1; +} + +TreeItem *TreeItem::childByName(QString name) const +{ + int index = childIndex(name); + + return (index >= 0) ? m_childItems[index] : 0; +} + +int TreeItem::insertionIndex(TreeItem *item) const +{ + QString name = item->data(0).toString(); + + for (int i = 0; i < childCount(); ++i) { + if (name < child(i)->data(0).toString()) { + return i; + } + } + return childCount(); +} + +int TreeItem::maxHexStringLength(UAVObjectField::FieldType type) +{ + switch (type) { + case UAVObjectField::INT8: + return 2; + + case UAVObjectField::INT16: + return 4; + + case UAVObjectField::INT32: + return 8; + + case UAVObjectField::UINT8: + return 2; + + case UAVObjectField::UINT16: + return 4; + + case UAVObjectField::UINT32: + return 8; + + default: + Q_ASSERT(false); + } + return 0; } QVariant ArrayFieldTreeItem::data(int column) const @@ -234,28 +373,32 @@ QVariant ArrayFieldTreeItem::data(int column) const if (column == 1) { if (m_field->getType() == UAVObjectField::UINT8 && m_field->getUnits().toLower() == "char") { QString dataString; + dataString.reserve(2 + m_field->getNumElements()); + dataString.append("'"); for (uint i = 0; i < m_field->getNumElements(); ++i) { dataString.append(m_field->getValue(i).toChar()); } - QString data = QString("'%1'").arg(dataString); - return data; + dataString.append("'"); + return dataString; } else if (m_field->getUnits().toLower() == "hex") { QString dataString; + int len = TreeItem::maxHexStringLength(m_field->getType()); + QChar fillChar('0'); + dataString.reserve(2 + (len + 1) * m_field->getNumElements()); + dataString.append("{"); for (uint i = 0; i < m_field->getNumElements(); ++i) { if (i > 0) { dataString.append(' '); } bool ok; - dataString.append(QString("%1") - .arg(m_field->getValue(i).toUInt(&ok), TreeItem::maxHexStringLength(m_field->getType()), - 16, QChar('0')).toUpper()); + uint value = m_field->getValue(i).toUInt(&ok); + QString str = QString("%1").arg(value, len, 16, fillChar); + str = str.toUpper(); + dataString.append(str); } - QString data = QString("{%1}").arg(dataString); - return data; - } else { - return QVariant(); + dataString.append("}"); + return dataString; } - } else { - return TreeItem::data(column); } + return TreeItem::data(column); } diff --git a/ground/gcs/src/plugins/uavobjectbrowser/treeitem.h b/ground/gcs/src/plugins/uavobjectbrowser/treeitem.h index 253ec041c..a67a45aff 100644 --- a/ground/gcs/src/plugins/uavobjectbrowser/treeitem.h +++ b/ground/gcs/src/plugins/uavobjectbrowser/treeitem.h @@ -29,16 +29,16 @@ #define TREEITEM_H #include "uavobject.h" +#include "uavdataobject.h" #include "uavmetaobject.h" #include "uavobjectfield.h" -#include -#include -#include -#include -#include -#include -#include -#include + +#include +#include +#include +#include +#include +#include class TreeItem; @@ -49,7 +49,7 @@ class TreeItem; * non highlighted state in a linked list. * A timer traverses this list periodically to find out * if any of the items should be restored. All items are - * updated withan expiration timestamp when they expires. + * updated with an expiration timestamp when they expires. * An item that is beeing restored is removed from the * list and its removeHighlight() method is called. Items * that are not expired are left in the list til next time. @@ -57,18 +57,26 @@ class TreeItem; * left untouched in the list. This reduces unwanted emits * of signals to the repaint/update function. */ -class HighLightManager : public QObject { +class HighlightManager : public QObject { Q_OBJECT public: - // Constructor taking the checking interval in ms. - HighLightManager(long checkingInterval); + HighlightManager(); - // This is called when an item has been set to - // highlighted = true. - bool add(TreeItem *itemToAdd); + // This is called when an item is set to highlighted = true. + bool add(TreeItem *item); // This is called when an item is set to highlighted = false; - bool remove(TreeItem *itemToRemove); + bool remove(TreeItem *item); + + // This is called when an item is destroyed + bool reset(TreeItem *item); + + bool startTimer(QTime time); + + void reset(); + +signals: + void updateHighlight(TreeItem *item); private slots: // Timer callback method. @@ -85,252 +93,182 @@ private: QMutex m_mutex; }; -class TreeItem : public QObject { - Q_OBJECT +class TreeItem { public: static const int TITLE_COLUMN = 0; - static const int DATA_COLUMN = 1; + static const int DATA_COLUMN = 1; - TreeItem(const QList &data, TreeItem *parent = 0); - TreeItem(const QVariant &data, TreeItem *parent = 0); + TreeItem(const QList &data); + TreeItem(const QVariant &data); virtual ~TreeItem(); - void appendChild(TreeItem *child); - void insertChild(TreeItem *child); - - TreeItem *getChild(int index); - inline QList treeChildren() const + TreeItem *parentItem() const { - return m_children; + return m_parentItem; } + void setParentItem(TreeItem *parentItem); + + void appendChild(TreeItem *childItem); + void insertChild(TreeItem *childItem, int index); + void removeChild(TreeItem *childItem, bool reparent = true); + + TreeItem *child(int index) const; + + QList children() const + { + return m_childItems; + } + int childCount() const; + int columnCount() const; - virtual QVariant data(int column = 1) const; - QString description() + + QString description() const { return m_description; } - void setDescription(QString d) // Split around 40 characters - { - int idx = d.indexOf(" ", 40); - d.insert(idx, QString("
")); - d.remove("@Ref", Qt::CaseInsensitive); - m_description = d; - } - // only column 1 (TreeItem::dataColumn) is changed with setData currently - // other columns are initialized in constructor + void setDescription(QString desc); + + virtual QVariant data(int column = 1) const; + virtual void setData(QVariant value, int column = 1); + int row() const; - TreeItem *parent() - { - return m_parent; - } - void setParentTree(TreeItem *parent) - { - m_parent = parent; - } - inline virtual bool isEditable() + + virtual bool isEditable() const { return false; } - virtual void update(); + + virtual void update(const QTime &ts); virtual void apply(); - inline bool highlighted() + bool changed() const { - return m_highlight; + return m_changed; } - void setHighlight(bool highlight); + + void setChanged(bool changed) + { + m_changed = changed; + } + + bool isHighlighted() const + { + return m_highlighted; + } + + void setHighlighted(bool highlighted, const QTime &ts); + static void setHighlightTime(int time) { m_highlightTimeMs = time; } - inline bool changed() + QTime getHighlightExpires() const; + + void resetHighlight(); + + void setHighlightManager(HighlightManager *mgr); + + virtual bool isKnown() const { - return m_changed; - } - inline void setChanged(bool changed) - { - m_changed = changed; - } - - virtual void setHighlightManager(HighLightManager *mgr); - - QTime getHiglightExpires(); - - virtual void removeHighlight(); - - int nameIndex(QString name) - { - for (int i = 0; i < childCount(); ++i) { - if (name < getChild(i)->data(0).toString()) { - return i; - } + if (m_parentItem) { + return m_parentItem->isKnown(); } - return childCount(); - } - - TreeItem *findChildByName(QString name) - { - foreach(TreeItem * child, m_children) { - if (name == child->data(0).toString()) { - return child; - } - } - return 0; - } - - static int maxHexStringLength(UAVObjectField::FieldType type) - { - switch (type) { - case UAVObjectField::INT8: - return 2; - - case UAVObjectField::INT16: - return 4; - - case UAVObjectField::INT32: - return 8; - - case UAVObjectField::UINT8: - return 2; - - case UAVObjectField::UINT16: - return 4; - - case UAVObjectField::UINT32: - return 8; - - default: - Q_ASSERT(false); - } - return 0; - } - void updateIsKnown(bool isKnown) - { - if (isKnown != this->isKnown()) { - m_changed = false; - foreach(TreeItem * child, m_children) { - child->updateIsKnown(isKnown); - } - emit updateIsKnown(this); - } - } - virtual bool isKnown() - { return true; } -signals: - void updateHighlight(TreeItem *item); - void updateIsKnown(TreeItem *item); + int childIndex(QString name) const; -private slots: + TreeItem *childByName(QString name) const; + + int insertionIndex(TreeItem *item) const; + + static int maxHexStringLength(UAVObjectField::FieldType type); private: static int m_highlightTimeMs; - QList m_children; + QList m_childItems; // m_data contains: [0] property name, [1] value, [2] unit - QList m_data; + QList m_itemData; + TreeItem *m_parentItem; + QString m_description; - TreeItem *m_parent; - bool m_highlight; + bool m_changed; + + bool m_highlighted; QTime m_highlightExpires; - HighLightManager *m_highlightManager; + HighlightManager *m_highlightManager; }; -class DataObjectTreeItem; -class MetaObjectTreeItem; - class TopTreeItem : public TreeItem { - Q_OBJECT public: - TopTreeItem(const QList &data, TreeItem *parent = 0) : TreeItem(data, parent) {} - TopTreeItem(const QVariant &data, TreeItem *parent = 0) : TreeItem(data, parent) {} - - void addObjectTreeItem(quint32 objectId, DataObjectTreeItem *oti) - { - m_objectTreeItemsPerObjectIds[objectId] = oti; - } - - DataObjectTreeItem *findDataObjectTreeItemByObjectId(quint32 objectId) - { - return m_objectTreeItemsPerObjectIds.value(objectId, 0); - } - - void addMetaObjectTreeItem(quint32 objectId, MetaObjectTreeItem *oti) - { - m_metaObjectTreeItemsPerObjectIds[objectId] = oti; - } - - MetaObjectTreeItem *findMetaObjectTreeItemByObjectId(quint32 objectId) - { - return m_metaObjectTreeItemsPerObjectIds.value(objectId, 0); - } - - QList getMetaObjectItems(); - -private: - QHash m_objectTreeItemsPerObjectIds; - QHash m_metaObjectTreeItemsPerObjectIds; + TopTreeItem(const QList &data) : + TreeItem(data) + {} + TopTreeItem(const QVariant &data) : + TreeItem(data) + {} }; class ObjectTreeItem : public TreeItem { - Q_OBJECT public: - ObjectTreeItem(const QList &data, UAVObject *object, TreeItem *parent = 0) : - TreeItem(data, parent), m_obj(object) + ObjectTreeItem(UAVObject *object, const QList &data) : + TreeItem(data), m_obj(object) { setDescription(m_obj->getDescription()); } - ObjectTreeItem(const QVariant &data, UAVObject *object, TreeItem *parent = 0) : - TreeItem(data, parent), m_obj(object) + ObjectTreeItem(UAVObject *object, const QVariant &data) : + TreeItem(data), m_obj(object) { setDescription(m_obj->getDescription()); } - inline UAVObject *object() + + UAVObject *object() const { return m_obj; } - bool isKnown() - { - return !m_obj->isSettingsObject() || m_obj->isKnown(); - } private: UAVObject *m_obj; }; class MetaObjectTreeItem : public ObjectTreeItem { - Q_OBJECT public: - MetaObjectTreeItem(UAVObject *object, const QList &data, TreeItem *parent = 0) : - ObjectTreeItem(data, object, parent) + MetaObjectTreeItem(UAVMetaObject *object, const QList &data) : + ObjectTreeItem(object, data) {} - MetaObjectTreeItem(UAVObject *object, const QVariant &data, TreeItem *parent = 0) : - ObjectTreeItem(data, object, parent) + MetaObjectTreeItem(UAVMetaObject *object, const QVariant &data) : + ObjectTreeItem(object, data) {} - bool isKnown() + UAVMetaObject *metaObject() const { - return parent()->isKnown(); + return static_cast(object()); } }; class DataObjectTreeItem : public ObjectTreeItem { - Q_OBJECT public: - DataObjectTreeItem(const QList &data, UAVObject *object, TreeItem *parent = 0) : - ObjectTreeItem(data, object, parent) {} - DataObjectTreeItem(const QVariant &data, UAVObject *object, TreeItem *parent = 0) : - ObjectTreeItem(data, object, parent) {} + DataObjectTreeItem(UAVDataObject *object, const QList &data) : + ObjectTreeItem(object, data) + {} + DataObjectTreeItem(UAVDataObject *object, const QVariant &data) : + ObjectTreeItem(object, data) + {} + + UAVDataObject *dataObject() const + { + return static_cast(object()); + } + virtual void apply() { - foreach(TreeItem * child, treeChildren()) { + foreach(TreeItem * child, children()) { MetaObjectTreeItem *metaChild = dynamic_cast(child); if (!metaChild) { @@ -338,49 +276,54 @@ public: } } } - virtual void update() + + virtual void update(const QTime &ts) { - foreach(TreeItem * child, treeChildren()) { + foreach(TreeItem * child, children()) { MetaObjectTreeItem *metaChild = dynamic_cast(child); if (!metaChild) { - child->update(); + child->update(ts); } } } + + virtual bool isKnown() const + { + return !object()->isSettingsObject() || object()->isKnown(); + } }; class InstanceTreeItem : public DataObjectTreeItem { - Q_OBJECT public: - InstanceTreeItem(UAVObject *object, const QList &data, TreeItem *parent = 0) : - DataObjectTreeItem(data, object, parent) + InstanceTreeItem(UAVDataObject *object, const QList &data) : + DataObjectTreeItem(object, data) {} - InstanceTreeItem(UAVObject *object, const QVariant &data, TreeItem *parent = 0) : - DataObjectTreeItem(data, object, parent) + InstanceTreeItem(UAVDataObject *object, const QVariant &data) : + DataObjectTreeItem(object, data) {} + + virtual void update(const QTime &ts) + { + TreeItem::update(ts); + } + virtual void apply() { TreeItem::apply(); } - virtual void update() - { - TreeItem::update(); - } }; class ArrayFieldTreeItem : public TreeItem { - Q_OBJECT public: - ArrayFieldTreeItem(UAVObjectField *field, const QList &data, TreeItem *parent = 0) : TreeItem(data, parent), m_field(field) + ArrayFieldTreeItem(UAVObjectField *field, const QList &data) : + TreeItem(data), m_field(field) {} - ArrayFieldTreeItem(UAVObjectField *field, const QVariant &data, TreeItem *parent = 0) : TreeItem(data, parent), m_field(field) + ArrayFieldTreeItem(UAVObjectField *field, const QVariant &data) : + TreeItem(data), m_field(field) {} + QVariant data(int column) const; - bool isKnown() - { - return parent()->isKnown(); - } private: UAVObjectField *m_field; diff --git a/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowser.cpp b/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowser.cpp index 0eb9011fd..40f710c26 100644 --- a/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowser.cpp +++ b/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowser.cpp @@ -50,11 +50,22 @@ void UAVObjectBrowser::loadConfiguration(IUAVGadgetConfiguration *config) m_widget->setRecentlyUpdatedColor(m->recentlyUpdatedColor()); m_widget->setManuallyChangedColor(m->manuallyChangedColor()); m_widget->setRecentlyUpdatedTimeout(m->recentlyUpdatedTimeout()); - m_widget->setOnlyHilightChangedValues(m->onlyHighlightChangedValues()); - m_widget->setViewOptions(m->categorizedView(), m->scientificView(), m->showMetaData(), m->showDescription()); + m_widget->setOnlyHighlightChangedValues(m->onlyHighlightChangedValues()); + m_widget->setViewOptions(m->categorizedView(), m->showMetaData(), m->scientificView(), m->showDescription()); m_widget->setSplitterState(m->splitterState()); } +void UAVObjectBrowser::saveState(QSettings &settings) const +{ + m_widget->saveState(settings); +} + + +void UAVObjectBrowser::restoreState(QSettings &settings) +{ + m_widget->restoreState(settings); +} + void UAVObjectBrowser::viewOptionsChangedSlot(bool categorized, bool scientific, bool metadata, bool description) { if (m_config) { diff --git a/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowser.h b/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowser.h index d84891a95..d8e0c7adf 100644 --- a/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowser.h +++ b/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowser.h @@ -51,6 +51,9 @@ public: } void loadConfiguration(IUAVGadgetConfiguration *config); + void saveState(QSettings &settings) const; + void restoreState(QSettings &settings); + private slots: void viewOptionsChangedSlot(bool categorized, bool scientific, bool metadata, bool description); void splitterChanged(QByteArray state); diff --git a/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowser.pro b/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowser.pro index 03fafd067..6ce72896d 100644 --- a/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowser.pro +++ b/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowser.pro @@ -7,28 +7,27 @@ include(../../plugin.pri) include(uavobjectbrowser_dependencies.pri) HEADERS += \ - browserplugin.h \ + treeitem.h \ + fieldtreeitem.h \ + browseritemdelegate.h \ + uavobjecttreemodel.h \ uavobjectbrowserconfiguration.h \ + uavobjectbrowseroptionspage.h \ uavobjectbrowser.h \ uavobjectbrowserwidget.h \ uavobjectbrowserfactory.h \ - uavobjectbrowseroptionspage.h \ - uavobjecttreemodel.h \ - treeitem.h \ - browseritemdelegate.h \ - fieldtreeitem.h + browserplugin.h SOURCES += \ - browserplugin.cpp \ + treeitem.cpp \ + browseritemdelegate.cpp \ + uavobjecttreemodel.cpp \ uavobjectbrowserconfiguration.cpp \ + uavobjectbrowseroptionspage.cpp \ uavobjectbrowser.cpp \ uavobjectbrowserfactory.cpp \ uavobjectbrowserwidget.cpp \ - uavobjectbrowseroptionspage.cpp \ - uavobjecttreemodel.cpp \ - treeitem.cpp \ - browseritemdelegate.cpp \ - fieldtreeitem.cpp + browserplugin.cpp OTHER_FILES += UAVObjectBrowser.pluginspec diff --git a/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowserwidget.cpp b/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowserwidget.cpp index b7ab69aef..a18c2ce0d 100644 --- a/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowserwidget.cpp +++ b/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowserwidget.cpp @@ -38,6 +38,7 @@ #include "extensionsystem/pluginmanager.h" #include "utils/mustache.h" +#include #include UAVObjectBrowserWidget::UAVObjectBrowserWidget(QWidget *parent) : QWidget(parent) @@ -47,10 +48,7 @@ UAVObjectBrowserWidget::UAVObjectBrowserWidget(QWidget *parent) : QWidget(parent m_viewoptions = new Ui_viewoptions(); m_viewoptions->setupUi(m_viewoptionsDialog); - m_model = new UAVObjectTreeModel(this, - m_viewoptions->cbCategorized->isChecked(), - m_viewoptions->cbMetaData->isChecked(), - m_viewoptions->cbScientific->isChecked()); + m_model = createTreeModel(); m_modelProxy = new TreeSortFilterProxyModel(this); m_modelProxy->setSourceModel(m_model); @@ -97,12 +95,12 @@ UAVObjectBrowserWidget::~UAVObjectBrowserWidget() delete m_browser; } -void UAVObjectBrowserWidget::setViewOptions(bool categorized, bool scientific, bool metadata, bool description) +void UAVObjectBrowserWidget::setViewOptions(bool showCategories, bool showMetadata, bool useScientificNotation, bool showDescription) { - m_viewoptions->cbCategorized->setChecked(categorized); - m_viewoptions->cbMetaData->setChecked(metadata); - m_viewoptions->cbScientific->setChecked(scientific); - m_viewoptions->cbDescription->setChecked(description); + m_viewoptions->cbCategorized->setChecked(showCategories); + m_viewoptions->cbMetaData->setChecked(showMetadata); + m_viewoptions->cbScientific->setChecked(useScientificNotation); + m_viewoptions->cbDescription->setChecked(showDescription); } void UAVObjectBrowserWidget::setSplitterState(QByteArray state) @@ -156,7 +154,7 @@ ObjectTreeItem *UAVObjectBrowserWidget::findCurrentObjectTreeItem() if (objItem) { break; } - item = item->parent(); + item = item->parentItem(); } return objItem; } @@ -186,7 +184,7 @@ void UAVObjectBrowserWidget::saveObject() if (objItem != NULL) { UAVObject *obj = objItem->object(); Q_ASSERT(obj); - updateObjectPersistance(ObjectPersistence::OPERATION_SAVE, obj); + updateObjectPersistence(ObjectPersistence::OPERATION_SAVE, obj); } } @@ -198,7 +196,7 @@ void UAVObjectBrowserWidget::loadObject() if (objItem != NULL) { UAVObject *obj = objItem->object(); Q_ASSERT(obj); - updateObjectPersistance(ObjectPersistence::OPERATION_LOAD, obj); + updateObjectPersistence(ObjectPersistence::OPERATION_LOAD, obj); // Retrieve object so that latest value is displayed requestUpdate(); } @@ -211,13 +209,13 @@ void UAVObjectBrowserWidget::eraseObject() if (objItem != NULL) { UAVObject *obj = objItem->object(); Q_ASSERT(obj); - updateObjectPersistance(ObjectPersistence::OPERATION_DELETE, obj); + updateObjectPersistence(ObjectPersistence::OPERATION_DELETE, obj); // Retrieve object so that correct default value is displayed requestUpdate(); } } -void UAVObjectBrowserWidget::updateObjectPersistance(ObjectPersistence::OperationOptions op, UAVObject *obj) +void UAVObjectBrowserWidget::updateObjectPersistence(ObjectPersistence::OperationOptions op, UAVObject *obj) { ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); UAVObjectManager *objManager = pm->getObject(); @@ -238,11 +236,11 @@ void UAVObjectBrowserWidget::currentChanged(const QModelIndex ¤t, const QM { Q_UNUSED(previous); - TreeItem *item = static_cast(current.data(Qt::UserRole).value()); - bool enable = true; + bool enable = true; if (!current.isValid()) { enable = false; } + TreeItem *item = static_cast(current.data(Qt::UserRole).value()); TopTreeItem *top = dynamic_cast(item); ObjectTreeItem *data = dynamic_cast(item); if (top || (data && !data->object())) { @@ -264,25 +262,33 @@ void UAVObjectBrowserWidget::viewSlot() } } -void UAVObjectBrowserWidget::updateViewOptions() +UAVObjectTreeModel *UAVObjectBrowserWidget::createTreeModel() { - // TODO we should update the model instead of rebuilding it - // a side effect of rebuilding is that some state is lost (expand state, ...) - UAVObjectTreeModel *model = new UAVObjectTreeModel(this, - m_viewoptions->cbCategorized->isChecked(), - m_viewoptions->cbMetaData->isChecked(), - m_viewoptions->cbScientific->isChecked()); + UAVObjectTreeModel *model = new UAVObjectTreeModel(this); + + model->setShowCategories(m_viewoptions->cbCategorized->isChecked()); + model->setShowMetadata(m_viewoptions->cbMetaData->isChecked()); + model->setUseScientificNotation(m_viewoptions->cbScientific->isChecked()); - model->setUnknowObjectColor(m_unknownObjectColor); model->setRecentlyUpdatedColor(m_recentlyUpdatedColor); model->setManuallyChangedColor(m_manuallyChangedColor); model->setRecentlyUpdatedTimeout(m_recentlyUpdatedTimeout); - model->setOnlyHilightChangedValues(m_onlyHilightChangedValues); + model->setUnknownObjectColor(m_unknownObjectColor); + model->setOnlyHighlightChangedValues(m_onlyHighlightChangedValues); - UAVObjectTreeModel *tmpModel = m_model; - m_model = model; - m_modelProxy->setSourceModel(m_model); - delete tmpModel; + return model; +} + +void UAVObjectBrowserWidget::updateViewOptions() +{ + bool showCategories = m_viewoptions->cbCategorized->isChecked(); + bool useScientificNotation = m_viewoptions->cbScientific->isChecked(); + bool showMetadata = m_viewoptions->cbMetaData->isChecked(); + bool showDesc = m_viewoptions->cbDescription->isChecked(); + + m_model->setShowCategories(showCategories); + m_model->setShowMetadata(showMetadata); + m_model->setUseScientificNotation(useScientificNotation); // force an expand all if search text is not empty if (!m_browser->searchLine->text().isEmpty()) { @@ -290,8 +296,7 @@ void UAVObjectBrowserWidget::updateViewOptions() } // persist options - emit viewOptionsChanged(m_viewoptions->cbCategorized->isChecked(), m_viewoptions->cbScientific->isChecked(), - m_viewoptions->cbMetaData->isChecked(), m_viewoptions->cbDescription->isChecked()); + emit viewOptionsChanged(showCategories, useScientificNotation, showMetadata, showDesc); } void UAVObjectBrowserWidget::splitterMoved() @@ -417,6 +422,68 @@ void UAVObjectBrowserWidget::searchLineChanged(QString searchText) } } +QString UAVObjectBrowserWidget::indexToPath(const QModelIndex &index) const +{ + QString path = index.data(Qt::DisplayRole).toString(); + + QModelIndex parent = index.parent(); + + while (parent.isValid()) { + path = parent.data(Qt::DisplayRole).toString() + "/" + path; + parent = parent.parent(); + } + return path; +} + +QModelIndex UAVObjectBrowserWidget::indexFromPath(const QString &path) const +{ + QStringList list = path.split("/"); + + QModelIndex index = m_modelProxy->index(0, 0); + + foreach(QString name, list) { + QModelIndexList items = m_modelProxy->match(index, Qt::DisplayRole, name, 1, Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive)); + + if (!items.isEmpty()) { + index = items.first(); + } else { + // bail out + return QModelIndex(); + } + } + return index; +} + +void UAVObjectBrowserWidget::saveState(QSettings &settings) const +{ + QStringList list; + + // prepare list + foreach(QModelIndex index, m_modelProxy->getPersistentIndexList()) { + if (m_browser->treeView->isExpanded(index)) { + QString path = indexToPath(index); + list << path; + } + } + + // save list + settings.setValue("expandedItems", QVariant::fromValue(list)); +} + +void UAVObjectBrowserWidget::restoreState(QSettings &settings) +{ + // get list + QStringList list = settings.value("expandedItems").toStringList(); + + foreach(QString path, list) { + QModelIndex index = indexFromPath(path); + + if (index.isValid()) { + m_browser->treeView->setExpanded(index, true); + } + } +} + void UAVObjectBrowserWidget::searchTextCleared() { m_browser->searchLine->clear(); diff --git a/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowserwidget.h b/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowserwidget.h index d5d536dd7..bcf8a7ac5 100644 --- a/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowserwidget.h +++ b/ground/gcs/src/plugins/uavobjectbrowser/uavobjectbrowserwidget.h @@ -46,6 +46,12 @@ class TreeSortFilterProxyModel : public QSortFilterProxyModel { public: TreeSortFilterProxyModel(QObject *parent); +public: + QModelIndexList getPersistentIndexList() const + { + return persistentIndexList(); + } + protected: bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; bool filterAcceptsRowItself(int source_row, const QModelIndex &source_parent) const; @@ -62,7 +68,7 @@ public: void setUnknownObjectColor(QColor color) { m_unknownObjectColor = color; - m_model->setUnknowObjectColor(color); + m_model->setUnknownObjectColor(color); } void setRecentlyUpdatedColor(QColor color) { @@ -79,14 +85,20 @@ public: m_recentlyUpdatedTimeout = timeout; m_model->setRecentlyUpdatedTimeout(timeout); } - void setOnlyHilightChangedValues(bool hilight) + void setOnlyHighlightChangedValues(bool highlight) { - m_onlyHilightChangedValues = hilight; - m_model->setOnlyHilightChangedValues(hilight); + m_onlyHighlightChangedValues = highlight; + m_model->setOnlyHighlightChangedValues(highlight); } - void setViewOptions(bool categorized, bool scientific, bool metadata, bool description); + void setViewOptions(bool showCategories, bool showMetadata, bool useScientificNotation, bool showDescription); void setSplitterState(QByteArray state); + void saveState(QSettings &settings) const; + void restoreState(QSettings &settings); + + QString indexToPath(const QModelIndex &index) const; + QModelIndex indexFromPath(const QString &path) const; + public slots: void showDescription(bool show); @@ -119,10 +131,12 @@ private: QColor m_unknownObjectColor; QColor m_recentlyUpdatedColor; QColor m_manuallyChangedColor; - bool m_onlyHilightChangedValues; + bool m_onlyHighlightChangedValues; QString m_mustacheTemplate; - void updateObjectPersistance(ObjectPersistence::OperationOptions op, UAVObject *obj); + UAVObjectTreeModel *createTreeModel(); + + void updateObjectPersistence(ObjectPersistence::OperationOptions op, UAVObject *obj); void enableSendRequest(bool enable); void updateDescription(); ObjectTreeItem *findCurrentObjectTreeItem(); diff --git a/ground/gcs/src/plugins/uavobjectbrowser/uavobjecttreemodel.cpp b/ground/gcs/src/plugins/uavobjectbrowser/uavobjecttreemodel.cpp index 8f5941a48..cb7b6d335 100644 --- a/ground/gcs/src/plugins/uavobjectbrowser/uavobjecttreemodel.cpp +++ b/ground/gcs/src/plugins/uavobjectbrowser/uavobjecttreemodel.cpp @@ -26,169 +26,245 @@ */ #include "uavobjecttreemodel.h" + #include "fieldtreeitem.h" #include "uavobjectmanager.h" #include "uavdataobject.h" #include "uavmetaobject.h" #include "uavobjectfield.h" #include "extensionsystem/pluginmanager.h" + #include -#include -#include -#include -UAVObjectTreeModel::UAVObjectTreeModel(QObject *parent, bool categorize, bool showMetadata, bool useScientificNotation) : - QAbstractItemModel(parent), - m_categorize(categorize), - m_showMetadata(showMetadata), - m_useScientificFloatNotation(useScientificNotation), - m_recentlyUpdatedTimeout(500), // ms - m_recentlyUpdatedColor(QColor(255, 230, 230)), - m_manuallyChangedColor(QColor(230, 230, 255)), - m_unknownObjectColor(QColor(Qt::gray)) +UAVObjectTreeModel::UAVObjectTreeModel(QObject *parent) : QAbstractItemModel(parent) { - ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); - UAVObjectManager *objManager = pm->getObject(); + m_highlightManager = new HighlightManager(); + connect(m_highlightManager, &HighlightManager::updateHighlight, this, &UAVObjectTreeModel::refreshHighlight); - Q_ASSERT(objManager); + TreeItem::setHighlightTime(recentlyUpdatedTimeout()); - // Create highlight manager, let it run every 300 ms. - m_highlightManager = new HighLightManager(300); - connect(objManager, SIGNAL(newObject(UAVObject *)), this, SLOT(newObject(UAVObject *))); - connect(objManager, SIGNAL(newInstance(UAVObject *)), this, SLOT(newObject(UAVObject *))); - - TreeItem::setHighlightTime(m_recentlyUpdatedTimeout); - setupModelData(objManager); + setupModelData(); } UAVObjectTreeModel::~UAVObjectTreeModel() { - delete m_highlightManager; delete m_rootItem; + delete m_highlightManager; } -void UAVObjectTreeModel::setupModelData(UAVObjectManager *objManager) +bool UAVObjectTreeModel::showCategories() const { - m_settingsTree = new TopTreeItem(tr("Settings")); + return m_settings.value("showCategories", false).toBool(); +} + +void UAVObjectTreeModel::setShowCategories(bool show) +{ + if (show == showCategories()) { + return; + } + m_settings.setValue("showCategories", show); + toggleCategoryItems(); +} + +bool UAVObjectTreeModel::showMetadata() const +{ + return m_settings.value("showMetadata", false).toBool(); +} + +void UAVObjectTreeModel::setShowMetadata(bool show) +{ + if (show == showMetadata()) { + return; + } + m_settings.setValue("showMetadata", show); + toggleMetaItems(); +} + +bool UAVObjectTreeModel::useScientificNotation() +{ + return m_settings.value("useScientificNotation", false).toBool(); +} + +void UAVObjectTreeModel::setUseScientificNotation(bool useScientificNotation) +{ + m_settings.setValue("useScientificNotation", useScientificNotation); +} + +QColor UAVObjectTreeModel::unknownObjectColor() const +{ + return m_settings.value("unknownObjectColor", QColor(Qt::gray)).value(); +} + +void UAVObjectTreeModel::setUnknownObjectColor(QColor color) +{ + m_settings.setValue("unknownObjectColor", color); +} + +QColor UAVObjectTreeModel::recentlyUpdatedColor() const +{ + return m_settings.value("recentlyUpdatedColor", QColor(255, 230, 230)).value(); +} + +void UAVObjectTreeModel::setRecentlyUpdatedColor(QColor color) +{ + m_settings.setValue("recentlyUpdatedColor", color); +} + +QColor UAVObjectTreeModel::manuallyChangedColor() const +{ + return m_settings.value("manuallyChangedColor", QColor(230, 230, 255)).value(); +} + +void UAVObjectTreeModel::setManuallyChangedColor(QColor color) +{ + m_settings.setValue("manuallyChangedColor", color); +} + +int UAVObjectTreeModel::recentlyUpdatedTimeout() const +{ + return m_settings.value("recentlyUpdatedTimeout", 300).toInt(); +} + +void UAVObjectTreeModel::setRecentlyUpdatedTimeout(int timeout) +{ + m_settings.setValue("recentlyUpdatedTimeout", timeout); + TreeItem::setHighlightTime(timeout); +} + +bool UAVObjectTreeModel::onlyHighlightChangedValues() const +{ + return m_settings.value("onlyHighlightChangedValues", false).toBool(); +} + +void UAVObjectTreeModel::setOnlyHighlightChangedValues(bool highlight) +{ + m_settings.setValue("onlyHighlightChangedValues", highlight); +} + +bool UAVObjectTreeModel::highlightTopTreeItems() const +{ + return m_settings.value("highlightTopTreeItems", false).toBool(); +} + +void UAVObjectTreeModel::setHighlightTopTreeItems(bool highlight) +{ + m_settings.setValue("highlightTopTreeItems", highlight); +} + +void UAVObjectTreeModel::setupModelData() +{ + QList rootData; + rootData << tr("Property") << tr("Value") << tr("Unit"); + m_rootItem = new TreeItem(rootData); + m_rootItem->setHighlightManager(m_highlightManager); + + m_settingsTree = new TopTreeItem(tr("Settings")); m_settingsTree->setHighlightManager(m_highlightManager); - connect(m_settingsTree, SIGNAL(updateHighlight(TreeItem *)), this, SLOT(updateHighlight(TreeItem *))); m_nonSettingsTree = new TopTreeItem(tr("Data Objects")); m_nonSettingsTree->setHighlightManager(m_highlightManager); - connect(m_nonSettingsTree, SIGNAL(updateHighlight(TreeItem *)), this, SLOT(updateHighlight(TreeItem *))); - // root - QList rootData; - rootData << tr("Property") << tr("Value") << tr("Unit"); - m_rootItem = new TreeItem(rootData); - m_rootItem->setHighlightManager(m_highlightManager); + // tree item takes ownership of their children + appendItem(m_rootItem, m_settingsTree); + appendItem(m_rootItem, m_nonSettingsTree); - // tree item takes ownership of its children - m_rootItem->appendChild(m_settingsTree); - m_rootItem->appendChild(m_nonSettingsTree); + ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance(); + UAVObjectManager *objManager = pm->getObject(); + Q_ASSERT(objManager); + + connect(objManager, &UAVObjectManager::newObject, this, &UAVObjectTreeModel::newObject, Qt::UniqueConnection); + connect(objManager, &UAVObjectManager::newInstance, this, &UAVObjectTreeModel::newObject, Qt::UniqueConnection); QList< QList > objList = objManager->getDataObjects(); foreach(QList list, objList) { foreach(UAVDataObject * obj, list) { - addDataObject(obj); + addObject(obj); } } } +void UAVObjectTreeModel::resetModelData() +{ + m_highlightManager->reset(); + + emit beginResetModel(); + + delete m_rootItem; + m_rootItem = NULL; + setupModelData(); + + emit endResetModel(); +} + void UAVObjectTreeModel::newObject(UAVObject *obj) { - UAVDataObject *dobj = qobject_cast(obj); + UAVDataObject *dataObj = qobject_cast(obj); - if (dobj) { - addDataObject(dobj); + if (dataObj) { + addObject(dataObj); } } -void UAVObjectTreeModel::addDataObject(UAVDataObject *obj) +void UAVObjectTreeModel::addObject(UAVDataObject *obj) { - TopTreeItem *root = obj->isSettingsObject() ? m_settingsTree : m_nonSettingsTree; - - TreeItem *parent = root; - - if (m_categorize && obj->getCategory() != 0 && !obj->getCategory().isEmpty()) { - QStringList categoryPath = obj->getCategory().split('/'); - parent = createCategoryItems(categoryPath, root); + connect(obj, &UAVDataObject::objectUpdated, this, &UAVObjectTreeModel::updateObject, Qt::UniqueConnection); + connect(obj, &UAVDataObject::isKnownChanged, this, &UAVObjectTreeModel::updateIsKnown, Qt::UniqueConnection); + if (obj->getInstID() == 0) { + UAVMetaObject *metaObj = obj->getMetaObject(); + connect(metaObj, &UAVDataObject::objectUpdated, this, &UAVObjectTreeModel::updateObject, Qt::UniqueConnection); } - - ObjectTreeItem *existing = root->findDataObjectTreeItemByObjectId(obj->getObjID()); - if (existing) { - addInstance(obj, existing); - } else { - DataObjectTreeItem *dataTreeItem = new DataObjectTreeItem(obj->getName(), obj); - dataTreeItem->setHighlightManager(m_highlightManager); - connect(dataTreeItem, SIGNAL(updateHighlight(TreeItem *)), this, SLOT(updateHighlight(TreeItem *))); - connect(dataTreeItem, SIGNAL(updateIsKnown(TreeItem *)), this, SLOT(updateIsKnown(TreeItem *))); - parent->insertChild(dataTreeItem); - root->addObjectTreeItem(obj->getObjID(), dataTreeItem); - if (m_showMetadata) { - UAVMetaObject *meta = obj->getMetaObject(); - MetaObjectTreeItem *metaTreeItem = addMetaObject(meta, dataTreeItem); - root->addMetaObjectTreeItem(meta->getObjID(), metaTreeItem); - } - addInstance(obj, dataTreeItem); - } -} - -TreeItem *UAVObjectTreeModel::createCategoryItems(QStringList categoryPath, TreeItem *root) -{ - TreeItem *parent = root; - - foreach(QString category, categoryPath) { - TreeItem *existing = parent->findChildByName(category); - - if (!existing) { - TreeItem *categoryItem = new TopTreeItem(category); - connect(categoryItem, SIGNAL(updateHighlight(TreeItem *)), this, SLOT(updateHighlight(TreeItem *))); - categoryItem->setHighlightManager(m_highlightManager); - parent->insertChild(categoryItem); - parent = categoryItem; - } else { - parent = existing; - } - } - return parent; -} - -MetaObjectTreeItem *UAVObjectTreeModel::addMetaObject(UAVMetaObject *obj, TreeItem *parent) -{ - connect(obj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(highlightUpdatedObject(UAVObject *))); - MetaObjectTreeItem *meta = new MetaObjectTreeItem(obj, tr("Meta Data")); - - meta->setHighlightManager(m_highlightManager); - connect(meta, SIGNAL(updateHighlight(TreeItem *)), this, SLOT(updateHighlight(TreeItem *))); - foreach(UAVObjectField * field, obj->getFields()) { - if (field->getNumElements() > 1) { - addArrayField(field, meta); - } else { - addSingleField(0, field, meta); - } - } - parent->appendChild(meta); - return meta; -} - -void UAVObjectTreeModel::addInstance(UAVObject *obj, TreeItem *parent) -{ - connect(obj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(highlightUpdatedObject(UAVObject *))); - connect(obj, SIGNAL(isKnownChanged(UAVObject *, bool)), this, SLOT(isKnownChanged(UAVObject *, bool))); - TreeItem *item; if (obj->isSingleInstance()) { - item = parent; - DataObjectTreeItem *objectItem = static_cast(parent); - connect(objectItem, SIGNAL(updateIsKnown(TreeItem *)), this, SLOT(updateIsKnown(TreeItem *))); + DataObjectTreeItem *dataObjectItem = createDataObject(obj); + TreeItem *parentItem = getParentItem(obj, showCategories()); + insertItem(parentItem, dataObjectItem); } else { - QString name = tr("Instance") + " " + QString::number(obj->getInstID()); - item = new InstanceTreeItem(obj, name); - item->setHighlightManager(m_highlightManager); - connect(item, SIGNAL(updateHighlight(TreeItem *)), this, SLOT(updateHighlight(TreeItem *))); - connect(item, SIGNAL(updateIsKnown(TreeItem *)), this, SLOT(updateIsKnown(TreeItem *))); - parent->appendChild(item); + TreeItem *dataObjectItem; + if (obj->getInstID() == 0) { + dataObjectItem = createDataObject(obj); + TreeItem *parentItem = getParentItem(obj, showCategories()); + insertItem(parentItem, dataObjectItem); + } else { + dataObjectItem = findObjectTreeItem(obj->getObjID()); + } + InstanceTreeItem *instanceItem = createDataObjectInstance(obj); + appendItem(dataObjectItem, instanceItem); } +} + +DataObjectTreeItem *UAVObjectTreeModel::createDataObject(UAVDataObject *obj) +{ + DataObjectTreeItem *item = new DataObjectTreeItem(obj, obj->getName()); + + addObjectTreeItem(obj->getObjID(), item); + item->setHighlightManager(m_highlightManager); + + // metadata items are created up front and are added/removed as needed + // see toggleMetaItems() + MetaObjectTreeItem *metaItem = createMetaObject(obj->getMetaObject()); + if (showMetadata()) { + appendItem(item, metaItem); + } + + if (obj->isSingleInstance()) { + foreach(UAVObjectField * field, obj->getFields()) { + if (field->getNumElements() > 1) { + addArrayField(field, item); + } else { + addSingleField(0, field, item); + } + } + } + + return item; +} + +InstanceTreeItem *UAVObjectTreeModel::createDataObjectInstance(UAVDataObject *obj) +{ + QString name = obj->getName() + " " + QString::number((int)obj->getInstID()); + InstanceTreeItem *item = new InstanceTreeItem(obj, name); + + item->setHighlightManager(m_highlightManager); + foreach(UAVObjectField * field, obj->getFields()) { if (field->getNumElements() > 1) { addArrayField(field, item); @@ -196,6 +272,52 @@ void UAVObjectTreeModel::addInstance(UAVObject *obj, TreeItem *parent) addSingleField(0, field, item); } } + + return item; +} + +MetaObjectTreeItem *UAVObjectTreeModel::createMetaObject(UAVMetaObject *obj) +{ + MetaObjectTreeItem *item = new MetaObjectTreeItem(obj, tr("Meta Data")); + + addObjectTreeItem(obj->getObjID(), item); + item->setHighlightManager(m_highlightManager); + + foreach(UAVObjectField * field, obj->getFields()) { + if (field->getNumElements() > 1) { + addArrayField(field, item); + } else { + addSingleField(0, field, item); + } + } + return item; +} + +TreeItem *UAVObjectTreeModel::getParentItem(UAVDataObject *obj, bool categorize) +{ + TreeItem *parentItem = obj->isSettingsObject() ? m_settingsTree : m_nonSettingsTree; + + if (categorize) { + QString category = obj->getCategory(); + if (obj->getCategory().isEmpty()) { + category = tr("Uncategorized"); + } + QStringList categoryPath = category.split('/'); + + foreach(QString category, categoryPath) { + // metadata items are created and destroyed as needed + // see toggleCategoryItems() + TreeItem *categoryItem = parentItem->childByName(category); + + if (!categoryItem) { + categoryItem = new TopTreeItem(category); + categoryItem->setHighlightManager(m_highlightManager); + insertItem(parentItem, categoryItem); + } + parentItem = categoryItem; + } + } + return parentItem; } void UAVObjectTreeModel::addArrayField(UAVObjectField *field, TreeItem *parent) @@ -203,21 +325,21 @@ void UAVObjectTreeModel::addArrayField(UAVObjectField *field, TreeItem *parent) TreeItem *item = new ArrayFieldTreeItem(field, field->getName()); item->setHighlightManager(m_highlightManager); - connect(item, SIGNAL(updateHighlight(TreeItem *)), this, SLOT(updateHighlight(TreeItem *))); - connect(item, SIGNAL(updateIsKnown(TreeItem *)), this, SLOT(updateIsKnown(TreeItem *))); - for (uint i = 0; i < field->getNumElements(); ++i) { + + appendItem(parent, item); + + for (int i = 0; i < (int)field->getNumElements(); ++i) { addSingleField(i, field, item); } - parent->appendChild(item); } -void UAVObjectTreeModel::addSingleField(int index, UAVObjectField *field, TreeItem *parent) +void UAVObjectTreeModel::addSingleField(int i, UAVObjectField *field, TreeItem *parent) { QList data; if (field->getNumElements() == 1) { data.append(field->getName()); } else { - data.append(QString("[%1]").arg((field->getElementNames())[index])); + data.append(QString("[%1]").arg((field->getElementNames())[i])); } FieldTreeItem *item = NULL; @@ -227,10 +349,10 @@ void UAVObjectTreeModel::addSingleField(int index, UAVObjectField *field, TreeIt case UAVObjectField::ENUM: { QStringList options = field->getOptions(); - QVariant value = field->getValue(index); + QVariant value = field->getValue(i); data.append(options.indexOf(value.toString())); data.append(field->getUnits()); - item = new EnumFieldTreeItem(field, index, data); + item = new EnumFieldTreeItem(field, i, data); break; } case UAVObjectField::INT8: @@ -239,29 +361,113 @@ void UAVObjectTreeModel::addSingleField(int index, UAVObjectField *field, TreeIt case UAVObjectField::UINT8: case UAVObjectField::UINT16: case UAVObjectField::UINT32: - data.append(field->getValue(index)); + data.append(field->getValue(i)); data.append(field->getUnits()); if (field->getUnits().toLower() == "hex") { - item = new HexFieldTreeItem(field, index, data); + item = new HexFieldTreeItem(field, i, data); } else if (field->getUnits().toLower() == "char") { - item = new CharFieldTreeItem(field, index, data); + item = new CharFieldTreeItem(field, i, data); } else { - item = new IntFieldTreeItem(field, index, data); + item = new IntFieldTreeItem(field, i, data); } break; case UAVObjectField::FLOAT32: - data.append(field->getValue(index)); + data.append(field->getValue(i)); data.append(field->getUnits()); - item = new FloatFieldTreeItem(field, index, data, m_useScientificFloatNotation); + item = new FloatFieldTreeItem(field, i, data, m_settings); break; default: Q_ASSERT(false); } + item->setHighlightManager(m_highlightManager); item->setDescription(field->getDescription()); - connect(item, SIGNAL(updateHighlight(TreeItem *)), this, SLOT(updateHighlight(TreeItem *))); - connect(item, SIGNAL(updateIsKnown(TreeItem *)), this, SLOT(updateIsKnown(TreeItem *))); - parent->appendChild(item); + + appendItem(parent, item); +} + +void UAVObjectTreeModel::appendItem(TreeItem *parentItem, TreeItem *childItem) +{ + int row = parentItem->childCount(); + + beginInsertRows(index(parentItem), row, row); + parentItem->appendChild(childItem); + endInsertRows(); +} + +void UAVObjectTreeModel::insertItem(TreeItem *parentItem, TreeItem *childItem, int row) +{ + if (row < 0) { + row = parentItem->insertionIndex(childItem); + } + beginInsertRows(index(parentItem), row, row); + parentItem->insertChild(childItem, row); + endInsertRows(); +} + +void UAVObjectTreeModel::removeItem(TreeItem *parentItem, TreeItem *childItem) +{ + int row = childItem->row(); + + beginRemoveRows(index(parentItem), row, row); + parentItem->removeChild(childItem); + endRemoveRows(); +} + +void UAVObjectTreeModel::moveItem(TreeItem *newParentItem, TreeItem *oldParentItem, TreeItem *childItem) +{ + int destinationRow = newParentItem->insertionIndex(childItem); + int sourceRow = childItem->row(); + + beginMoveRows(index(oldParentItem), sourceRow, sourceRow, index(newParentItem), destinationRow); + oldParentItem->removeChild(childItem); + newParentItem->insertChild(childItem, destinationRow); + endMoveRows(); +} + +void UAVObjectTreeModel::toggleCategoryItems() +{ + foreach(ObjectTreeItem * item, m_objectTreeItems.values()) { + DataObjectTreeItem *dataItem = dynamic_cast(item); + + if (dataItem) { + TreeItem *oldParentItem = dataItem->parentItem(); + TreeItem *newParentItem = getParentItem(dataItem->dataObject(), showCategories()); + if (oldParentItem == newParentItem) { + // should not happen + continue; + } + + moveItem(newParentItem, oldParentItem, dataItem); + + if (!showCategories()) { + // remove empty category items + TreeItem *item = oldParentItem; + while (item->childCount() == 0 && item != newParentItem) { + TreeItem *tmp = item; + item = item->parentItem(); + removeItem(tmp->parentItem(), tmp); + delete tmp; + } + } + } + } +} + +void UAVObjectTreeModel::toggleMetaItems() +{ + foreach(ObjectTreeItem * item, m_objectTreeItems.values()) { + MetaObjectTreeItem *metaItem = dynamic_cast(item); + + if (metaItem) { + DataObjectTreeItem *dataItem = findDataObjectTreeItem(metaItem->object()); + if (showMetadata()) { + insertItem(dataItem, metaItem, 0); + } else { + removeItem(dataItem, metaItem); + } + } + } } QModelIndex UAVObjectTreeModel::index(int row, int column, const QModelIndex &parent) const @@ -271,38 +477,26 @@ QModelIndex UAVObjectTreeModel::index(int row, int column, const QModelIndex &pa } TreeItem *parentItem; - if (!parent.isValid()) { parentItem = m_rootItem; } else { parentItem = static_cast(parent.internalPointer()); } - TreeItem *childItem = parentItem->getChild(row); + TreeItem *childItem = parentItem->child(row); if (childItem) { return createIndex(row, column, childItem); - } else { - return QModelIndex(); } + return QModelIndex(); } -QModelIndex UAVObjectTreeModel::index(TreeItem *item) +QModelIndex UAVObjectTreeModel::index(TreeItem *item, int column) const { - if (item->parent() == 0) { + if (item == m_rootItem) { return QModelIndex(); } - QModelIndex root = index(item->parent()); - - for (int i = 0; i < rowCount(root); ++i) { - QModelIndex childIndex = index(i, 0, root); - TreeItem *child = static_cast(childIndex.internalPointer()); - if (child == item) { - return childIndex; - } - } - Q_ASSERT(false); - return QModelIndex(); + return createIndex(item->row(), column, item); } QModelIndex UAVObjectTreeModel::parent(const QModelIndex &index) const @@ -311,11 +505,14 @@ QModelIndex UAVObjectTreeModel::parent(const QModelIndex &index) const return QModelIndex(); } - TreeItem *item = static_cast(index.internalPointer()); + TreeItem *childItem = static_cast(index.internalPointer()); + TreeItem *parentItem = childItem->parentItem(); - TreeItem *parentItem = item->parent(); if (!parentItem) { - // item is root has no parent... + return QModelIndex(); + } + + if (parentItem == m_rootItem) { return QModelIndex(); } @@ -341,24 +538,15 @@ int UAVObjectTreeModel::rowCount(const QModelIndex &parent) const int UAVObjectTreeModel::columnCount(const QModelIndex &parent) const { + TreeItem *parentItem; + if (parent.isValid()) { - return static_cast(parent.internalPointer())->columnCount(); + parentItem = static_cast(parent.internalPointer()); } else { - return m_rootItem->columnCount(); - } -} - -QList UAVObjectTreeModel::getMetaDataIndexes() -{ - QList metaIndexes; - foreach(MetaObjectTreeItem * metaItem, m_settingsTree->getMetaObjectItems()) { - metaIndexes.append(index(metaItem)); + parentItem = m_rootItem; } - foreach(MetaObjectTreeItem * metaItem, m_nonSettingsTree->getMetaObjectItems()) { - metaIndexes.append(index(metaItem)); - } - return metaIndexes; + return parentItem->columnCount(); } QVariant UAVObjectTreeModel::data(const QModelIndex &index, int role) const @@ -391,22 +579,26 @@ QVariant UAVObjectTreeModel::data(const QModelIndex &index, int role) const case Qt::ForegroundRole: if (!dynamic_cast(item) && !item->isKnown()) { - return m_unknownObjectColor; + return unknownObjectColor(); } return QVariant(); case Qt::BackgroundRole: if (index.column() == TreeItem::TITLE_COLUMN) { - if (!dynamic_cast(item) && item->highlighted()) { - return m_recentlyUpdatedColor; + // TODO filtering here on highlightTopTreeItems() should not be necessary + // top tree items should not be highlighted at all in the first place + // when highlightTopTreeItems() is false + bool highlight = (highlightTopTreeItems() || !dynamic_cast(item)); + if (highlight && item->isHighlighted()) { + return recentlyUpdatedColor(); } } else if (index.column() == TreeItem::DATA_COLUMN) { FieldTreeItem *fieldItem = dynamic_cast(item); - if (fieldItem && fieldItem->highlighted()) { - return m_recentlyUpdatedColor; + if (fieldItem && fieldItem->isHighlighted()) { + return recentlyUpdatedColor(); } if (fieldItem && fieldItem->changed()) { - return m_manuallyChangedColor; + return manuallyChangedColor(); } } return QVariant(); @@ -423,13 +615,12 @@ QVariant UAVObjectTreeModel::data(const QModelIndex &index, int role) const bool UAVObjectTreeModel::setData(const QModelIndex &index, const QVariant & value, int role) { - Q_UNUSED(role) - TreeItem * item = static_cast(index.internalPointer()); + Q_UNUSED(role); + TreeItem *item = static_cast(index.internalPointer()); item->setData(value, index.column()); return true; } - Qt::ItemFlags UAVObjectTreeModel::flags(const QModelIndex &index) const { if (!index.isValid()) { @@ -452,77 +643,83 @@ QVariant UAVObjectTreeModel::headerData(int section, Qt::Orientation orientation if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { return m_rootItem->data(section); } - return QVariant(); } -void UAVObjectTreeModel::highlightUpdatedObject(UAVObject *obj) +void UAVObjectTreeModel::updateObject(UAVObject *obj) { Q_ASSERT(obj); - ObjectTreeItem *item = findObjectTreeItem(obj); + ObjectTreeItem *item = findObjectTreeItem(obj->getObjID()); Q_ASSERT(item); - if (!m_onlyHilightChangedValues) { - item->setHighlight(true); - } - item->update(); - if (!m_onlyHilightChangedValues) { - QModelIndex itemIndex = index(item); - Q_ASSERT(itemIndex != QModelIndex()); - emit dataChanged(itemIndex, itemIndex); + // TODO don't update meta object if they are not shown + QTime ts = QTime::currentTime(); + item->update(ts); + if (!onlyHighlightChangedValues()) { + item->setHighlighted(true, ts); } } -ObjectTreeItem *UAVObjectTreeModel::findObjectTreeItem(UAVObject *object) +void UAVObjectTreeModel::updateIsKnown(UAVObject *object) { - UAVDataObject *dataObject = qobject_cast(object); + DataObjectTreeItem *item = findDataObjectTreeItem(object); + + if (item) { + refreshIsKnown(item); + } +} + +void UAVObjectTreeModel::refreshHighlight(TreeItem *item) +{ + // performance note: here we emit data changes column by column + // emitting a dataChanged that spans multiple columns kills performance (CPU shoots up) + // this is probably caused by the sort/filter proxy... + + QModelIndex itemIndex; + + itemIndex = index(item, TreeItem::TITLE_COLUMN); + Q_ASSERT(itemIndex != QModelIndex()); + emit dataChanged(itemIndex, itemIndex); + + itemIndex = index(item, TreeItem::DATA_COLUMN); + Q_ASSERT(itemIndex != QModelIndex()); + emit dataChanged(itemIndex, itemIndex); +} + +void UAVObjectTreeModel::refreshIsKnown(TreeItem *item) +{ + QModelIndex itemIndex; + + itemIndex = index(item, TreeItem::TITLE_COLUMN); + Q_ASSERT(itemIndex != QModelIndex()); + emit dataChanged(itemIndex, itemIndex); + + foreach(TreeItem * child, item->children()) { + refreshIsKnown(child); + } +} + +void UAVObjectTreeModel::addObjectTreeItem(quint32 objectId, ObjectTreeItem *oti) +{ + m_objectTreeItems[objectId] = oti; +} + +ObjectTreeItem *UAVObjectTreeModel::findObjectTreeItem(quint32 objectId) +{ + return m_objectTreeItems.value(objectId, 0); +} + +DataObjectTreeItem *UAVObjectTreeModel::findDataObjectTreeItem(UAVObject *object) +{ + UAVDataObject *dataObject; + UAVMetaObject *metaObject = qobject_cast(object); - Q_ASSERT(dataObject || metaObject); - if (dataObject) { - return findDataObjectTreeItem(dataObject); + if (metaObject) { + dataObject = qobject_cast(metaObject->getParentObject()); } else { - return findMetaObjectTreeItem(metaObject); + dataObject = qobject_cast(object); } - return 0; -} - -DataObjectTreeItem *UAVObjectTreeModel::findDataObjectTreeItem(UAVDataObject *obj) -{ - TopTreeItem *root = obj->isSettingsObject() ? m_settingsTree : m_nonSettingsTree; - - return root->findDataObjectTreeItemByObjectId(obj->getObjID()); -} - -MetaObjectTreeItem *UAVObjectTreeModel::findMetaObjectTreeItem(UAVMetaObject *obj) -{ - UAVDataObject *dataObject = qobject_cast(obj->getParentObject()); Q_ASSERT(dataObject); - TopTreeItem *root = dataObject->isSettingsObject() ? m_settingsTree : m_nonSettingsTree; - return root->findMetaObjectTreeItemByObjectId(obj->getObjID()); -} - -void UAVObjectTreeModel::updateHighlight(TreeItem *item) -{ - QModelIndex itemIndex = index(item); - - Q_ASSERT(itemIndex != QModelIndex()); - emit dataChanged(itemIndex, itemIndex.sibling(itemIndex.row(), TreeItem::DATA_COLUMN)); -} - -void UAVObjectTreeModel::updateIsKnown(TreeItem *item) -{ - QModelIndex itemIndex = index(item); - - Q_ASSERT(itemIndex != QModelIndex()); - emit dataChanged(itemIndex, itemIndex.sibling(itemIndex.row(), TreeItem::TITLE_COLUMN)); -} - -void UAVObjectTreeModel::isKnownChanged(UAVObject *object, bool isKnown) -{ - Q_UNUSED(isKnown); - ObjectTreeItem *item = findObjectTreeItem(object); - if (item) { - item->updateIsKnown(isKnown); - } + return static_cast(findObjectTreeItem(dataObject->getObjID())); } diff --git a/ground/gcs/src/plugins/uavobjectbrowser/uavobjecttreemodel.h b/ground/gcs/src/plugins/uavobjectbrowser/uavobjecttreemodel.h index b7c1d2e8a..e6f641ae5 100644 --- a/ground/gcs/src/plugins/uavobjectbrowser/uavobjecttreemodel.h +++ b/ground/gcs/src/plugins/uavobjectbrowser/uavobjecttreemodel.h @@ -29,10 +29,12 @@ #define UAVOBJECTTREEMODEL_H #include "treeitem.h" + #include -#include -#include +#include +#include #include +#include class TopTreeItem; class ObjectTreeItem; @@ -48,8 +50,7 @@ class QTimer; class UAVObjectTreeModel : public QAbstractItemModel { Q_OBJECT public: - explicit UAVObjectTreeModel(QObject *parent, bool categorize, bool showMetadata, bool useScientificNotation); - explicit UAVObjectTreeModel(bool categorize, bool showMetadata, bool useScientificNotation); + explicit UAVObjectTreeModel(QObject *parent); ~UAVObjectTreeModel(); QVariant data(const QModelIndex &index, int role) const; @@ -63,71 +64,77 @@ public: int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; - void setUnknowObjectColor(QColor color) - { - m_unknownObjectColor = color; - } - void setRecentlyUpdatedColor(QColor color) - { - m_recentlyUpdatedColor = color; - } - void setManuallyChangedColor(QColor color) - { - m_manuallyChangedColor = color; - } - void setRecentlyUpdatedTimeout(int timeout) - { - m_recentlyUpdatedTimeout = timeout; - TreeItem::setHighlightTime(timeout); - } - void setOnlyHilightChangedValues(bool hilight) - { - m_onlyHilightChangedValues = hilight; - } + bool showCategories() const; + void setShowCategories(bool show); - QList getMetaDataIndexes(); + bool showMetadata() const; + void setShowMetadata(bool show); -signals: + bool useScientificNotation(); + void setUseScientificNotation(bool useScientificNotation); -public slots: - void newObject(UAVObject *obj); + QColor unknownObjectColor() const; + void setUnknownObjectColor(QColor color); + + QColor recentlyUpdatedColor() const; + void setRecentlyUpdatedColor(QColor color); + + QColor manuallyChangedColor() const; + void setManuallyChangedColor(QColor color); + + int recentlyUpdatedTimeout() const; + void setRecentlyUpdatedTimeout(int timeout); + + bool onlyHighlightChangedValues() const; + void setOnlyHighlightChangedValues(bool highlight); + + bool highlightTopTreeItems() const; + void setHighlightTopTreeItems(bool highlight); private slots: - void updateHighlight(TreeItem *item); - void updateIsKnown(TreeItem *item); - void highlightUpdatedObject(UAVObject *obj); - void isKnownChanged(UAVObject *object, bool isKnown); + void newObject(UAVObject *obj); + void updateObject(UAVObject *obj); + void updateIsKnown(UAVObject *obj); + void refreshHighlight(TreeItem *item); + void refreshIsKnown(TreeItem *item); private: - void setupModelData(UAVObjectManager *objManager); - QModelIndex index(TreeItem *item); - void addDataObject(UAVDataObject *obj); - MetaObjectTreeItem *addMetaObject(UAVMetaObject *obj, TreeItem *parent); - void addArrayField(UAVObjectField *field, TreeItem *parent); - void addSingleField(int index, UAVObjectField *field, TreeItem *parent); - void addInstance(UAVObject *obj, TreeItem *parent); + QSettings m_settings; - TreeItem *createCategoryItems(QStringList categoryPath, TreeItem *root); - - QString updateMode(quint8 updateMode); - ObjectTreeItem *findObjectTreeItem(UAVObject *obj); - DataObjectTreeItem *findDataObjectTreeItem(UAVDataObject *obj); - MetaObjectTreeItem *findMetaObjectTreeItem(UAVMetaObject *obj); + HighlightManager *m_highlightManager; TreeItem *m_rootItem; TopTreeItem *m_settingsTree; TopTreeItem *m_nonSettingsTree; - bool m_categorize; - bool m_showMetadata; - bool m_useScientificFloatNotation; - int m_recentlyUpdatedTimeout; - QColor m_recentlyUpdatedColor; - QColor m_manuallyChangedColor; - QColor m_unknownObjectColor; - bool m_onlyHilightChangedValues; - // Highlight manager to handle highlighting of tree items. - HighLightManager *m_highlightManager; + QHash m_objectTreeItems; + + QModelIndex index(TreeItem *item, int column = 0) const; + + void setupModelData(); + void resetModelData(); + + void addObject(UAVDataObject *obj); + void addArrayField(UAVObjectField *field, TreeItem *parent); + void addSingleField(int index, UAVObjectField *field, TreeItem *parent); + + DataObjectTreeItem *createDataObject(UAVDataObject *obj); + InstanceTreeItem *createDataObjectInstance(UAVDataObject *obj); + MetaObjectTreeItem *createMetaObject(UAVMetaObject *obj); + TreeItem *getParentItem(UAVDataObject *obj, bool categorize); + + void appendItem(TreeItem *parentItem, TreeItem *childItem); + void insertItem(TreeItem *parentItem, TreeItem *childItem, int row = -1); + void removeItem(TreeItem *parentItem, TreeItem *childItem); + void moveItem(TreeItem *newParentItem, TreeItem *oldParentItem, TreeItem *childItem); + + void toggleCategoryItems(); + void toggleMetaItems(); + + void addObjectTreeItem(quint32 objectId, ObjectTreeItem *oti); + ObjectTreeItem *findObjectTreeItem(quint32 objectId); + + DataObjectTreeItem *findDataObjectTreeItem(UAVObject *obj); }; #endif // UAVOBJECTTREEMODEL_H diff --git a/ground/gcs/src/plugins/uavobjects/uavobject.cpp b/ground/gcs/src/plugins/uavobjects/uavobject.cpp index 63b07b8fc..8a95d873b 100644 --- a/ground/gcs/src/plugins/uavobjects/uavobject.cpp +++ b/ground/gcs/src/plugins/uavobjects/uavobject.cpp @@ -622,7 +622,7 @@ void UAVObject::setIsKnown(bool isKnown) unlock(); if (changed) { - emit isKnownChanged(this, isKnown); + emit isKnownChanged(this); } } diff --git a/ground/gcs/src/plugins/uavobjects/uavobject.h b/ground/gcs/src/plugins/uavobjects/uavobject.h index 26c24f037..a1e332b16 100644 --- a/ground/gcs/src/plugins/uavobjects/uavobject.h +++ b/ground/gcs/src/plugins/uavobjects/uavobject.h @@ -181,7 +181,7 @@ signals: void updateRequested(UAVObject *obj, bool all = false); void transactionCompleted(UAVObject *obj, bool success); void newInstance(UAVObject *obj); - void isKnownChanged(UAVObject *obj, bool isKnown); + void isKnownChanged(UAVObject *obj); protected: quint32 objID; diff --git a/ground/gcs/src/plugins/uavtalk/telemetry.cpp b/ground/gcs/src/plugins/uavtalk/telemetry.cpp index 78602919f..e118b3291 100644 --- a/ground/gcs/src/plugins/uavtalk/telemetry.cpp +++ b/ground/gcs/src/plugins/uavtalk/telemetry.cpp @@ -79,7 +79,7 @@ Telemetry::~Telemetry() closeAllTransactions(); foreach(QList instances, objMngr->getObjects()) { foreach(UAVObject * object, instances) { - // make sure we 'forget' all objects before we request it from the flight side + // 'forget' all objects object->setIsKnown(false); } } @@ -244,9 +244,8 @@ void Telemetry::transactionCompleted(UAVObject *obj, bool success) if (transInfo) { if (success) { - // We now know tat the flight side knows of this object. + // We now know that the flight side knows of this object. obj->setIsKnown(true); - #ifdef VERBOSE_TELEMETRY qDebug() << "Telemetry - transaction successful for object" << obj->toStringBrief(); #endif diff --git a/ground/gcs/src/plugins/uavtalk/uavtalk.cpp b/ground/gcs/src/plugins/uavtalk/uavtalk.cpp index 8bb246d90..dc4b4d18a 100644 --- a/ground/gcs/src/plugins/uavtalk/uavtalk.cpp +++ b/ground/gcs/src/plugins/uavtalk/uavtalk.cpp @@ -365,7 +365,7 @@ bool UAVTalk::processInputByte(quint8 rxbyte) { UAVObject *rxObj = objMngr->getObject(rxObjId); if (rxObj == NULL && rxType != TYPE_OBJ_REQ) { - qWarning() << "UAVTalk - error : unknown object" << rxObjId; + qWarning().noquote() << "UAVTalk - error : unknown object" << QString::number(rxObjId, 16).toUpper(); stats.rxErrors++; rxState = STATE_ERROR; break; @@ -385,7 +385,7 @@ bool UAVTalk::processInputByte(quint8 rxbyte) // Check length and determine next state if (rxLength >= MAX_PAYLOAD_LENGTH) { // packet error - exceeded payload max length - qWarning() << "UAVTalk - error : exceeded payload max length" << rxObjId; + qWarning().noquote() << "UAVTalk - error : exceeded payload max length" << QString::number(rxObjId, 16).toUpper(); stats.rxErrors++; rxState = STATE_ERROR; break; @@ -394,7 +394,7 @@ bool UAVTalk::processInputByte(quint8 rxbyte) // Check the lengths match if ((rxPacketLength + rxLength) != packetSize) { // packet error - mismatched packet size - qWarning() << "UAVTalk - error : mismatched packet size" << rxObjId; + qWarning().noquote() << "UAVTalk - error : mismatched packet size" << QString::number(rxObjId, 16).toUpper(); stats.rxErrors++; rxState = STATE_ERROR; break; @@ -430,7 +430,7 @@ bool UAVTalk::processInputByte(quint8 rxbyte) if (rxCS != rxCSPacket) { // packet error - faulty CRC - qWarning() << "UAVTalk - error : failed CRC check" << rxObjId; + qWarning().noquote() << "UAVTalk - error : failed CRC check" << QString::number(rxObjId, 16).toUpper(); stats.rxCrcErrors++; rxState = STATE_ERROR; break; @@ -438,7 +438,7 @@ bool UAVTalk::processInputByte(quint8 rxbyte) if (rxPacketLength != packetSize + CHECKSUM_LENGTH) { // packet error - mismatched packet size - qWarning() << "UAVTalk - error : mismatched packet size" << rxObjId; + qWarning().noquote() << "UAVTalk - error : mismatched packet size" << QString::number(rxObjId, 16).toUpper(); stats.rxErrors++; rxState = STATE_ERROR; break; diff --git a/ground/gcs/src/share/configurations/default.xml b/ground/gcs/src/share/configurations/default.xml index 65dc90947..8a5b4766d 100644 --- a/ground/gcs/src/share/configurations/default.xml +++ b/ground/gcs/src/share/configurations/default.xml @@ -2780,7 +2780,7 @@ #5baa56 false #ff7957 - 500 + 300 false