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

U.S. Department of the Interior | U.S. Geological Survey
ISIS | Privacy & Disclaimers | Astrogeology Research Program
To contact us, please post comments and questions on the USGS Astrogeology Discussion Board
To report a bug, or suggest a feature go to: ISIS Github
File Modified: 07/13/2023 15:16:05