CVB++ 15.0
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 case Qt::BackgroundColorRole:
197 if (!searchText_.isEmpty())
198 {
199 auto param = prop->Value(0, Qt::DisplayRole).toString();
200 if (param.contains(searchText_, Qt::CaseInsensitive))
201 {
202 return searchResultBGColor_;
203 }
204 }
205 break;
206 case Qt::DecorationRole:
207 if (prop->Type() == Private::Property::PT_Category)
208 return QVariant();
209
210 if (index.column() == 0 && prop->NodeHasPolling())
211 {
212 auto by = QByteArray::fromBase64(pollingImg_.toLatin1());
213 auto img = QImage::fromData(by, "PNG");
214 return QIcon(QPixmap::fromImage(img));
215 }
216 break;
217 };
218 return QVariant();
219 }
220
221 bool setData(const QModelIndex &index, const QVariant &value, int role) override
222 {
223 if (!index.isValid())
224 return false;
225
226 switch (role)
227 {
228 case Qt::EditRole:
229 return true;
230 case Qt::CheckStateRole:
231 {
232 auto prop = static_cast<Private::Property *>(index.internalPointer());
233 if (!prop)
234 return false;
235
236 prop->SetValue(value);
237 return true;
238 }
239 }
240
241 return false;
242 }
243
244 QVariant headerData(int section, Qt::Orientation orientation, int role) const override
245 {
246 if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
247 {
248 switch (section)
249 {
250 case 0:
251 {
252 auto header = QString("Property");
253 if (maxVisibility_ == GenApi::Visibility::Beginner)
254 header.append(" (Beginner)");
255 else if (maxVisibility_ == GenApi::Visibility::Expert)
256 header.append(" (Expert)");
257 else if (maxVisibility_ == GenApi::Visibility::Guru)
258 header.append(" (Guru)");
259 return header;
260 }
261 case 1:
262 {
263 return tr("Value");
264 }
265 }
266 }
267 return QVariant();
268 }
269
270 QModelIndex buddy(const QModelIndex &index) const override
271 {
272 if (index.isValid() && index.column() == 0)
273 return createIndex(index.row(), 1, index.internalPointer());
274 return index;
275 }
276
277 GenApi::NodeMapPtr NodeMap() const
278 {
279 return nodeMap_;
280 }
281
282 void SetNodeMap(const NodeMapPtr &nodeMap)
283 {
284 Clear();
285
286 nodeMap_ = nodeMap;
287 ScanNodeMap();
288 }
289
290 GenApi::Visibility MaxVisibility() const
291 {
292 return maxVisibility_;
293 }
294
295 void SetMaxVisibility(Visibility visibility)
296 {
297 maxVisibility_ = visibility;
298 ScanNodeMap();
299 }
300
301 QModelIndexList Search(const QString &text)
302 {
303 searchText_ = text;
304 if (searchText_.isEmpty())
305 return QModelIndexList();
306 return match(index(0, 0, QModelIndex()), Qt::DisplayRole, QVariant::fromValue(searchText_), -1,
307 Qt::MatchRecursive | Qt::MatchContains);
308 }
309
310 QModelIndex FindExactMatch(const QString &text)
311 {
312 if (text.isEmpty())
313 return QModelIndex();
314
315 auto list =
316 match(index(0, 0, QModelIndex()), Qt::DisplayRole, QVariant::fromValue(text), -1, Qt::MatchRecursive);
317 if (list.size() == 1)
318 return list.at(0);
319
320 return QModelIndex();
321 }
322
323 Private::Property *Property(const QString &text)
324 {
325 auto index = FindExactMatch(text);
326 return Item(index);
327 }
328
329 Private::Property *Property(const QModelIndex &index)
330 {
331 return Item(index);
332 }
333
334 void ResetSearch()
335 {
336 searchText_ = "";
337 layoutChanged(); // send signal
338 }
339
340 QString SearchText()
341 {
342 return searchText_;
343 }
344
345 void SetSearchResultBGColor(QColor color)
346 {
347 searchResultBGColor_ = color;
348 }
349
350 QColor SearchResultBGColor()
351 {
352 return searchResultBGColor_;
353 }
354
355 void UpdateProperty(const QModelIndex &index)
356 {
357 dataChanged(index, index); // send signal
358 }
359
360 void Update()
361 {
362 // do not update all properties at timeout as this might happen, if device is disconnected
363 if (!updateLock_)
364 dataChanged(QModelIndex(), QModelIndex()); // send signal
365 }
366
367 void ResetUpdateLock()
368 {
369 updateLock_ = false;
370 }
371
372 void RemoveNodeMap()
373 {
374 nodeMap_.reset();
375 nodeMap_ = nullptr;
376 }
377
378 private:
379 bool ScanNodeMap()
380 {
381 if (nodeMap_)
382 {
383 if (!rootProperty_)
384 {
385 auto rootNode = nodeMap_->Node<CategoryNode>(CVB_LIT("Root"));
386 rootProperty_ = new CategoryProperty(rootNode, nullptr);
387 WalkTree(rootNode, rootProperty_, 1);
388 return true;
389 }
390 else
391 {
392 beginResetModel();
393 auto rootNode = rootProperty_->Node();
394 WalkTree(rootNode, rootProperty_, 1);
395 endResetModel();
396 return true;
397 }
398 }
399 return false;
400 }
401
402 void WalkTree(const NodePtr &parentNode, Private::Property *parentProperty,
403 int depth) // NOLINT(misc-no-recursion)
404 {
405 if (depth >= maxDepth_)
406 return;
407
408 auto children = Children(parentNode);
409 auto excludedNodes = ExcludedNodes(children);
410 int childCount = 0;
411
412 for (int i = 0; i < children.size(); i++)
413 {
414 auto child = children.at(i);
415 if (child == nullptr)
416 continue;
417
418 // Does the node property already exist?
419 auto pos = parentProperty->FindChildProperty(child);
420
421 if (pos > -1) // Property exists
422 {
423 if (!IsValid(child) || !IsVisible(child, MaxVisibility())) // delete
424 {
425 parentProperty->RemoveChild(pos);
426 }
427 else // keep and check sub nodes if available
428 {
429 if (auto category = std::dynamic_pointer_cast<CategoryNode>(child))
430 WalkTree(category, parentProperty->ChildProperty(pos), depth + 1);
431 else if (auto selector = std::dynamic_pointer_cast<SelectorNode>(child))
432 WalkTree(selector, parentProperty->ChildProperty(pos), depth + 1);
433
434 childCount = pos;
435 ++childCount;
436 }
437 }
438 else // Property does not exist
439 {
440 if (!IsValid(child) || !IsVisible(child, MaxVisibility())) // skip
441 continue;
442 if (excludedNodes.contains(Cvb::UI::CvbToQt(child->Name()))) // skip
443 continue;
444
445 auto p = CreateProperty(child, parentProperty); // create
446
447 if (p)
448 {
449 parentProperty->InsertChild(childCount, p);
450 ++childCount;
451 QObject::connect(p, &Property::NodeUpdated, [=](Private::Property *property) {
452 auto index = Index(property, QModelIndex());
453 dataChanged(index, index); // send signal
454 });
455
456 QObject::connect(p, &Property::TimeoutOccurred, [=]() { updateLock_ = true; });
457
458 if (auto category = std::dynamic_pointer_cast<CategoryNode>(child))
459 WalkTree(category, p, depth + 1);
460 else if (auto selector = std::dynamic_pointer_cast<SelectorNode>(child))
461 WalkTree(selector, p, depth + 1);
462 }
463 }
464 }
465 }
466
467 QVector<NodePtr> Children(const NodePtr &parentNode) const
468 {
469 QVector<NodePtr> children;
470 if (auto catNode = std::dynamic_pointer_cast<CategoryNode>(parentNode))
471 {
472 std::vector<NodePtr> categoryNodes = catNode->Nodes();
473 for (std::size_t i = 0; i < categoryNodes.size(); i++)
474 children.push_back(categoryNodes.at(i));
475 }
476 else if (auto selNode = std::dynamic_pointer_cast<SelectorNode>(parentNode))
477 {
478 auto selectedNodes = selNode->SelectedNodes();
479 for (std::size_t i = 0; i < selectedNodes.size(); i++)
480 children.push_back(selectedNodes.at(i));
481 }
482 return children;
483 }
484
485 QSet<QString> ExcludedNodes(QVector<NodePtr> nodelist) const
486 {
487 QSet<QString> excluded;
488 for (int i = 0; i < nodelist.size(); i++)
489 {
490 auto node = nodelist.at(i);
491 if (node)
492 {
493 if (!node->IsFeature() || !node->IsImplemented())
494 continue;
495 }
496
497 if (auto nodeSelector = std::dynamic_pointer_cast<SelectorNode>(node))
498 {
499 auto selectedNodes = nodeSelector->SelectedNodes();
500 for (std::size_t j = 0; j < selectedNodes.size(); j++)
501 {
502 auto selectedNode = selectedNodes.at(j);
503 if (selectedNode)
504 {
505 if (selectedNode->IsFeature())
506 excluded.insert(Cvb::UI::CvbToQt(selectedNode->Name()));
507 }
508 }
509 }
510 }
511 return excluded;
512 }
513
514 Private::Property *CreateProperty(const NodePtr &node, Private::Property *parent)
515 {
516 if (!IsValid(node) || !IsVisible(node, MaxVisibility()))
517 return nullptr;
518
519 if (auto catNode = std::dynamic_pointer_cast<CategoryNode>(node))
520 return new CategoryProperty(catNode, parent);
521 else if (auto strNode = std::dynamic_pointer_cast<StringNode>(node))
522 return new StringProperty(strNode, parent);
523 else if (auto cmdNode = std::dynamic_pointer_cast<CommandNode>(node))
524 return new CommandProperty(cmdNode, parent);
525 else if (auto fNode = std::dynamic_pointer_cast<FloatNode>(node))
526 return new FloatProperty(fNode, parent);
527 else if (auto boolNode = std::dynamic_pointer_cast<BooleanNode>(node))
528 return new BooleanProperty(boolNode, parent);
529 else if (auto enumNode = std::dynamic_pointer_cast<EnumerationNode>(node))
530 return new EnumProperty(enumNode, parent);
531 else if (auto iNode = std::dynamic_pointer_cast<IntegerNode>(node))
532 return new IntegerProperty(iNode, parent);
533
534 return nullptr;
535 }
536
537 static bool IsValid(const NodePtr &node)
538 {
539 return !(!node || node->Visibility() == Visibility::Invisible || node->IsDeprecated()
540 || !node->IsImplemented());
541 }
542
543 QModelIndex Index(Private::Property *property, const QModelIndex &parent) const // NOLINT(misc-no-recursion)
544 {
545 int numRows = rowCount(parent);
546 for (int row = 0; row < numRows; ++row)
547 {
548 auto index = this->index(row, 0, parent);
549 auto prop = Item(index);
550 if (prop == property)
551 return index;
552
553 Index(prop, index);
554 }
555
556 return QModelIndex();
557 }
558
559 static bool IsVisible(const NodePtr &node, Visibility maxVisibility = Visibility::Guru)
560 {
561 return IsVisible(node ? node->Visibility() : Visibility::Invisible, maxVisibility);
562 }
563
564 static bool IsVisible(Visibility visibility, Visibility maxVisibility = Visibility::Guru)
565 {
566 return visibility != Visibility::Invisible && visibility <= maxVisibility;
567 }
568
569 void Clear()
570 {
571 // Reset search text
572 searchText_ = "";
573
574 // Clear the model
575 beginResetModel();
576 removeColumns(0, 2);
577 delete rootProperty_;
578 rootProperty_ = NULL;
579 endResetModel();
580 }
581
582 private:
583 const int maxDepth_ = 20;
584 const int defaultColumns_ = 2;
585 Private::Property *rootProperty_;
586 GenApi::Visibility maxVisibility_;
587 GenApi::NodeMapPtr nodeMap_;
588 QString searchText_;
589 QColor searchResultBGColor_;
590 bool updateLock_;
591 const QString pollingImg_ = QString(
592 "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/"
593 "9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA3ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/"
594 "eHBhY2tldCBiZWdpbj0i77u/"
595 "IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+"
596 "IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNS"
597 "wgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIy"
598 "LXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb2"
599 "0veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxu"
600 "czp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo5YjExYzFmNC"
601 "0yMGVhLWZiNDgtOGVkYi1kN2JlYTA3MDkyZjEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NzU4Q0ZGQjI0MDcxMTFFN0E4NUM5ODMx"
602 "RTEwQ0RGOEEiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NzU4Q0ZGQjE0MDcxMTFFN0E4NUM5ODMxRTEwQ0RGOEEiIHhtcDpDcmVhdG"
603 "9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9"
604 "InhtcC5paWQ6OWIxMWMxZjQtMjBlYS1mYjQ4LThlZGItZDdiZWEwNzA5MmYxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjliMTFjMW"
605 "Y0LTIwZWEtZmI0OC04ZWRiLWQ3YmVhMDcwOTJmMSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94"
606 "cGFja2V0IGVuZD0iciI/"
607 "PjRDl6wAAAL9SURBVHjadFNtSFNRGH7u165Tp9t00805t0ytkD6RMuhDTIqK0sp+REgpRhR9QD+DCPrRnygqLCoqrIj+"
608 "FEXQj8KoKMxUFHSllebHdNbmx+Z0H/dup3MXDlf0wOHc+/K+7znP87yHwV9I1RutRlthfbI2s5KAKSIgPMeyA0G/t2nC2X9z3PmjdX4+M/"
609 "fBchxMBUvr9Rb76WBYzhMFHjptGmgxpnzTmAkEoRIEX9DruTnU3XZGCs4G4g0YlsPCkg0XxDT9KYE22r21HFvL1iHfZqENOIyM/"
610 "cK7lnY8fPoCLvc4BIY0fW15XRWamZ6OnZ6zaHldccUesrG6lrR3fSZzaGh8RM5dvhH/d/"
611 "3ykJqTp0lRWSUpXFPeyHK8wtlgKd60a3TFlr2k09FD5mPHwWOkdOd+"
612 "EgyF4jFKh2yrOUqUAw15hRWs0VZ0OBSWTdXbK7BsSVGCoKKogloUE2LpmlQcr90HWZKgz7EdYZN1mVWKYNvKN/"
613 "xtCFiWAcMwEFWqhPi61auQa84Cq1KX8tSqggyqdq4pKyEpGArDPxOI7Y6vfUhRqxElUWjTNNBr02E1mzDodGXylBbPUquUNR8fWjvw5Vs/"
614 "kpJEVNadiPkVDktYvNCOZ3euxujRGeGUIXFSYXI9E1MxfnOw5piQk22EJEewfvVKCIIAWZZRYLeCpbRGxn6CZ7kpPuT3vooKybVvP7YhP8"
615 "8Sb6AkPr51CZIkw5xlSLhdp6MXfYNOICJ1sZ7hvgZRJczef/"
616 "IcnonJhESDXvdPsYJrjY8gRwl87tG7XMDvdWl0Bl0ggrWO3u8oKy2J8f4fzjfcxrOXb8Az0Zah7tZTnBL0T7rfZ1kXlA6Nue1vmj9Bl56G"
617 "bGMmtU+IFYWp5x2OHpy9eD1WnJKiHhnobK4K+CZ/xh9TUmq6xlpcckWl0R5QxLJQAa3mbPA8BzrCGHCOQo4QsFHp/"
618 "bCjvZ5evyfhNf4ZHA4Z1vzNerPtkDIkkWjUSK1ieY6boKPX5fO4HrgHeu+FA7PSXM1vAQYAJKlQ2kI6nbwAAAAASUVORK5CYII=");
619
620 }; /* class PropertyModel */
621
622 } /* namespace Private */
623 } /* namespace UI */
624
625 CVB_END_INLINE_NS
626
627} /* 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