Isis 3 Programmer Reference
AbstractTreeModel.cpp
1
7/* SPDX-License-Identifier: CC0-1.0 */
8
9#include "AbstractTreeModel.h"
10
11#include <algorithm>
12#include <iostream>
13
14#include <QFutureWatcher>
15#include <QList>
16#include <QModelIndex>
17#include <QMutex>
18#include <QPair>
19#include <QStack>
20#include <QString>
21#include <QtConcurrentFilter>
22
23#include <QtConcurrentMap>
24
25#include <QFlags>
26#include <QtGlobal>
27#include <QVariant>
28
29#include "BusyLeafItem.h"
30#include "TreeView.h"
31#include "ControlMeasure.h"
32#include "ControlNet.h"
33#include "ControlPoint.h"
34#include "IException.h"
35
36#include "AbstractTreeItem.h"
37#include "FilterWidget.h"
38#include "RootItem.h"
39
40
41namespace Isis {
42 AbstractTreeModel::AbstractTreeModel(ControlNet *controlNet, TreeView *v,
43 QObject *parent) : QObject(parent), m_view(v), m_cNet(controlNet) {
44
45 m_filterWatcher = NULL;
46 m_rebuildWatcher = NULL;
47 m_busyItem = NULL;
48 rootItem = NULL;
49 m_expandedState = NULL;
50 m_selectedState = NULL;
51 m_guisFilterWidget = NULL;
52 m_localFilterWidgetCopy = NULL;
53 m_mutex = NULL;
54
55 m_busyItem = new BusyLeafItem(NULL);
56 rootItem = new RootItem;
57 m_expandedState = new QList< QPair< QString, QString > >;
58 m_selectedState = new QList< QPair< QString, QString > >;
59 m_mutex = new QMutex;
60
61 m_filterWatcher = new QFutureWatcher< QAtomicPointer< AbstractTreeItem > >;
62 m_rebuildWatcher = new QFutureWatcher< QAtomicPointer< RootItem > >;
63
64 connect(m_filterWatcher, SIGNAL(finished()), this, SLOT(applyFilterDone()));
65 connect(m_rebuildWatcher, SIGNAL(finished()), this, SLOT(rebuildItemsDone()));
66
67 connect(m_filterWatcher, SIGNAL(progressValueChanged(int)),
68 this, SIGNAL(filterProgressChanged(int)));
69 connect(m_filterWatcher, SIGNAL(progressRangeChanged(int, int)),
70 this, SIGNAL(filterProgressRangeChanged(int, int)));
71 connect(m_rebuildWatcher, SIGNAL(progressValueChanged(int)),
72 this, SIGNAL(rebuildProgressChanged(int)));
73 connect(m_rebuildWatcher, SIGNAL(progressRangeChanged(int, int)),
74 this, SIGNAL(rebuildProgressRangeChanged(int, int)));
75
76 m_drivable = false;
77 m_filterAgain = false;
78 m_filterRunning = false;
79 m_rebuildRunning = false;
80 m_frozen = false;
81 m_rebuildPending = false;
82 }
83
84
85 AbstractTreeModel::~AbstractTreeModel() {
86 delete m_filterWatcher;
87 m_filterWatcher = NULL;
88
89 delete m_rebuildWatcher;
90 m_rebuildWatcher = NULL;
91
92 delete m_busyItem;
93 m_busyItem = NULL;
94
95 delete rootItem;
96 rootItem = NULL;
97
98 delete m_expandedState;
99 m_expandedState = NULL;
100
101 delete m_selectedState;
102 m_selectedState = NULL;
103
104 delete m_mutex;
105 m_mutex = NULL;
106
107 delete m_localFilterWidgetCopy;
108 m_localFilterWidgetCopy = NULL;
109
110 m_guisFilterWidget = NULL;
111 m_cNet = NULL;
112 m_view = NULL;
113 }
114
115
116 // If a negative end is passed in, grabs all items from start to the end of
117 // the tree. No busy leaf items will be inserted.
118 QList< AbstractTreeItem * > AbstractTreeModel::getItems(int start, int end,
119 InterestingItemsFlag flags, bool ignoreExpansion) {
120 QList< AbstractTreeItem * > foundItems;
121 int rowCount = end - start;
122 const AbstractTreeItem *lastVisibleFilteredItem =
123 rootItem->getLastVisibleFilteredItem();
124
125 bool grabToEnd = (start >= 0 && end < 0);
126
127 if (lastVisibleFilteredItem && (rowCount > 0 || grabToEnd) &&
128 rootItem->childCount()) {
129 int row = 0;
130 AbstractTreeItem *currentItem = rootItem->getFirstVisibleChild();
131
132 if (currentItem && !itemIsInteresting(currentItem, flags)) {
133 currentItem = nextItem(currentItem, flags, ignoreExpansion);
134 }
135
136 bool listStillValid = true;
137
138 while (row < start && listStillValid && currentItem) {
139 row++;
140 listStillValid = (currentItem != lastVisibleFilteredItem ||
141 currentItem == currentItem->parent()->getLastVisibleChild());
142
143 if (listStillValid)
144 currentItem = nextItem(currentItem, flags, ignoreExpansion);
145 }
146
147 while ((row < end || grabToEnd) && listStillValid && currentItem) {
148 foundItems.append(currentItem);
149 listStillValid = (currentItem != lastVisibleFilteredItem ||
150 currentItem == currentItem->parent()->getLastVisibleChild());
151 row++;
152
153 if (listStillValid)
154 currentItem = nextItem(currentItem, flags, ignoreExpansion);
155 }
156
157 // Fill in the rest with busy items if needed. If we are grabbing all
158 // items to the end of the visible tree, we do not want any busy items
159 // added to our found items list.
160 while (!grabToEnd && isFiltering() && foundItems.size() < rowCount) {
161 foundItems.append(m_busyItem);
162 }
163 }
164
165 return foundItems;
166 }
167
168
169 QList< AbstractTreeItem * > AbstractTreeModel::getItems(
170 AbstractTreeItem *item1, AbstractTreeItem *item2,
171 InterestingItemsFlag flags, bool ignoreExpansion) {
172 QList< AbstractTreeItem * > foundItems;
173
174 if (rootItem->childCount()) {
175 AbstractTreeItem *start = NULL;
176
177 AbstractTreeItem *curItem = rootItem->getFirstVisibleChild();
178
179 while (!start && curItem) {
180 if (curItem == item1)
181 start = item1;
182 else if (curItem == item2)
183 start = item2;
184
185 if (!start)
186 curItem = nextItem(curItem, flags, ignoreExpansion);
187 }
188
189 if (!start) {
190 QString msg = "The first item passed to getItems(AbstractTreeItem*, "
191 "AbstractTreeItem*) is not visible in this model's tree";
192 throw IException(IException::Programmer, msg, _FILEINFO_);
193 }
194
195 AbstractTreeItem *end = item2;
196
197 // Sometimes we need to build the list forwards and sometimes backwards.
198 // This is accomplished by using either append or prepend. We abstract
199 // away which of these we should use (why should we care) by using the
200 // variable "someKindaPend" to store the appropriate method.
201 void (QList<AbstractTreeItem *>::*someKindaPend)(
202 AbstractTreeItem * const &);
203
204 someKindaPend = &QList<AbstractTreeItem *>::append;
205 if (start == item2) {
206 end = item1;
207 someKindaPend = &QList<AbstractTreeItem *>::prepend;
208 }
209
210 while (curItem && curItem != end) {
211 (foundItems.*someKindaPend)(curItem);
212 curItem = nextItem(curItem, flags, ignoreExpansion);
213 }
214
215 if (!curItem) {
216 QString msg = "The second item passed to getItems(AbstractTreeItem*, "
217 "AbstractTreeItem*) is not visible in this model's tree";
218 throw IException(IException::Programmer, msg, _FILEINFO_);
219 }
220
221 (foundItems.*someKindaPend)(end);
222 }
223
224 return foundItems;
225 }
226
227
228 QList< AbstractTreeItem * > AbstractTreeModel::getSelectedItems(
229 InterestingItemsFlag flags, bool ignoreExpansion) {
230 QList< AbstractTreeItem * > selectedItems;
231
232 if (!isFiltering()) {
233 AbstractTreeItem *currentItem = rootItem->getFirstVisibleChild();
234
235 if (currentItem && !itemIsInteresting(currentItem, flags))
236 currentItem = nextItem(currentItem, flags, ignoreExpansion);
237
238 while (currentItem) {
239 if (currentItem->isSelected())
240 selectedItems.append(currentItem);
241
242 currentItem = nextItem(currentItem, flags, ignoreExpansion);
243 }
244 }
245
246 return selectedItems;
247 }
248
249
250 QMutex *AbstractTreeModel::getMutex() const {
251 return m_mutex;
252 }
253
254
255 int AbstractTreeModel::getItemCount(InterestingItemsFlag flags) const {
256 return getItemCount(rootItem, flags);
257 }
258
259
260 int AbstractTreeModel::getTopLevelItemCount() const {
261 return rootItem->childCount();
262 }
263
264 int AbstractTreeModel::getVisibleItemCount(InterestingItemsFlag flags,
265 bool ignoreExpansion) const {
266 AbstractTreeItem *currentItem = rootItem->getFirstVisibleChild();
267 int count = -1;
268
269 if (!isFiltering()) {
270 count = 0;
271
272 while (currentItem) {
273 if (itemIsInteresting(currentItem, flags)) {
274 count++;
275 }
276
277 currentItem = nextItem(currentItem, flags, ignoreExpansion);
278 }
279 }
280
281 return count;
282 }
283
284
285 int AbstractTreeModel::getVisibleTopLevelItemCount() const {
286 AbstractTreeItem *currentItem = rootItem->getFirstVisibleChild();
287 int count = -1;
288
289 if (!isFiltering()) {
290 count = 0;
291
292 while (currentItem) {
293 count++;
294 currentItem = currentItem->getNextVisiblePeer();
295 }
296 }
297
298 return count;
299 }
300
301
302 int AbstractTreeModel::indexOfVisibleItem(AbstractTreeItem const *item,
303 InterestingItemsFlag flags, bool ignoreExpansion) const {
304 AbstractTreeItem *currentItem = rootItem->getFirstVisibleChild();
305 int index = -1;
306
307 if (!isFiltering()) {
308 while (currentItem && currentItem != item) {
309 if (itemIsInteresting(currentItem, flags))
310 index++;
311
312 currentItem = nextItem(currentItem, flags, ignoreExpansion);
313 }
314
315 index++;
316
317 if (!currentItem)
318 index = -1;
319 }
320
321 return index;
322 }
323
324
325 void AbstractTreeModel::setFrozen(bool newFrozenState) {
326 m_frozen = newFrozenState;
327 if (!m_frozen) {
328 if (m_rebuildPending) {
329 rebuildItems();
330 m_rebuildPending = false;
331 }
332 else {
333 applyFilter();
334 }
335 }
336 }
337
338
339 bool AbstractTreeModel::isFrozen() const {
340 return m_frozen;
341 }
342
343
344 void AbstractTreeModel::queueRebuild() {
345 m_rebuildPending = true;
346 }
347
348
349 bool AbstractTreeModel::isFiltering() const {
350 return m_filterRunning;
351 }
352
353
354 bool AbstractTreeModel::isRebuilding() const {
355 return m_rebuildRunning;
356 }
357
358
359 void AbstractTreeModel::setFilter(FilterWidget *fw) {
360 m_guisFilterWidget = fw;
361 if (fw) {
362 connect(m_guisFilterWidget, SIGNAL(filterChanged()),
363 this, SLOT(applyFilter()));
364 applyFilter();
365 }
366 }
367
368
369 void AbstractTreeModel::clear() {
370
371 delete rootItem;
372 rootItem = NULL;
373 rootItem = new RootItem;
374 }
375
376
377 ControlNet *AbstractTreeModel::getControlNetwork() const {
378 return m_cNet;
379 }
380
381
382 QFutureWatcher< QAtomicPointer< RootItem > > *
383 AbstractTreeModel::getRebuildWatcher() const {
384 return m_rebuildWatcher;
385 }
386
387
388 RootItem *AbstractTreeModel::getRootItem() const {
389 return rootItem;
390 }
391
392
393 TreeView *AbstractTreeModel::getView() const {
394 return m_view;
395 }
396
397
398 void AbstractTreeModel::stopWorking() {
399 m_filterWatcher->cancel();
400 m_filterWatcher->waitForFinished();
401 m_rebuildWatcher->cancel();
402 m_rebuildWatcher->waitForFinished();
403 }
404
405
407 QSize AbstractTreeModel::getVisibleSize(int indentation) const {
408 QSize size;
409
410 if (!isFiltering()) {
411 int visibleRowCount = 0;
412 int maxWidth = 0;
413
414 if (rootItem && rootItem->getFirstVisibleChild()) {
415 AbstractTreeItem *current = rootItem->getFirstVisibleChild();
416
417 while (current != NULL) {
418 int depth = current->getDepth();
419
420 visibleRowCount++;
421 maxWidth = qMax(maxWidth,
422 current->getDataWidth() + indentation * depth);
423 current = nextItem(current, AllItems, false);
424 }
425 }
426
427 size = QSize(maxWidth, visibleRowCount);
428 }
429
430 return size;
431 }
432
433
434 void AbstractTreeModel::applyFilter() {
435 // If m_filterAgain is true, then this method will be recalled later
436 // with m_filterAgain = false.
437 if (!m_frozen && !m_filterAgain && m_guisFilterWidget &&
438 m_rebuildWatcher->isFinished()) {
439 emit cancelSort();
440 QFuture< QAtomicPointer< AbstractTreeItem> > futureRoot;
441
442 if (m_filterRunning) {
443 m_filterAgain = true;
444 futureRoot = m_filterWatcher->future();
445 futureRoot.cancel();
446 }
447 else {
448 // filterCounts are unknown and invalid and this fact is shared to
449 // users of this class by emitting invalid (negative) information.
450 emit filterCountsChanged(-1, getTopLevelItemCount());
451
452 // update our local copy of the gui widget
453 if (m_localFilterWidgetCopy) {
454 delete m_localFilterWidgetCopy;
455 m_localFilterWidgetCopy = NULL;
456 }
457
458 m_localFilterWidgetCopy = new FilterWidget(*m_guisFilterWidget);
459
460 // using the local copy (NOT the GUI's FilterWidget!!!) apply then
461 // the filter using qtconcurrent's filteredReduced. ApplyFilterDone()
462 // will get called when the filtering is finished.
463 m_filterRunning = true;
464 rootItem->setLastVisibleFilteredItem(NULL);
465 futureRoot = QtConcurrent::filteredReduced(rootItem->getChildren(),
466 FilterFunctor(m_localFilterWidgetCopy),
467 &FilterFunctor::updateTopLevelLinks,
468 QtConcurrent::OrderedReduce | QtConcurrent::SequentialReduce);
469
470 m_filterWatcher->setFuture(futureRoot);
471 }
472 }
473 }
474
475
476 void AbstractTreeModel::setGlobalSelection(bool selected,
477 InterestingItemsFlag flags) {
478 selectItems(rootItem, selected, flags);
479 }
480
481
482 void AbstractTreeModel::selectItems(
483 AbstractTreeItem *item, bool selected, InterestingItemsFlag flags) {
484 if (item && itemIsInteresting(item, flags)) {
485 item->setSelected(selected);
486 }
487
488 if (item->childCount()) {
489 foreach (AbstractTreeItem * childItem, item->getChildren()) {
490 selectItems(childItem, selected, flags);
491 }
492 }
493 }
494
495
496 bool AbstractTreeModel::itemIsInteresting(AbstractTreeItem *item,
497 InterestingItemsFlag flags) {
498 AbstractTreeItem::InternalPointerType pointerType =
499 item->getPointerType();
500
501 if ((pointerType == AbstractTreeItem::Point && flags.testFlag(PointItems)) ||
502 (pointerType == AbstractTreeItem::Measure && flags.testFlag(MeasureItems)) ||
503 (pointerType == AbstractTreeItem::ImageAndNet && flags.testFlag(ImageItems))) {
504 return true;
505 }
506 else {
507 return false;
508 }
509 }
510
511
512 int AbstractTreeModel::getItemCount(AbstractTreeItem *item,
513 InterestingItemsFlag flags) const {
514 int count = 0;
515
516 if (item && itemIsInteresting(item, flags)) {
517 count++;
518 }
519
520 if (item->childCount()) {
521 foreach (AbstractTreeItem * childItem, item->getChildren()) {
522 count += getItemCount(childItem, flags);
523 }
524 }
525
526 return count;
527 }
528
529
530 AbstractTreeItem *AbstractTreeModel::nextItem(AbstractTreeItem *current,
531 InterestingItemsFlag flags, bool ignoreExpansion) const {
532 if (current) {
533 do {
534 if ((ignoreExpansion || current->isExpanded()) &&
535 current->getFirstVisibleChild())
536 current = current->getFirstVisibleChild();
537 else if (current->getNextVisiblePeer())
538 current = current->getNextVisiblePeer();
539 else if (current->parent())
540 current = current->parent()->getNextVisiblePeer();
541 else
542 current = NULL;
543 }
544 while (current && !itemIsInteresting(current, flags));
545 }
546
547 return current;
548 }
549
550
551 void AbstractTreeModel::applyFilterDone() {
552 m_filterRunning = false;
553
554 if (m_filterAgain) {
555 m_filterAgain = false;
556 applyFilter();
557 }
558 else {
559 emit modelModified();
560 emit filterCountsChanged(getVisibleTopLevelItemCount(),
561 getTopLevelItemCount());
562 }
563 }
564
565
566 void AbstractTreeModel::rebuildItemsDone() {
567 clear();
568
569 QAtomicPointer< RootItem > newRootPtr = m_rebuildWatcher->future();
570 RootItem *newRoot = newRootPtr.loadAcquire();
571
572 if (newRoot && newRoot->childCount()) {
573 delete rootItem;
574 rootItem = NULL;
575 rootItem = newRoot;
576 }
577 // Not safe - if newRoot == NULL, the condition fails - delete (NULL) is undefined
578 /*else {
579 delete newRoot;
580 newRoot = NULL;
581 }*/
582
583 applyFilter();
584
585 setRebuilding(false);
586 emit modelModified();
587 }
588
589
590 AbstractTreeModel::FilterFunctor::FilterFunctor(
591 FilterWidget *fw) : m_filter(fw) {
592 }
593
594
595 AbstractTreeModel::FilterFunctor::FilterFunctor(FilterFunctor const &other) {
596 m_filter = other.m_filter;
597 }
598
599
600 AbstractTreeModel::FilterFunctor::~FilterFunctor() {
601 }
602
603
604 bool AbstractTreeModel::FilterFunctor::operator()(
605 AbstractTreeItem *const &item) const {
606 filterWorker(item);
607 return true;
608 }
609
610
611 AbstractTreeModel::FilterFunctor &
612 AbstractTreeModel::FilterFunctor::operator=(FilterFunctor const &other) {
613 if (this != &other)
614 m_filter = other.m_filter;
615
616 return *this;
617 }
618
619
620 void AbstractTreeModel::FilterFunctor::filterWorker(
621 AbstractTreeItem *item) const {
622 switch (item->getPointerType()) {
623
624 case AbstractTreeItem::Point:
625 item->setVisible((!m_filter || m_filter->evaluate(
626 (ControlPoint *) item->getPointer())) ? true : false);
627 break;
628
629 case AbstractTreeItem::Measure:
630 item->setVisible((!m_filter || m_filter->evaluate(
631 (ControlMeasure *) item->getPointer())) ? true : false);
632 break;
633
634 case AbstractTreeItem::ImageAndNet:
635 item->setVisible((!m_filter || m_filter->evaluate(
636 (QPair<QString, ControlNet *> *) item->getPointer())) ? true : false);
637 break;
638
639 case AbstractTreeItem::None:
640 item->setVisible(true);
641 break;
642 }
643
644 // Destroy peer link because it will need to be recreated later.
645 if (item->getFirstVisibleChild())
646 item->setFirstVisibleChild(NULL);
647
648 if (item->getLastVisibleChild())
649 item->setLastVisibleChild(NULL);
650
651 item->setNextVisiblePeer(NULL);
652
653 // Update each tree item's visible flag based on whether or not it is
654 // accepted by the filter.
655 if (item->childCount()) {
656 for (int i = 0; i < item->childCount(); i++) {
657 AbstractTreeItem *child = item->childAt(i);
658 filterWorker(child);
659
660 if (child->isVisible()) {
661 if (!item->getFirstVisibleChild()) {
662 item->setFirstVisibleChild(child);
663 item->setLastVisibleChild(child);
664 }
665 else {
666 item->getLastVisibleChild()->setNextVisiblePeer(child);
667 item->setLastVisibleChild(child);
668 }
669 }
670 }
671 }
672 }
673
674
675 void AbstractTreeModel::FilterFunctor::updateTopLevelLinks(
676 QAtomicPointer< AbstractTreeItem > & root,
677 AbstractTreeItem *const &item) {
678 // We will update the root if it is NULL
679 if ( root.testAndSetOrdered(NULL, item->parent()) ) {
680 AbstractTreeItem *loadedRoot = root.loadAcquire();
681 loadedRoot->setFirstVisibleChild(NULL);
682 loadedRoot->setLastVisibleChild(NULL);
683 loadedRoot->setLastVisibleFilteredItem(NULL);
684 }
685
686 // Let's get that root ptr again
687 AbstractTreeItem *loadedRoot = root.loadAcquire();
688 if (item->isVisible()) {
689 if (!loadedRoot->getFirstVisibleChild()) {
690 loadedRoot->setFirstVisibleChild(item);
691 loadedRoot->setLastVisibleChild(item);
692 }
693 else {
694 loadedRoot->getLastVisibleChild()->setNextVisiblePeer(item);
695 loadedRoot->setLastVisibleChild(item);
696 }
697
698 loadedRoot->setLastVisibleFilteredItem(item);
699 }
700 }
701}
Base class for an item in the tree.
This is free and unencumbered software released into the public domain.
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16