diff --git a/app/ui/MainWindow.ui b/app/ui/MainWindow.ui
index 44aecb5..79fbee1 100644
--- a/app/ui/MainWindow.ui
+++ b/app/ui/MainWindow.ui
@@ -112,7 +112,13 @@
-
-
+
+
+
+ Name
+
+
+
diff --git a/core/include/DataSource/DataSourceItem.h b/core/include/DataSource/DataSourceItem.h
index 3b03955..9b82471 100644
--- a/core/include/DataSource/DataSourceItem.h
+++ b/core/include/DataSource/DataSourceItem.h
@@ -25,10 +25,14 @@ class SCIQLOP_CORE_EXPORT DataSourceItem {
public:
/// Key associated with the name of the item
static const QString NAME_DATA_KEY;
+ /// Key associated with the plugin of the item
+ static const QString PLUGIN_DATA_KEY;
explicit DataSourceItem(DataSourceItemType type, const QString &name);
explicit DataSourceItem(DataSourceItemType type, QVariantHash data = {});
+ std::unique_ptr clone() const;
+
/// @return the actions of the item as a vector
QVector actions() const noexcept;
@@ -64,6 +68,46 @@ public:
/// Gets all data
QVariantHash data() const noexcept;
+ /**
+ * Merge in the item the source item passed as parameter.
+ *
+ * The merge is done by adding as child of the item the complete tree represented by the source
+ * item. If a part of the tree already exists in the item (based on the name of the nodes), it
+ * is merged by completing the existing tree by items "leaves" (products, components or nodes
+ * with no child).
+ *
+ * For example, with item representing the tree:
+ * R (root node)
+ * - N1 (node)
+ * -- N11 (node)
+ * --- P1 (product)
+ * --- P2 (product)
+ * - N2 (node)
+ *
+ * and the source item representing the tree:
+ * N1 (root node)
+ * - N11 (node)
+ * -- P3 (product)
+ * - N12 (node)
+ *
+ * The leaves of the source item to merge into the item are N1/N11/P3 and N1/N12 => we therefore
+ * have the following merge result:
+ * R
+ * - N1
+ * -- N11
+ * --- P1
+ * --- P2
+ * --- P3 (added leaf)
+ * -- N12 (added leaf)
+ *
+ * @param item the source item
+ * @remarks No control is performed on products or components that are merged into the same tree
+ * part (two products or components may have the same name)
+ * @remarks the merge is made by copy (source item is not changed and still exists after the
+ * operation)
+ */
+ void merge(const DataSourceItem &item);
+
bool isRoot() const noexcept;
QString name() const noexcept;
diff --git a/core/include/DataSource/DataSourceItemAction.h b/core/include/DataSource/DataSourceItemAction.h
index 72f2a88..9bab4e3 100644
--- a/core/include/DataSource/DataSourceItemAction.h
+++ b/core/include/DataSource/DataSourceItemAction.h
@@ -35,6 +35,8 @@ public:
*/
explicit DataSourceItemAction(const QString &name, ExecuteFunction fun);
+ std::unique_ptr clone() const;
+
QString name() const noexcept;
/// Sets the data source item concerned by the action
diff --git a/core/include/DataSource/DataSourceItemMergeHelper.h b/core/include/DataSource/DataSourceItemMergeHelper.h
new file mode 100644
index 0000000..722da2c
--- /dev/null
+++ b/core/include/DataSource/DataSourceItemMergeHelper.h
@@ -0,0 +1,15 @@
+#ifndef SCIQLOP_DATASOURCEITEMMERGEHELPER_H
+#define SCIQLOP_DATASOURCEITEMMERGEHELPER_H
+
+class DataSourceItem;
+
+/**
+ * @brief The DataSourceItemMergeHelper struct is used to merge two data source items
+ * @sa DataSourceItem::merge()
+ */
+struct DataSourceItemMergeHelper {
+ /// Merges source item into dest item
+ static void merge(const DataSourceItem &source, DataSourceItem &dest);
+};
+
+#endif // SCIQLOP_DATASOURCEITEMMERGEHELPER_H
diff --git a/core/meson.build b/core/meson.build
index 11a1fb6..864624b 100644
--- a/core/meson.build
+++ b/core/meson.build
@@ -31,6 +31,7 @@ core_sources = [
'src/DataSource/DataSourceController.cpp',
'src/DataSource/DataSourceItem.cpp',
'src/DataSource/DataSourceItemAction.cpp',
+ 'src/DataSource/DataSourceItemMergeHelper.cpp',
'src/Network/NetworkController.cpp',
'src/Plugin/PluginManager.cpp',
'src/Settings/SqpSettingsDefs.cpp',
diff --git a/core/src/DataSource/DataSourceController.cpp b/core/src/DataSource/DataSourceController.cpp
index 81adfb3..a7bc3d2 100644
--- a/core/src/DataSource/DataSourceController.cpp
+++ b/core/src/DataSource/DataSourceController.cpp
@@ -12,28 +12,6 @@
Q_LOGGING_CATEGORY(LOG_DataSourceController, "DataSourceController")
-namespace {
-
-/**
- * Builds the metadata of the variable that will be generated from the loading of an item
- * @param dataSourceItem the data source item from which to generate the metadata
- * @return the metadata of the variable
- */
-QVariantHash variableMetadata(const DataSourceItem &dataSourceItem)
-{
- // Variable metadata contains...
-
- // ... all metadata of the item
- auto result = dataSourceItem.data();
-
- // ... and the name of the plugin, recovered from root item
- result.insert(QStringLiteral("plugin"), dataSourceItem.rootItem().name());
-
- return result;
-}
-
-} // namespace
-
class DataSourceController::DataSourceControllerPrivate {
public:
QMutex m_WorkingMutex;
@@ -131,8 +109,7 @@ void DataSourceController::loadProductItem(const QUuid &dataSourceUid,
auto it = impl->m_DataProviders.find(dataSourceUid);
auto dataProvider = (it != impl->m_DataProviders.end()) ? it->second : nullptr;
- emit variableCreationRequested(productItem.name(), variableMetadata(productItem),
- dataProvider);
+ emit variableCreationRequested(productItem.name(), productItem.data(), dataProvider);
}
else {
qCWarning(LOG_DataSourceController()) << tr("Can't load an item that is not a product");
diff --git a/core/src/DataSource/DataSourceItem.cpp b/core/src/DataSource/DataSourceItem.cpp
index 35972b7..39d6599 100644
--- a/core/src/DataSource/DataSourceItem.cpp
+++ b/core/src/DataSource/DataSourceItem.cpp
@@ -1,9 +1,11 @@
#include
#include
+#include
#include
const QString DataSourceItem::NAME_DATA_KEY = QStringLiteral("name");
+const QString DataSourceItem::PLUGIN_DATA_KEY = QStringLiteral("plugin");
struct DataSourceItem::DataSourceItemPrivate {
explicit DataSourceItemPrivate(DataSourceItemType type, QVariantHash data)
@@ -28,6 +30,23 @@ DataSourceItem::DataSourceItem(DataSourceItemType type, QVariantHash data)
{
}
+std::unique_ptr DataSourceItem::clone() const
+{
+ auto result = std::make_unique(impl->m_Type, impl->m_Data);
+
+ // Clones children
+ for (const auto &child : impl->m_Children) {
+ result->appendChild(std::move(child->clone()));
+ }
+
+ // Clones actions
+ for (const auto &action : impl->m_Actions) {
+ result->addAction(std::move(action->clone()));
+ }
+
+ return result;
+}
+
QVector DataSourceItem::actions() const noexcept
{
auto result = QVector{};
@@ -75,6 +94,11 @@ QVariantHash DataSourceItem::data() const noexcept
return impl->m_Data;
}
+void DataSourceItem::merge(const DataSourceItem &item)
+{
+ DataSourceItemMergeHelper::merge(item, *this);
+}
+
bool DataSourceItem::isRoot() const noexcept
{
return impl->m_Parent == nullptr;
@@ -146,7 +170,7 @@ bool DataSourceItem::operator==(const DataSourceItem &other)
if (std::tie(impl->m_Type, impl->m_Data) == std::tie(other.impl->m_Type, other.impl->m_Data)) {
// Compares contents of items' children
return std::equal(std::cbegin(impl->m_Children), std::cend(impl->m_Children),
- std::cbegin(other.impl->m_Children),
+ std::cbegin(other.impl->m_Children), std::cend(other.impl->m_Children),
[](const auto &itemChild, const auto &otherChild) {
return *itemChild == *otherChild;
});
diff --git a/core/src/DataSource/DataSourceItemAction.cpp b/core/src/DataSource/DataSourceItemAction.cpp
index 1677091..10b233a 100644
--- a/core/src/DataSource/DataSourceItemAction.cpp
+++ b/core/src/DataSource/DataSourceItemAction.cpp
@@ -22,6 +22,11 @@ DataSourceItemAction::DataSourceItemAction(const QString &name, ExecuteFunction
{
}
+std::unique_ptr DataSourceItemAction::clone() const
+{
+ return std::make_unique(impl->m_Name, impl->m_Fun);
+}
+
QString DataSourceItemAction::name() const noexcept
{
return impl->m_Name;
diff --git a/core/src/DataSource/DataSourceItemMergeHelper.cpp b/core/src/DataSource/DataSourceItemMergeHelper.cpp
new file mode 100644
index 0000000..91e60af
--- /dev/null
+++ b/core/src/DataSource/DataSourceItemMergeHelper.cpp
@@ -0,0 +1,55 @@
+#include "DataSource/DataSourceItemMergeHelper.h"
+
+#include
+
+namespace {
+
+/**
+ * Finds in a tree an item similar to the item passed in parameter
+ * @param item the item for which to find a similar item
+ * @param root the root item of the tree
+ * @return the similar item if found, nullptr otherwise
+ */
+DataSourceItem *findSimilarItem(const DataSourceItem &item, const DataSourceItem &root)
+{
+ // An item is considered similar to the another item if:
+ // - the items are both nodes AND
+ // - the names of the items are identical
+
+ if (item.type() != DataSourceItemType::NODE) {
+ return nullptr;
+ }
+
+ DataSourceItem *result{nullptr};
+ bool found{false};
+ for (auto i = 0, count = root.childCount(); i < count && !found; ++i) {
+ auto child = root.child(i);
+
+ found = child->type() == DataSourceItemType::NODE
+ && QString::compare(child->name(), item.name(), Qt::CaseInsensitive) == 0;
+ if (found) {
+ result = child;
+ }
+ }
+
+ return result;
+}
+
+} // namespace
+
+void DataSourceItemMergeHelper::merge(const DataSourceItem &source, DataSourceItem &dest)
+{
+ // Checks if the source item can be merged into the destination item (i.e. there is a child item
+ // similar to the source item)
+ if (auto subItem = findSimilarItem(source, dest)) {
+ // If there is an item similar to the source item, applies the merge recursively
+ for (auto i = 0, count = source.childCount(); i < count; ++i) {
+ merge(*source.child(i), *subItem);
+ }
+ }
+ else {
+ // If no item is similar to the source item, the item is copied as the child of the
+ // destination item
+ dest.appendChild(source.clone());
+ }
+}
diff --git a/core/tests/DataSource/DataSourceItemBuilder.cpp b/core/tests/DataSource/DataSourceItemBuilder.cpp
new file mode 100644
index 0000000..c3ff319
--- /dev/null
+++ b/core/tests/DataSource/DataSourceItemBuilder.cpp
@@ -0,0 +1,73 @@
+#include "DataSourceItemBuilder.h"
+
+DataSourceItemBuilder &DataSourceItemBuilder::root(const QString &name)
+{
+ m_Root = std::make_shared(DataSourceItemType::NODE, name);
+ m_Items.push(m_Root.get());
+ return *this;
+}
+
+DataSourceItemBuilder &DataSourceItemBuilder::root(QVariantHash data)
+{
+ m_Root = std::make_shared(DataSourceItemType::NODE, data);
+ m_Items.push(m_Root.get());
+ return *this;
+}
+
+DataSourceItemBuilder &DataSourceItemBuilder::node(const QString &name)
+{
+ return append(DataSourceItemType::NODE, name);
+}
+
+DataSourceItemBuilder &DataSourceItemBuilder::node(QVariantHash data)
+{
+ return append(DataSourceItemType::NODE, std::move(data));
+}
+
+DataSourceItemBuilder &DataSourceItemBuilder::product(const QString &name)
+{
+ return append(DataSourceItemType::PRODUCT, name);
+}
+
+DataSourceItemBuilder &DataSourceItemBuilder::product(QVariantHash data)
+{
+ return append(DataSourceItemType::PRODUCT, std::move(data));
+}
+
+DataSourceItemBuilder &DataSourceItemBuilder::component(const QString &name)
+{
+ return append(DataSourceItemType::COMPONENT, name);
+}
+
+DataSourceItemBuilder &DataSourceItemBuilder::component(QVariantHash data)
+{
+ return append(DataSourceItemType::COMPONENT, std::move(data));
+}
+
+DataSourceItemBuilder &DataSourceItemBuilder::end()
+{
+ m_Items.pop();
+ return *this;
+}
+
+std::shared_ptr DataSourceItemBuilder::build()
+{
+ return m_Root;
+}
+
+DataSourceItemBuilder &DataSourceItemBuilder::append(DataSourceItemType type, const QString &name)
+{
+ append(type, QVariantHash{{DataSourceItem::NAME_DATA_KEY, name}});
+ return *this;
+}
+
+DataSourceItemBuilder &DataSourceItemBuilder::append(DataSourceItemType type, QVariantHash data)
+{
+ auto parentItem = m_Items.top();
+
+ auto insertIndex = parentItem->childCount();
+ parentItem->appendChild(std::make_unique(type, std::move(data)));
+
+ m_Items.push(parentItem->child(insertIndex));
+ return *this;
+}
diff --git a/core/tests/DataSource/DataSourceItemBuilder.h b/core/tests/DataSource/DataSourceItemBuilder.h
new file mode 100644
index 0000000..ce675a4
--- /dev/null
+++ b/core/tests/DataSource/DataSourceItemBuilder.h
@@ -0,0 +1,45 @@
+#ifndef SCIQLOP_DATASOURCEITEMBUILDER_H
+#define SCIQLOP_DATASOURCEITEMBUILDER_H
+
+#include
+
+#include
+#include
+
+/**
+ * @brief The DataSourceItemBuilder class aims to facilitate the creation of a DataSourceItem for unit tests
+ * @sa DataSourceItem
+ */
+class DataSourceItemBuilder {
+public:
+ /// Inits root item
+ DataSourceItemBuilder & root(const QString &name);
+ DataSourceItemBuilder & root(QVariantHash data);
+
+ /// Adds node into the current item
+ DataSourceItemBuilder & node(const QString &name);
+ DataSourceItemBuilder & node(QVariantHash data);
+
+ /// Adds product into the current item
+ DataSourceItemBuilder & product(const QString &name);
+ DataSourceItemBuilder & product(QVariantHash data);
+
+ /// Adds component into the current item
+ DataSourceItemBuilder & component(const QString &name);
+ DataSourceItemBuilder & component(QVariantHash data);
+
+ /// Closes the build of the current item
+ DataSourceItemBuilder& end();
+
+ /// Creates the DataSourceItem
+ std::shared_ptr build();
+
+private:
+ DataSourceItemBuilder& append(DataSourceItemType type, const QString &name);
+ DataSourceItemBuilder& append(DataSourceItemType type, QVariantHash data);
+
+ std::shared_ptr m_Root{nullptr};
+ std::stack m_Items;
+};
+
+#endif // SCIQLOP_DATASOURCEITEMBUILDER_H
diff --git a/core/tests/DataSource/TestDataSourceItem.cpp b/core/tests/DataSource/TestDataSourceItem.cpp
new file mode 100644
index 0000000..dd998f0
--- /dev/null
+++ b/core/tests/DataSource/TestDataSourceItem.cpp
@@ -0,0 +1,207 @@
+#include
+
+#include "DataSourceItemBuilder.h"
+
+#include
+#include
+
+#include
+
+namespace {
+
+void printItem(std::ostream &out, const DataSourceItem &item, int level = 0)
+{
+ for (auto i = 0; i < level; ++i) {
+ out << " ";
+ }
+
+ out << item.name().toStdString() << "\n";
+
+ for (auto i = 0, count = item.childCount(); i < count; ++i) {
+ printItem(out, *item.child(i), level + 1);
+ }
+}
+
+std::ostream &operator<<(std::ostream &out, const DataSourceItem &item)
+{
+ printItem(out, item, 0);
+ return out;
+}
+
+} // namespace
+
+Q_DECLARE_METATYPE(std::shared_ptr)
+
+class TestDataSourceItem : public QObject {
+ Q_OBJECT
+private slots:
+ void testMerge_data();
+ void testMerge();
+};
+
+void TestDataSourceItem::testMerge_data()
+{
+ QTest::addColumn >("source");
+ QTest::addColumn >("dest");
+ QTest::addColumn >("expectedResult");
+
+ QTest::newRow("merge (basic case)") << DataSourceItemBuilder{}
+ .root("A2")
+ .node("- B2")
+ .product("-- P2")
+ .end() // P2
+ .end() // B2
+ .end() // A2
+ .build()
+ << DataSourceItemBuilder{}
+ .root("A1")
+ .node("- B1")
+ .product("-- P1")
+ .end() // P1
+ .end() // B1
+ .end() // A1
+ .build()
+ << DataSourceItemBuilder{}
+ .root("A1")
+ .node("- B1")
+ .product("-- P1")
+ .end() // P1
+ .end() // B1
+ .node("- B2")
+ .product("-- P2")
+ .end() // P2
+ .end() // B2
+ .end() // A1
+ .build();
+
+ QTest::newRow("merge (some of the source and destination trees are identical)")
+ << DataSourceItemBuilder{}
+ .root("A2")
+ .node("- B1")
+ .node("-- C1")
+ .product("--- P2")
+ .end() // P2
+ .end() // C1
+ .node("-- C2")
+ .end() // C2
+ .end() // B1
+ .end() // A2
+ .build()
+ << DataSourceItemBuilder{}
+ .root("A1")
+ .node("- B1")
+ .node("-- C1")
+ .product("--- P1")
+ .end() // P1
+ .end() // C1
+ .end() // B1
+ .end() // A1
+ .build()
+ << DataSourceItemBuilder{}
+ .root("A1")
+ .node("- B1")
+ .node("-- C1")
+ .product("--- P1")
+ .end() // P1
+ .product("--- P2")
+ .end() // P2
+ .end() // C1
+ .node("-- C2")
+ .end() // C2
+ .end() // B1
+ .end() // A1
+ .build();
+
+ QTest::newRow("merge (products with the same name and tree are kept)")
+ << DataSourceItemBuilder{}
+ .root("A2")
+ .node("- B1")
+ .node("-- C1")
+ .product({{"name", "--- P1"}, {"from", "source"}})
+ .end() // P1
+ .end() // C1
+ .end() // B1
+ .end() // A2
+ .build()
+ << DataSourceItemBuilder{}
+ .root("A1")
+ .node("- B1")
+ .node("-- C1")
+ .product({{"name", "--- P1"}, {"from", "dest"}})
+ .end() // P1
+ .end() // C1
+ .end() // B1
+ .end() // A1
+ .build()
+ << DataSourceItemBuilder{}
+ .root("A1")
+ .node("- B1")
+ .node("-- C1")
+ .product({{"name", "--- P1"}, {"from", "dest"}})
+ .end() // P1 (dest)
+ .product({{"name", "--- P1"}, {"from", "source"}})
+ .end() // P1 (source)
+ .end() // C1
+ .end() // B1
+ .end() // A1
+ .build();
+
+ QTest::newRow("merge (for same nodes, metadata of dest node are kept)")
+ << DataSourceItemBuilder{}
+ .root("A2")
+ .node("- B1")
+ .node({{"name", "-- C1"}, {"from", "source"}})
+ .product("--- P2")
+ .end() // P1
+ .end() // C1
+ .end() // B1
+ .end() // A2
+ .build()
+ << DataSourceItemBuilder{}
+ .root("A1")
+ .node("- B1")
+ .node({{"name", "-- C1"}, {"from", "dest"}})
+ .product("--- P1")
+ .end() // P1
+ .end() // C1
+ .end() // B1
+ .end() // A1
+ .build()
+ << DataSourceItemBuilder{}
+ .root("A1")
+ .node("- B1")
+ .node({{"name", "-- C1"}, {"from", "dest"}})
+ .product("--- P1")
+ .end() // P1
+ .product("--- P2")
+ .end() // P2
+ .end() // C1 (dest)
+ .end() // B1
+ .end() // A1
+ .build();
+}
+
+void TestDataSourceItem::testMerge()
+{
+ QFETCH(std::shared_ptr, source);
+ QFETCH(std::shared_ptr, dest);
+ QFETCH(std::shared_ptr, expectedResult);
+
+ // Uncomment to print trees
+ // std::cout << "source: \n" << *source << "\n";
+ // std::cout << "dest: \n" << *dest << "\n";
+
+ // Merges source in dest (not taking source root)
+ for (auto i = 0, count = source->childCount(); i < count; ++i) {
+ dest->merge(*source->child(i));
+ }
+
+ // Uncomment to print trees
+ // std::cout << "dest after merge: \n" << *dest << "\n";
+
+ // Checks merge result
+ QVERIFY(*dest == *expectedResult);
+}
+
+QTEST_MAIN(TestDataSourceItem)
+#include "TestDataSourceItem.moc"
diff --git a/core/tests/meson.build b/core/tests/meson.build
index 8a23801..ec2a714 100644
--- a/core/tests/meson.build
+++ b/core/tests/meson.build
@@ -10,6 +10,7 @@ tests = [
[['Data/TestTwoDimArrayData.cpp'],'test_2d','Two Dim Array test'],
[['Data/TestDataSeriesUtils.cpp'],'test_dataseries_util','Data series utils test'],
[['DataSource/TestDataSourceController.cpp'],'test_data_source','DataSourceController test'],
+ [['DataSource/TestDataSourceItem.cpp'],'test_data_source_item','DataSourceItem test'],
[['Variable/TestVariableCacheController.cpp'],'test_variable_cache','VariableCacheController test'],
[['Variable/TestVariable.cpp'],'test_variable','Variable test'],
[['Variable/TestVariableSync.cpp'],'test_variable_sync','Variable synchronization test']
@@ -19,7 +20,9 @@ amdatest_sources = [
'Data/DataSeriesBuilders.h',
'Data/DataSeriesBuilders.cpp',
'Data/DataSeriesTestsUtils.h',
- 'Data/DataSeriesTestsUtils.cpp'
+ 'Data/DataSeriesTestsUtils.cpp',
+ 'DataSource/DataSourceItemBuilder.h',
+ 'DataSource/DataSourceItemBuilder.cpp'
]
foreach unit_test : tests
diff --git a/gui/include/DataSource/DataSourceWidget.h b/gui/include/DataSource/DataSourceWidget.h
index 615adfd..388dbd8 100644
--- a/gui/include/DataSource/DataSourceWidget.h
+++ b/gui/include/DataSource/DataSourceWidget.h
@@ -3,6 +3,8 @@
#include
+#include
+
namespace Ui {
class DataSourceWidget;
} // Ui
@@ -29,7 +31,10 @@ public slots:
void addDataSource(DataSourceItem *dataSource) noexcept;
private:
+ void updateTreeWidget() noexcept;
+
Ui::DataSourceWidget *ui;
+ std::unique_ptr m_Root;
private slots:
/// Slot called when the filtering text has changed
diff --git a/gui/src/DataSource/DataSourceTreeWidgetItem.cpp b/gui/src/DataSource/DataSourceTreeWidgetItem.cpp
index 67bbeb1..d02d738 100644
--- a/gui/src/DataSource/DataSourceTreeWidgetItem.cpp
+++ b/gui/src/DataSource/DataSourceTreeWidgetItem.cpp
@@ -11,6 +11,41 @@ namespace {
// Column indexes
const auto NAME_COLUMN = 0;
+/**
+ * Generates the full name of an item.
+ *
+ * The full name of an item is its name possibly suffixed by the name of its plugin, in case there
+ * are items of the same name in its relatives
+ * @param item the item for which to generate the complete name
+ * @return the complete name of the item
+ */
+QString completeName(const DataSourceItem &item)
+{
+ auto name = item.name();
+
+ if (item.type() == DataSourceItemType::NODE) {
+ return name;
+ }
+
+ auto parentItem = item.parentItem();
+ if (!parentItem) {
+ return name;
+ }
+
+ // Finds in item's relatives items that have the same name
+ bool foundSameName = false;
+ for (auto i = 0, count = parentItem->childCount(); i < count && !foundSameName; ++i) {
+ auto child = parentItem->child(i);
+ foundSameName = child != &item
+ && QString::compare(child->name(), item.name(), Qt::CaseInsensitive) == 0;
+ }
+
+ // If the name of the item is not unique, it is completed by the plugin suffix
+ return foundSameName
+ ? QString{"%1 (%2)"}.arg(name, item.data(DataSourceItem::PLUGIN_DATA_KEY).toString())
+ : name;
+}
+
QIcon itemIcon(const DataSourceItem *dataSource)
{
if (dataSource) {
@@ -92,10 +127,15 @@ QString itemTooltip(const DataSourceItem *dataSource) noexcept
} // namespace
struct DataSourceTreeWidgetItem::DataSourceTreeWidgetItemPrivate {
- explicit DataSourceTreeWidgetItemPrivate(const DataSourceItem *data) : m_Data{data} {}
+ explicit DataSourceTreeWidgetItemPrivate(const DataSourceItem *data)
+ : m_Data{data}, m_Name{completeName(*m_Data)}
+ {
+ }
/// Model used to retrieve data source information
const DataSourceItem *m_Data;
+ /// Name displayed
+ QString m_Name;
/// Actions associated to the item. The parent of the item (QTreeWidget) takes the ownership of
/// the actions
QList m_Actions;
@@ -151,7 +191,7 @@ QVariant DataSourceTreeWidgetItem::data(int column, int role) const
if (impl->m_Data) {
switch (column) {
case NAME_COLUMN:
- return impl->m_Data->name();
+ return impl->m_Name;
default:
// No action
break;
diff --git a/gui/src/DataSource/DataSourceWidget.cpp b/gui/src/DataSource/DataSourceWidget.cpp
index 454f76b..7934df4 100644
--- a/gui/src/DataSource/DataSourceWidget.cpp
+++ b/gui/src/DataSource/DataSourceWidget.cpp
@@ -36,7 +36,11 @@ DataSourceTreeWidgetItem *createTreeWidgetItem(DataSourceItem *dataSource)
} // namespace
-DataSourceWidget::DataSourceWidget(QWidget *parent) : QWidget{parent}, ui{new Ui::DataSourceWidget}
+DataSourceWidget::DataSourceWidget(QWidget *parent)
+ : QWidget{parent},
+ ui{new Ui::DataSourceWidget},
+ m_Root{
+ std::make_unique(DataSourceItemType::NODE, QStringLiteral("Sources"))}
{
ui->setupUi(this);
@@ -51,6 +55,9 @@ DataSourceWidget::DataSourceWidget(QWidget *parent) : QWidget{parent}, ui{new Ui
// Connection to filter tree
connect(ui->filterLineEdit, &QLineEdit::textChanged, this, &DataSourceWidget::filterChanged);
+
+ // First init
+ updateTreeWidget();
}
DataSourceWidget::~DataSourceWidget() noexcept
@@ -60,13 +67,29 @@ DataSourceWidget::~DataSourceWidget() noexcept
void DataSourceWidget::addDataSource(DataSourceItem *dataSource) noexcept
{
- // Creates the item associated to the source and adds it to the tree widget. The tree widget
- // takes the ownership of the item
+ // Merges the data source (without taking its root)
if (dataSource) {
- ui->treeWidget->addTopLevelItem(createTreeWidgetItem(dataSource));
+ for (auto i = 0, count = dataSource->childCount(); i < count; ++i) {
+ m_Root->merge(*dataSource->child(i));
+ }
+
+ updateTreeWidget();
}
}
+void DataSourceWidget::updateTreeWidget() noexcept
+{
+ ui->treeWidget->clear();
+
+ auto rootItem = createTreeWidgetItem(m_Root.get());
+ ui->treeWidget->addTopLevelItem(rootItem);
+ rootItem->setExpanded(true);
+
+ // Sorts tree
+ ui->treeWidget->setSortingEnabled(true);
+ ui->treeWidget->sortByColumn(0, Qt::AscendingOrder);
+}
+
void DataSourceWidget::filterChanged(const QString &text) noexcept
{
auto validateItem = [&text](const DataSourceTreeWidgetItem &item) {
diff --git a/plugins/amda/src/AmdaPlugin.cpp b/plugins/amda/src/AmdaPlugin.cpp
index 5ce9064..0a033a9 100644
--- a/plugins/amda/src/AmdaPlugin.cpp
+++ b/plugins/amda/src/AmdaPlugin.cpp
@@ -31,11 +31,14 @@ void associateActions(DataSourceItem &item, const QUuid &dataSourceUid)
};
const auto itemType = item.type();
- if (itemType == DataSourceItemType::PRODUCT) {
- addLoadAction(QObject::tr("Load %1 product").arg(item.name()));
- }
- else if (itemType == DataSourceItemType::COMPONENT) {
- addLoadAction(QObject::tr("Load %1 component").arg(item.name()));
+ if (itemType == DataSourceItemType::PRODUCT || itemType == DataSourceItemType::COMPONENT) {
+ // Adds plugin name to item metadata
+ item.setData(DataSourceItem::PLUGIN_DATA_KEY, DATA_SOURCE_NAME);
+
+ // Adds load action
+ auto actionLabel = QObject::tr(
+ itemType == DataSourceItemType::PRODUCT ? "Load %1 product" : "Load %1 component");
+ addLoadAction(actionLabel.arg(item.name()));
}
auto count = item.childCount();
diff --git a/plugins/mockplugin/src/MockPlugin.cpp b/plugins/mockplugin/src/MockPlugin.cpp
index 734200b..1cbf77b 100644
--- a/plugins/mockplugin/src/MockPlugin.cpp
+++ b/plugins/mockplugin/src/MockPlugin.cpp
@@ -25,6 +25,10 @@ std::unique_ptr createProductItem(const QVariantHash &data,
const QUuid &dataSourceUid)
{
auto result = std::make_unique(DataSourceItemType::PRODUCT, data);
+
+ // Adds plugin name to product metadata
+ result->setData(DataSourceItem::PLUGIN_DATA_KEY, DATA_SOURCE_NAME);
+
auto productName = data.value(DataSourceItem::NAME_DATA_KEY).toString();
// Add action to load product from DataSourceController