CVB++ 15.1
Loading...
Searching...
No Matches
detail_property_model.hpp
1#pragma once
2
3#pragma warning(push)
4#pragma warning(disable : 4800)
5#pragma warning(disable : 4251)
6#pragma warning(disable : 4244)
7#pragma warning(disable : 4127)
8#include <QAbstractItemModel>
9#include <QFont>
10#include <QColor>
11#pragma warning(pop)
12
13#include "../../global.hpp"
14#include "../../utilities/system_info.hpp"
15#include "../../genapi/node_map.hpp"
16#include "detail_property.hpp"
17#include "detail_boolean_property.hpp"
18#include "detail_category_property.hpp"
19#include "detail_command_property.hpp"
20#include "detail_enum_property.hpp"
21#include "detail_float_property.hpp"
22#include "detail_integer_property.hpp"
23#include "detail_string_property.hpp"
24
25namespace Cvb
26{
27
28 CVB_BEGIN_INLINE_NS
29
30 namespace UI
31 {
32 namespace Private
33 {
34
35 class PropertyModel : public QAbstractItemModel
36 {
37
38 Q_OBJECT
39
40 public:
41 explicit PropertyModel(QObject *parent = 0) noexcept
42 : QAbstractItemModel(parent)
43 , rootProperty_(NULL)
44 , maxVisibility_(GenApi::Visibility::Beginner)
45 , nodeMap_(nullptr)
46 , searchText_("")
47 , searchResultBGColor_(255, 255, 0)
48 , updateLock_(false)
49 {
50 }
51
52 explicit PropertyModel(const NodeMapPtr &nodemap, QObject *parent = 0) noexcept
53 : QAbstractItemModel(parent)
54 , rootProperty_(NULL)
55 , maxVisibility_(GenApi::Visibility::Beginner)
56 , nodeMap_(nodemap)
57 , searchText_("")
58 , searchResultBGColor_(255, 255, 0)
59 , updateLock_(false)
60 {
61 ScanNodeMap();
62 }
63
64 PropertyModel(const PropertyModel &other) = delete;
65 PropertyModel &operator=(const PropertyModel &other) = delete;
66 PropertyModel(PropertyModel &&other) = delete;
67 PropertyModel &operator=(PropertyModel &&other) = delete;
68 ~PropertyModel()
69 {
70 Clear();
71 }
72
73 Private::Property *Item(const QModelIndex &index) const
74 {
75 if (index.isValid())
76 {
77 auto prop = static_cast<Private::Property *>(index.internalPointer());
78 if (prop)
79 return prop;
80 }
81 return rootProperty_;
82 }
83
84 int rowCount(const QModelIndex &parent) const override
85 {
86 auto prop = rootProperty_;
87 if (parent.isValid())
88 prop = Item(parent);
89
90 if (prop)
91 return prop->ChildCount();
92 return 0;
93 }
94
95 int columnCount(const QModelIndex & /* parent */ = QModelIndex()) const override
96 {
97 return defaultColumns_;
98 }
99
100 Qt::ItemFlags flags(const QModelIndex &index) const override
101 {
102 if (!index.isValid())
103 return Qt::NoItemFlags;
104
105 auto prop = Item(index);
106 if (!prop)
107 return Qt::NoItemFlags;
108
109 if (prop->IsRoot())
110 return Qt::NoItemFlags;
111 else if (prop->IsReadOnly())
112 return Qt::ItemIsSelectable;
113 else if (prop->Type() == Private::Property::PT_Boolean)
114 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
115 else
116 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
117 }
118
119 QModelIndex index(int row, int column, const QModelIndex &parent) const override
120 {
121 if (!hasIndex(row, column, parent))
122 return QModelIndex();
123
124 auto item = rootProperty_;
125 if (parent.isValid())
126 item = static_cast<Private::Property *>(parent.internalPointer());
127 if (item)
128 {
129 auto child = item->Child(row);
130 if (child)
131 return createIndex(row, column, (void *)child);
132 }
133 return QModelIndex();
134 }
135
136 QModelIndex parent(const QModelIndex &child) const override
137 {
138 if (!child.isValid())
139 return QModelIndex();
140
141 auto childProp = static_cast<Private::Property *>(child.internalPointer());
142 if (childProp)
143 {
144 auto prop = childProp->Parent();
145 if (prop)
146 {
147 if (prop != rootProperty_)
148 return createIndex(prop->Position(), 0, (void *)prop);
149 }
150 }
151 return QModelIndex();
152 }
153
154 QVariant data(const QModelIndex &index, int role) const override
155 {
156 if (!index.isValid())
157 return QVariant();
158
159 auto prop = static_cast<Private::Property *>(index.internalPointer());
160 if (!prop)
161 return QVariant();
162
163 /* The try catch blocks are necessary to catch the case if the device
164 does not exist any more and the QVariant type does not fit (QVariantDelegate).
165 Maybe this can be solved in a better way? */
166
167 switch (role)
168 {
169 case Qt::CheckStateRole:
170 if (prop->Type() == Private::Property::PT_Boolean && index.column() == 1)
171 return prop->Value(index.column(), role);
172 break;
173 case Qt::ToolTipRole:
174 case Qt::DisplayRole:
175 case Qt::EditRole:
176 return prop->Value(index.column(), role);
177 break;
178 case Qt::FontRole:
179 if (prop->Type() == Private::Property::PT_Category)
180 {
181 QFont font;
182 font.setBold(true);
183 return font;
184 }
185 break;
186 case Qt::ForegroundRole:
187 if (prop->Type() == Private::Property::PT_Category)
188 {
189 return QColor(Qt::black);
190 }
191 else if (prop->IsReadOnly())
192 {
193 return QColor(Qt::darkGray);
194 }
195 break;
196#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
197 case Qt::BackgroundRole:
198#else
199 case Qt::BackgroundColorRole:
200#endif
201 if (!searchText_.isEmpty())
202 {
203 auto param = prop->Value(0, Qt::DisplayRole).toString();
204 if (param.contains(searchText_, Qt::CaseInsensitive))
205 {
206 return searchResultBGColor_;
207 }
208 }
209 break;
210 case Qt::DecorationRole:
211 if (prop->Type() == Private::Property::PT_Category)
212 return QVariant();
213
214 if (index.column() == 0 && prop->NodeHasPolling())
215 {
216 auto by = QByteArray::fromBase64(pollingImg_.toLatin1());
217 auto img = QImage::fromData(by, "PNG");
218 return QIcon(QPixmap::fromImage(img));
219 }
220 break;
221 };
222 return QVariant();
223 }
224
225 bool setData(const QModelIndex &index, const QVariant &value, int role) override
226 {
227 if (!index.isValid())
228 return false;
229
230 switch (role)
231 {
232 case Qt::EditRole:
233 return true;
234 case Qt::CheckStateRole:
235 {
236 auto prop = static_cast<Private::Property *>(index.internalPointer());
237 if (!prop)
238 return false;
239
240 prop->SetValue(value);
241 return true;
242 }
243 }
244
245 return false;
246 }
247
248 QVariant headerData(int section, Qt::Orientation orientation, int role) const override
249 {
250 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
251 {
252 switch (section)
253 {
254 case 0:
255 {
256 auto header = QString("Property");
257 if (maxVisibility_ == GenApi::Visibility::Beginner)
258 header.append(" (Beginner)");
259 else if (maxVisibility_ == GenApi::Visibility::Expert)
260 header.append(" (Expert)");
261 else if (maxVisibility_ == GenApi::Visibility::Guru)
262 header.append(" (Guru)");
263 return header;
264 }
265 case 1:
266 {
267 return tr("Value");
268 }
269 }
270 }
271 return QVariant();
272 }
273
274 QModelIndex buddy(const QModelIndex &index) const override
275 {
276 if (index.isValid() && index.column() == 0)
277 return createIndex(index.row(), 1, index.internalPointer());
278 return index;
279 }
280
281 GenApi::NodeMapPtr NodeMap() const
282 {
283 return nodeMap_;
284 }
285
286 void SetNodeMap(const NodeMapPtr &nodeMap)
287 {
288 Clear();
289
290 nodeMap_ = nodeMap;
291 ScanNodeMap();
292 }
293
294 GenApi::Visibility MaxVisibility() const
295 {
296 return maxVisibility_;
297 }
298
299 void SetMaxVisibility(Visibility visibility)
300 {
301 maxVisibility_ = visibility;
302 ScanNodeMap();
303 }
304
305 QModelIndexList Search(const QString &text)
306 {
307 searchText_ = text;
308 if (searchText_.isEmpty())
309 return QModelIndexList();
310 return match(index(0, 0, QModelIndex()), Qt::DisplayRole, QVariant::fromValue(searchText_), -1,
311 Qt::MatchRecursive | Qt::MatchContains);
312 }
313
314 QModelIndex FindExactMatch(const QString &text)
315 {
316 if (text.isEmpty())
317 return QModelIndex();
318
319 auto list =
320 match(index(0, 0, QModelIndex()), Qt::DisplayRole, QVariant::fromValue(text), -1, Qt::MatchRecursive);
321 if (list.size() == 1)
322 return list.at(0);
323
324 return QModelIndex();
325 }
326
327 Private::Property *Property(const QString &text)
328 {
329 auto index = FindExactMatch(text);
330 return Item(index);
331 }
332
333 Private::Property *Property(const QModelIndex &index)
334 {
335 return Item(index);
336 }
337
338 void ResetSearch()
339 {
340 searchText_ = "";
341 layoutChanged(); // send signal
342 }
343
344 QString SearchText()
345 {
346 return searchText_;
347 }
348
349 void SetSearchResultBGColor(QColor color)
350 {
351 searchResultBGColor_ = color;
352 }
353
354 QColor SearchResultBGColor()
355 {
356 return searchResultBGColor_;
357 }
358
359 void UpdateProperty(const QModelIndex &index)
360 {
361 dataChanged(index, index); // send signal
362 }
363
364 void Update()
365 {
366 // do not update all properties at timeout as this might happen, if device is disconnected
367 if (!updateLock_)
368 dataChanged(QModelIndex(), QModelIndex()); // send signal
369 }
370
371 void ResetUpdateLock()
372 {
373 updateLock_ = false;
374 }
375
376 void RemoveNodeMap()
377 {
378 nodeMap_.reset();
379 nodeMap_ = nullptr;
380 }
381
382 private:
383 bool ScanNodeMap()
384 {
385 if (nodeMap_)
386 {
387 if (!rootProperty_)
388 {
389 auto rootNode = nodeMap_->Node<CategoryNode>(CVB_LIT("Root"));
390 rootProperty_ = new CategoryProperty(rootNode, nullptr);
391 WalkTree(rootNode, rootProperty_, 1);
392 return true;
393 }
394 else
395 {
396 beginResetModel();
397 auto rootNode = rootProperty_->Node();
398 WalkTree(rootNode, rootProperty_, 1);
399 endResetModel();
400 return true;
401 }
402 }
403 return false;
404 }
405
406 void WalkTree(const NodePtr &parentNode, Private::Property *parentProperty,
407 int depth) // NOLINT(misc-no-recursion)
408 {
409 if (depth >= maxDepth_)
410 return;
411
412 auto children = Children(parentNode);
413 auto excludedNodes = ExcludedNodes(children);
414 int childCount = 0;
415
416 for (int i = 0; i < children.size(); i++)
417 {
418 auto child = children.at(i);
419 if (child == nullptr)
420 continue;
421
422 // Does the node property already exist?
423 auto pos = parentProperty->FindChildProperty(child);
424
425 if (pos > -1) // Property exists
426 {
427 if (!IsValid(child) || !IsVisible(child, MaxVisibility())) // delete
428 {
429 parentProperty->RemoveChild(pos);
430 }
431 else // keep and check sub nodes if available
432 {
433 if (auto category = std::dynamic_pointer_cast<CategoryNode>(child))
434 WalkTree(category, parentProperty->ChildProperty(pos), depth + 1);
435 else if (auto selector = std::dynamic_pointer_cast<SelectorNode>(child))
436 WalkTree(selector, parentProperty->ChildProperty(pos), depth + 1);
437
438 childCount = pos;
439 ++childCount;
440 }
441 }
442 else // Property does not exist
443 {
444 if (!IsValid(child) || !IsVisible(child, MaxVisibility())) // skip
445 continue;
446 if (excludedNodes.contains(Cvb::UI::CvbToQt(child->Name()))) // skip
447 continue;
448
449 auto p = CreateProperty(child, parentProperty); // create
450
451 if (p)
452 {
453 parentProperty->InsertChild(childCount, p);
454 ++childCount;
455 QObject::connect(p, &Property::NodeUpdated, [this](Private::Property *property) {
456 auto index = Index(property, QModelIndex());
457 dataChanged(index, index); // send signal
458 });
459
460 QObject::connect(p, &Property::TimeoutOccurred, [this]() { updateLock_ = true; });
461
462 if (auto category = std::dynamic_pointer_cast<CategoryNode>(child))
463 WalkTree(category, p, depth + 1);
464 else if (auto selector = std::dynamic_pointer_cast<SelectorNode>(child))
465 WalkTree(selector, p, depth + 1);
466 }
467 }
468 }
469 }
470
471 QVector<NodePtr> Children(const NodePtr &parentNode) const
472 {
473 QVector<NodePtr> children;
474 if (auto catNode = std::dynamic_pointer_cast<CategoryNode>(parentNode))
475 {
476 std::vector<NodePtr> categoryNodes = catNode->Nodes();
477 for (std::size_t i = 0; i < categoryNodes.size(); i++)
478 children.push_back(categoryNodes.at(i));
479 }
480 else if (auto selNode = std::dynamic_pointer_cast<SelectorNode>(parentNode))
481 {
482 auto selectedNodes = selNode->SelectedNodes();
483 for (std::size_t i = 0; i < selectedNodes.size(); i++)
484 children.push_back(selectedNodes.at(i));
485 }
486 return children;
487 }
488
489 QSet<QString> ExcludedNodes(QVector<NodePtr> nodelist) const
490 {
491 QSet<QString> excluded;
492 for (int i = 0; i < nodelist.size(); i++)
493 {
494 auto node = nodelist.at(i);
495 if (node)
496 {
497 if (!node->IsFeature() || !node->IsImplemented())
498 continue;
499 }
500
501 if (auto nodeSelector = std::dynamic_pointer_cast<SelectorNode>(node))
502 {
503 auto selectedNodes = nodeSelector->SelectedNodes();
504 for (std::size_t j = 0; j < selectedNodes.size(); j++)
505 {
506 auto selectedNode = selectedNodes.at(j);
507 if (selectedNode)
508 {
509 if (selectedNode->IsFeature())
510 excluded.insert(Cvb::UI::CvbToQt(selectedNode->Name()));
511 }
512 }
513 }
514 }
515 return excluded;
516 }
517
518 Private::Property *CreateProperty(const NodePtr &node, Private::Property *parent)
519 {
520 if (!IsValid(node) || !IsVisible(node, MaxVisibility()))
521 return nullptr;
522
523 if (auto catNode = std::dynamic_pointer_cast<CategoryNode>(node))
524 return new CategoryProperty(catNode, parent);
525 else if (auto strNode = std::dynamic_pointer_cast<StringNode>(node))
526 return new StringProperty(strNode, parent);
527 else if (auto cmdNode = std::dynamic_pointer_cast<CommandNode>(node))
528 return new CommandProperty(cmdNode, parent);
529 else if (auto fNode = std::dynamic_pointer_cast<FloatNode>(node))
530 return new FloatProperty(fNode, parent);
531 else if (auto boolNode = std::dynamic_pointer_cast<BooleanNode>(node))
532 return new BooleanProperty(boolNode, parent);
533 else if (auto enumNode = std::dynamic_pointer_cast<EnumerationNode>(node))
534 return new EnumProperty(enumNode, parent);
535 else if (auto iNode = std::dynamic_pointer_cast<IntegerNode>(node))
536 return new IntegerProperty(iNode, parent);
537
538 return nullptr;
539 }
540
541 static bool IsValid(const NodePtr &node)
542 {
543 return !(!node || node->Visibility() == Visibility::Invisible || node->IsDeprecated()
544 || !node->IsImplemented());
545 }
546
547 QModelIndex Index(Private::Property *property, const QModelIndex &parent) const // NOLINT(misc-no-recursion)
548 {
549 int numRows = rowCount(parent);
550 for (int row = 0; row < numRows; ++row)
551 {
552 auto index = this->index(row, 0, parent);
553 auto prop = Item(index);
554 if (prop == property)
555 return index;
556
557 Index(prop, index);
558 }
559
560 return QModelIndex();
561 }
562
563 static bool IsVisible(const NodePtr &node, Visibility maxVisibility = Visibility::Guru)
564 {
565 return IsVisible(node ? node->Visibility() : Visibility::Invisible, maxVisibility);
566 }
567
568 static bool IsVisible(Visibility visibility, Visibility maxVisibility = Visibility::Guru)
569 {
570 return visibility != Visibility::Invisible && visibility <= maxVisibility;
571 }
572
573 void Clear()
574 {
575 // Reset search text
576 searchText_ = "";
577
578 // Clear the model
579 beginResetModel();
580 removeColumns(0, 2);
581 delete rootProperty_;
582 rootProperty_ = NULL;
583 endResetModel();
584 }
585
586 private:
587 const int maxDepth_ = 20;
588 const int defaultColumns_ = 2;
589 Private::Property *rootProperty_;
590 GenApi::Visibility maxVisibility_;
591 GenApi::NodeMapPtr nodeMap_;
592 QString searchText_;
593 QColor searchResultBGColor_;
594 bool updateLock_;
595 const QString pollingImg_ = QString(
596 "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/"
597 "9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/"
598 "eHBhY2tldCBiZWdpbj0i77u/"
599 "IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+"
600 "IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNS"
601 "wgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIy"
602 "LXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb2"
603 "0veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxu"
604 "czp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo5YjExYzFmNC"
605 "0yMGVhLWZiNDgtOGVkYi1kN2JlYTA3MDkyZjEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NzU4Q0ZGQjI0MDcxMTFFN0E4NUM5ODMx"
606 "RTEwQ0RGOEEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NzU4Q0ZGQjE0MDcxMTFFN0E4NUM5ODMxRTEwQ0RGOEEiIHhtcDpDcmVhdG"
607 "9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9"
608 "InhtcC5paWQ6OWIxMWMxZjQtMjBlYS1mYjQ4LThlZGItZDdiZWEwNzA5MmYxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjliMTFjMW"
609 "Y0LTIwZWEtZmI0OC04ZWRiLWQ3YmVhMDcwOTJmMSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94"
610 "cGFja2V0IGVuZD0iciI/"
611 "PjRDl6wAAAL9SURBVHjadFNtSFNRGH7u165Tp9t00805t0ytkD6RMuhDTIqK0sp+REgpRhR9QD+DCPrRnygqLCoqrIj+"
612 "FEXQj8KoKMxUFHSllebHdNbmx+Z0H/dup3MXDlf0wOHc+/K+7znP87yHwV9I1RutRlthfbI2s5KAKSIgPMeyA0G/t2nC2X9z3PmjdX4+M/"
613 "fBchxMBUvr9Rb76WBYzhMFHjptGmgxpnzTmAkEoRIEX9DruTnU3XZGCs4G4g0YlsPCkg0XxDT9KYE22r21HFvL1iHfZqENOIyM/"
614 "cK7lnY8fPoCLvc4BIY0fW15XRWamZ6OnZ6zaHldccUesrG6lrR3fSZzaGh8RM5dvhH/d/"
615 "3ykJqTp0lRWSUpXFPeyHK8wtlgKd60a3TFlr2k09FD5mPHwWOkdOd+"
616 "EgyF4jFKh2yrOUqUAw15hRWs0VZ0OBSWTdXbK7BsSVGCoKKogloUE2LpmlQcr90HWZKgz7EdYZN1mVWKYNvKN/"
617 "xtCFiWAcMwEFWqhPi61auQa84Cq1KX8tSqggyqdq4pKyEpGArDPxOI7Y6vfUhRqxElUWjTNNBr02E1mzDodGXylBbPUquUNR8fWjvw5Vs/"
618 "kpJEVNadiPkVDktYvNCOZ3euxujRGeGUIXFSYXI9E1MxfnOw5piQk22EJEewfvVKCIIAWZZRYLeCpbRGxn6CZ7kpPuT3vooKybVvP7YhP8"
619 "8Sb6AkPr51CZIkw5xlSLhdp6MXfYNOICJ1sZ7hvgZRJczef/"
620 "IcnonJhESDXvdPsYJrjY8gRwl87tG7XMDvdWl0Bl0ggrWO3u8oKy2J8f4fzjfcxrOXb8Az0Zah7tZTnBL0T7rfZ1kXlA6Nue1vmj9Bl56G"
621 "bGMmtU+IFYWp5x2OHpy9eD1WnJKiHhnobK4K+CZ/xh9TUmq6xlpcckWl0R5QxLJQAa3mbPA8BzrCGHCOQo4QsFHp/"
622 "bCjvZ5evyfhNf4ZHA4Z1vzNerPtkDIkkWjUSK1ieY6boKPX5fO4HrgHeu+FA7PSXM1vAQYAJKlQ2kI6nbwAAAAASUVORK5CYII=");
623
624 }; /* class PropertyModel */
625
626 } /* namespace Private */
627 } /* namespace UI */
628
629 CVB_END_INLINE_NS
630
631} /* namespace Cvb */
std::shared_ptr< Node > NodePtr
Convenience shared pointer for Node.
Definition genapi.hpp:71
Visibility
Feature complexity level.
Definition genapi.hpp:238
@ Expert
More complex feature, that requires deeper knowledge about the feature.
Definition genapi.hpp:244
@ Beginner
Simple feature usable by everybody.
Definition genapi.hpp:242
@ Guru
Definition genapi.hpp:251
std::shared_ptr< NodeMap > NodeMapPtr
Convenience shared pointer for NodeMap.
Definition genapi.hpp:27
Namespace for user interface components.
Definition decl_image_scene.hpp:39
QString CvbToQt(const Cvb::String &text) noexcept
Convenience converter for strings.
Definition ui.hpp:257
Root namespace for the Image Manager interface.
Definition version.hpp:11