RoboDK Plug-In Interface
Loading...
Searching...
No Matches
stationtreeeventmonitor.cpp
1/****************************************************************************
2**
3** Copyright (c) 2015-2025 RoboDK Inc.
4** Contact: https://robodk.com/
5**
6** This file is part of the RoboDK API.
7**
8** Permission is hereby granted, free of charge, to any person obtaining a copy
9** of this software and associated documentation files (the "Software"), to deal
10** in the Software without restriction, including without limitation the rights
11** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12** copies of the Software, and to permit persons to whom the Software is
13** furnished to do so, subject to the following conditions:
14**
15** The above copyright notice and this permission notice shall be included in all
16** copies or substantial portions of the Software.
17**
18** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24** SOFTWARE.
25**
26** RoboDK is a registered trademark of RoboDK Inc.
27**
28****************************************************************************/
29
30#include "stationtreeeventmonitor.h"
31
32#include <stack>
33
34#include <QTreeWidget>
35#include <QTreeWidgetItem>
36#include <QDebug>
37#include <QTimer>
38
39#include "iitem.h"
40
41
42namespace robodk
43{
44
45StationTreeEventMonitor::StationTreeEventMonitor(IRoboDK* rdk, QObject* parent)
46 : QObject(parent)
47 , _rdk(rdk)
48{
49 if (!_rdk)
50 return;
51
52 const auto items = _rdk->getItemList();
53 for (const auto& item : items)
54 {
55 auto treeItem = dynamic_cast<QTreeWidgetItem*>(item);
56 if (treeItem && treeItem->treeWidget())
57 {
58 _tree = treeItem->treeWidget();
59 break;
60 }
61 }
62
63 if (!_tree)
64 return;
65
66 auto model = _tree->model();
67
68 using ModelClass = QAbstractItemModel;
69 using ThisClass = StationTreeEventMonitor;
70 connect(model, &ModelClass::modelReset, this, &ThisClass::refresh);
71 connect(model, &ModelClass::dataChanged, this, &ThisClass::onModelDataChanged);
72 connect(model, &ModelClass::rowsInserted, this, &ThisClass::onModelRowsInserted);
73 connect(model, &ModelClass::rowsRemoved, this, &ThisClass::onModelRowsRemoved);
74
75 refresh();
76}
77
78void StationTreeEventMonitor::refresh()
79{
80 _addedIndices.clear();
81 _nameTable.clear();
82 _nameCache.clear();
83
84 if (!_tree || !_tree->model())
85 return;
86
87 iterateOverTree(QModelIndex(), [this] (const QModelIndex& index)
88 {
89 auto item = itemFromIndex(index);
90 if (!item)
91 return;
92
93 const QString name = index.data().toString();
94 _nameTable.insert({name, item});
95 _nameCache[item] = name;
96 });
97}
98
99void StationTreeEventMonitor::submit()
100{
101 bool child = false;
102
103 auto addItem = [this, &child] (const QModelIndex& index)
104 {
105 auto item = itemFromIndex(index);
106 if (!item)
107 return;
108
109 const QString name = index.data().toString();
110 _nameTable.insert({name, item});
111 _nameCache[item] = name;
112
113 if (_filter & IgnoreAdd)
114 return;
115
116 if ((_filter & IgnoreChildren) && child)
117 return;
118
119 if ((_filter & IgnoreInactiveStations) && !isActiveStationItem(index))
120 return;
121
122 emit itemAdded(item);
123 };
124
125 for (const auto& index : _addedIndices)
126 {
127 child = false;
128 addItem(index);
129
130 child = true;
131 iterateOverTree(index, addItem);
132 }
133
134 _addedIndices.clear();
135}
136
137void StationTreeEventMonitor::onModelDataChanged(
138 const QModelIndex& topLeft,
139 const QModelIndex& bottomRight,
140 const QVector<int>& roles)
141{
142 if (!topLeft.isValid() || !bottomRight.isValid() || topLeft.parent() != bottomRight.parent())
143 return;
144
145 bool nameChanged = false;
146 bool iconChanged = false;
147
148 for (int role : roles)
149 {
150 switch (role)
151 {
152 case Qt::DisplayRole:
153 case Qt::EditRole:
154 nameChanged = true;
155 break;
156 case Qt::DecorationRole:
157 iconChanged = true;
158 break;
159 default:
160 break;
161 }
162 }
163
164 bool isActive = (_filter & IgnoreInactiveStations) == 0 || isActiveStationItem(topLeft);
165 const auto parent = topLeft.parent();
166
167 auto updateItem = [this, nameChanged, iconChanged, isActive, parent] (const QModelIndex& index)
168 {
169 auto item = itemFromIndex(index);
170 if (!item)
171 return;
172
173 const auto cache = _nameCache.find(item);
174 if (cache == _nameCache.end())
175 return;
176
177 const auto range = _nameTable.equal_range(cache->second);
178 for (auto it = range.first; it != range.second; ++it)
179 {
180 if (it->second == item)
181 {
182 _nameTable.erase(it);
183 break;
184 }
185 }
186
187 const QString name = index.data().toString();
188 _nameTable.insert({name, item});
189 _nameCache[item] = name;
190
191 if (!isActive)
192 return;
193
194 if ((_filter & IgnoreChildren) && parent != index.parent())
195 return;
196
197 if (nameChanged && (_filter & IgnoreNameChange) == 0)
198 emit itemNameChanged(item, name);
199
200 if (iconChanged && (_filter & IgnoreIconChange) == 0)
201 emit itemIconChanged(item, qvariant_cast<QIcon>(index.data(Qt::DecorationRole)));
202 };
203
204 for (int row = topLeft.row(); row <= bottomRight.row(); ++row)
205 {
206 auto index = _tree->model()->index(row, 0, topLeft.parent());
207 if (!index.isValid())
208 continue;
209
210 updateItem(index);
211 iterateOverTree(index, updateItem);
212 }
213}
214
215void StationTreeEventMonitor::onModelRowsInserted(const QModelIndex& parent, int first, int last)
216{
217 if (_filter & IgnoreAdd)
218 return;
219
220 for (int row = first; row <= last; ++row)
221 {
222 auto index = _tree->model()->index(row, 0, parent);
223 if (index.isValid())
224 {
225 auto it = std::find(_addedIndices.begin(), _addedIndices.end(), index);
226 if (it == _addedIndices.end())
227 _addedIndices.push_back(index);
228 }
229 }
230
231 if (_policy == AutoSubmit)
232 QTimer::singleShot(0, this, &StationTreeEventMonitor::submit);
233}
234
235void StationTreeEventMonitor::onModelRowsRemoved(const QModelIndex& parent, int first, int last)
236{
237 auto removeItem = [this, &parent] (const QModelIndex& index)
238 {
239 auto item = itemFromIndex(index);
240 if (!item)
241 return;
242
243 const auto cache = _nameCache.find(item);
244 if (cache == _nameCache.end())
245 return;
246
247 const auto range = _nameTable.equal_range(cache->second);
248 for (auto it = range.first; it != range.second; ++it)
249 {
250 if (it->second == item)
251 {
252 _nameTable.erase(it);
253 break;
254 }
255 }
256
257 _nameCache.erase(cache);
258
259 if (_filter & IgnoreRemove)
260 return;
261
262 if ((_filter & IgnoreChildren) && parent != index.parent())
263 return;
264
265 if ((_filter & IgnoreInactiveStations) && !isActiveStationItem(index))
266 return;
267
268 emit itemRemoved(item);
269 };
270
271 for (int row = last; row >= first; --row)
272 {
273 auto index = _tree->model()->index(row, 0, parent);
274 if (!index.isValid())
275 continue;
276
277 iterateOverTree(index, removeItem, true);
278 removeItem(index);
279 }
280}
281
282void StationTreeEventMonitor::iterateOverTree(
283 const QModelIndex& parent,
284 const TreeCallback& callback,
285 bool reverse)
286{
287 auto model = _tree->model();
288
289 using StackEntry = std::pair<int, QModelIndex>;
290 std::stack<StackEntry> stack;
291 stack.push({0, parent});
292
293 while (!stack.empty())
294 {
295 auto& entry = stack.top();
296 const auto& parent = entry.second;
297 const int start = entry.first;
298 const int count = model->rowCount(parent);
299
300 bool goBack = true;
301
302 if (reverse && start > 0)
303 {
304 auto index = model->index(count - start, 0, parent);
305 callback(index);
306 }
307
308 for (int i = start; i < count; ++i)
309 {
310 int row = reverse ? (count - i - 1) : i;
311
312 auto index = model->index(row, 0, parent);
313 if (!index.isValid())
314 continue;
315
316 if (!reverse)
317 callback(index);
318
319 if (model->hasChildren(index))
320 {
321 entry.first = i + 1;
322 stack.push({0, index});
323 goBack = false;
324 break;
325 }
326 else if (reverse)
327 {
328 callback(index);
329 }
330 }
331
332 if (goBack)
333 stack.pop();
334 }
335}
336
337IItem* StationTreeEventMonitor::itemFromIndex(const QModelIndex& index) const
338{
339 auto treeItem = static_cast<QTreeWidgetItem*>(index.internalPointer());
340 return dynamic_cast<IItem*>(treeItem);
341}
342
343bool StationTreeEventMonitor::isActiveStationItem(const QModelIndex& index) const
344{
345 QModelIndex parent = index;
346 while (parent.parent().isValid())
347 parent = parent.parent();
348
349 if (!parent.isValid())
350 return false;
351
352 auto item = itemFromIndex(parent);
353 return item && _rdk && item == _rdk->getActiveStation();
354}
355
356} // namespace robodk
The Item class represents an item in RoboDK station. An item can be a robot, a frame,...
Definition iitem.h:14
This class is the iterface to the RoboDK API. With the RoboDK API you can automate certain tasks and ...
Definition irobodk.h:14