Isis 3.0 Programmer Reference
Back | Home
AbstractTreeModel.cpp
1 #include "IsisDebug.h"
2 
3 #include "AbstractTreeModel.h"
4 
5 #include <algorithm>
6 #include <iostream>
7 
8 #include <QFutureWatcher>
9 #include <QList>
10 #include <QModelIndex>
11 #include <QMutex>
12 #include <QPair>
13 #include <QStack>
14 #include <QString>
15 #include <QtConcurrentFilter>
16 
17 #include <QtConcurrentMap>
18 
19 #include <QFlags>
20 #include <QtGlobal>
21 #include <QVariant>
22 
23 #include "BusyLeafItem.h"
24 #include "TreeView.h"
25 #include "ControlMeasure.h"
26 #include "ControlNet.h"
27 #include "ControlPoint.h"
28 #include "IException.h"
29 
30 #include "AbstractTreeItem.h"
31 #include "FilterWidget.h"
32 #include "RootItem.h"
33 
34 
35 namespace Isis {
36  namespace CnetViz {
37  AbstractTreeModel::AbstractTreeModel(ControlNet *controlNet, TreeView *v,
38  QObject *parent) : QObject(parent), m_view(v), m_cNet(controlNet) {
39  ASSERT(m_cNet);
40 
41  m_filterWatcher = NULL;
42  m_rebuildWatcher = NULL;
43  m_busyItem = NULL;
44  rootItem = NULL;
45  m_expandedState = NULL;
46  m_selectedState = NULL;
47  m_guisFilterWidget = NULL;
48  m_localFilterWidgetCopy = NULL;
49  m_mutex = NULL;
50 
51  m_busyItem = new BusyLeafItem(NULL);
52  rootItem = new RootItem;
53  m_expandedState = new QList< QPair< QString, QString > >;
54  m_selectedState = new QList< QPair< QString, QString > >;
55  m_mutex = new QMutex;
56 
58  m_rebuildWatcher = new QFutureWatcher< QAtomicPointer< RootItem > >;
59 
60  connect(m_filterWatcher, SIGNAL(finished()), this, SLOT(applyFilterDone()));
61  connect(m_rebuildWatcher, SIGNAL(finished()), this, SLOT(rebuildItemsDone()));
62 
63  connect(m_filterWatcher, SIGNAL(progressValueChanged(int)),
64  this, SIGNAL(filterProgressChanged(int)));
65  connect(m_filterWatcher, SIGNAL(progressRangeChanged(int, int)),
66  this, SIGNAL(filterProgressRangeChanged(int, int)));
67  connect(m_rebuildWatcher, SIGNAL(progressValueChanged(int)),
68  this, SIGNAL(rebuildProgressChanged(int)));
69  connect(m_rebuildWatcher, SIGNAL(progressRangeChanged(int, int)),
70  this, SIGNAL(rebuildProgressRangeChanged(int, int)));
71 
72  m_drivable = false;
73  m_filterAgain = false;
74  m_filterRunning = false;
75  m_rebuildRunning = false;
76  m_frozen = false;
77  m_rebuildPending = false;
78  }
79 
80 
81  AbstractTreeModel::~AbstractTreeModel() {
82  delete m_filterWatcher;
83  m_filterWatcher = NULL;
84 
85  delete m_rebuildWatcher;
86  m_rebuildWatcher = NULL;
87 
88  delete m_busyItem;
89  m_busyItem = NULL;
90 
91  delete rootItem;
92  rootItem = NULL;
93 
94  delete m_expandedState;
95  m_expandedState = NULL;
96 
97  delete m_selectedState;
98  m_selectedState = NULL;
99 
100  delete m_mutex;
101  m_mutex = NULL;
102 
103  delete m_localFilterWidgetCopy;
104  m_localFilterWidgetCopy = NULL;
105 
106  m_guisFilterWidget = NULL;
107  m_cNet = NULL;
108  m_view = NULL;
109  }
110 
111 
112  // If a negative end is passed in, grabs all items from start to the end of
113  // the tree. No busy leaf items will be inserted.
114  QList< AbstractTreeItem * > AbstractTreeModel::getItems(int start, int end,
115  InterestingItemsFlag flags, bool ignoreExpansion) {
116  QList< AbstractTreeItem * > foundItems;
117  int rowCount = end - start;
118  const AbstractTreeItem *lastVisibleFilteredItem =
119  rootItem->getLastVisibleFilteredItem();
120 
121  bool grabToEnd = (start >= 0 && end < 0);
122 
123  if (lastVisibleFilteredItem && (rowCount > 0 || grabToEnd) &&
124  rootItem->childCount()) {
125  int row = 0;
126  AbstractTreeItem *currentItem = rootItem->getFirstVisibleChild();
127 
128  if (currentItem && !itemIsInteresting(currentItem, flags)) {
129  currentItem = nextItem(currentItem, flags, ignoreExpansion);
130  }
131 
132  bool listStillValid = true;
133 
134  while (row < start && listStillValid && currentItem) {
135  row++;
136  listStillValid = (currentItem != lastVisibleFilteredItem ||
137  currentItem == currentItem->parent()->getLastVisibleChild());
138 
139  if (listStillValid)
140  currentItem = nextItem(currentItem, flags, ignoreExpansion);
141  }
142 
143  while ((row < end || grabToEnd) && listStillValid && currentItem) {
144  ASSERT(currentItem);
145  foundItems.append(currentItem);
146  listStillValid = (currentItem != lastVisibleFilteredItem ||
147  currentItem == currentItem->parent()->getLastVisibleChild());
148  row++;
149 
150  if (listStillValid)
151  currentItem = nextItem(currentItem, flags, ignoreExpansion);
152  }
153 
154  // Fill in the rest with busy items if needed. If we are grabbing all
155  // items to the end of the visible tree, we do not want any busy items
156  // added to our found items list.
157  while (!grabToEnd && isFiltering() && foundItems.size() < rowCount) {
158  foundItems.append(m_busyItem);
159  }
160  }
161 
162  return foundItems;
163  }
164 
165 
166  QList< AbstractTreeItem * > AbstractTreeModel::getItems(
167  AbstractTreeItem *item1, AbstractTreeItem *item2,
168  InterestingItemsFlag flags, bool ignoreExpansion) {
169  QList< AbstractTreeItem * > foundItems;
170 
171  if (rootItem->childCount()) {
172  AbstractTreeItem *start = NULL;
173 
174  AbstractTreeItem *curItem = rootItem->getFirstVisibleChild();
175 
176  while (!start && curItem) {
177  if (curItem == item1)
178  start = item1;
179  else if (curItem == item2)
180  start = item2;
181 
182  if (!start)
183  curItem = nextItem(curItem, flags, ignoreExpansion);
184  }
185 
186  if (!start) {
187  QString msg = "The first item passed to getItems(AbstractTreeItem*, "
188  "AbstractTreeItem*) is not visible in this model's tree";
189  throw IException(IException::Programmer, msg, _FILEINFO_);
190  }
191 
192  AbstractTreeItem *end = item2;
193 
194  // Sometimes we need to build the list forwards and sometimes backwards.
195  // This is accomplished by using either append or prepend. We abstract
196  // away which of these we should use (why should we care) by using the
197  // variable "someKindaPend" to store the appropriate method.
198  void (QList<AbstractTreeItem *>::*someKindaPend)(
199  AbstractTreeItem * const &);
200 
201  someKindaPend = &QList<AbstractTreeItem *>::append;
202  if (start == item2) {
203  end = item1;
204  someKindaPend = &QList<AbstractTreeItem *>::prepend;
205  }
206 
207  while (curItem && curItem != end) {
208  (foundItems.*someKindaPend)(curItem);
209  curItem = nextItem(curItem, flags, ignoreExpansion);
210  }
211 
212  if (!curItem) {
213  QString msg = "The second item passed to getItems(AbstractTreeItem*, "
214  "AbstractTreeItem*) is not visible in this model's tree";
215  throw IException(IException::Programmer, msg, _FILEINFO_);
216  }
217 
218  (foundItems.*someKindaPend)(end);
219  }
220 
221  return foundItems;
222  }
223 
224 
225  QList< AbstractTreeItem * > AbstractTreeModel::getSelectedItems(
226  InterestingItemsFlag flags, bool ignoreExpansion) {
227  QList< AbstractTreeItem * > selectedItems;
228 
229  ASSERT(rootItem);
230 
231  if (!isFiltering()) {
232  AbstractTreeItem *currentItem = rootItem->getFirstVisibleChild();
233 
234  if (currentItem && !itemIsInteresting(currentItem, flags))
235  currentItem = nextItem(currentItem, flags, ignoreExpansion);
236 
237  while (currentItem) {
238  if (currentItem->isSelected())
239  selectedItems.append(currentItem);
240 
241  currentItem = nextItem(currentItem, flags, ignoreExpansion);
242  }
243  }
244 
245  return selectedItems;
246  }
247 
248 
249  QMutex *AbstractTreeModel::getMutex() const {
250  return m_mutex;
251  }
252 
253 
254  int AbstractTreeModel::getItemCount(InterestingItemsFlag flags) const {
255  return getItemCount(rootItem, flags);
256  }
257 
258 
259  int AbstractTreeModel::getTopLevelItemCount() const {
260  return rootItem->childCount();
261  }
262 
263  int AbstractTreeModel::getVisibleItemCount(InterestingItemsFlag flags,
264  bool ignoreExpansion) const {
265  AbstractTreeItem *currentItem = rootItem->getFirstVisibleChild();
266  int count = -1;
267 
268  if (!isFiltering()) {
269  count = 0;
270 
271  while (currentItem) {
272  if (itemIsInteresting(currentItem, flags)) {
273  count++;
274  }
275 
276  currentItem = nextItem(currentItem, flags, ignoreExpansion);
277  }
278  }
279 
280  return count;
281  }
282 
283 
284  int AbstractTreeModel::getVisibleTopLevelItemCount() const {
285  AbstractTreeItem *currentItem = rootItem->getFirstVisibleChild();
286  int count = -1;
287 
288  if (!isFiltering()) {
289  count = 0;
290 
291  while (currentItem) {
292  count++;
293  currentItem = currentItem->getNextVisiblePeer();
294  }
295  }
296 
297  return count;
298  }
299 
300 
301  int AbstractTreeModel::indexOfVisibleItem(AbstractTreeItem const *item,
302  InterestingItemsFlag flags, bool ignoreExpansion) const {
303  AbstractTreeItem *currentItem = rootItem->getFirstVisibleChild();
304  int index = -1;
305 
306  if (!isFiltering()) {
307  while (currentItem && currentItem != item) {
308  if (itemIsInteresting(currentItem, flags))
309  index++;
310 
311  currentItem = nextItem(currentItem, flags, ignoreExpansion);
312  }
313 
314  index++;
315 
316  if (!currentItem)
317  index = -1;
318  }
319 
320  return index;
321  }
322 
323 
324  void AbstractTreeModel::setFrozen(bool newFrozenState) {
325  m_frozen = newFrozenState;
326  if (!m_frozen) {
327  if (m_rebuildPending) {
328  rebuildItems();
329  m_rebuildPending = false;
330  }
331  else {
332  applyFilter();
333  }
334  }
335  }
336 
337 
338  bool AbstractTreeModel::isFrozen() const {
339  return m_frozen;
340  }
341 
342 
343  void AbstractTreeModel::queueRebuild() {
344  m_rebuildPending = true;
345  }
346 
347 
348  bool AbstractTreeModel::isFiltering() const {
349  return m_filterRunning;
350  }
351 
352 
353  bool AbstractTreeModel::isRebuilding() const {
354  return m_rebuildRunning;
355  }
356 
357 
358  void AbstractTreeModel::setFilter(FilterWidget *fw) {
359  m_guisFilterWidget = fw;
360  if (fw) {
361  connect(m_guisFilterWidget, SIGNAL(filterChanged()),
362  this, SLOT(applyFilter()));
363  applyFilter();
364  }
365  }
366 
367 
368  void AbstractTreeModel::clear() {
369  ASSERT(rootItem);
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 
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 &&
502  flags.testFlag(PointItems)) ||
503  (pointerType == AbstractTreeItem::Measure &&
504  flags.testFlag(MeasureItems)) ||
505  (pointerType == AbstractTreeItem::CubeGraphNode &&
506  flags.testFlag(SerialItems))) {
507  return true;
508  }
509  else {
510  return false;
511  }
512  }
513 
514 
515  int AbstractTreeModel::getItemCount(AbstractTreeItem *item,
516  InterestingItemsFlag flags) const {
517  int count = 0;
518 
519  if (item && itemIsInteresting(item, flags)) {
520  count++;
521  }
522 
523  if (item->childCount()) {
524  foreach (AbstractTreeItem * childItem, item->getChildren()) {
525  count += getItemCount(childItem, flags);
526  }
527  }
528 
529  return count;
530  }
531 
532 
533  AbstractTreeItem *AbstractTreeModel::nextItem(AbstractTreeItem *current,
534  InterestingItemsFlag flags, bool ignoreExpansion) const {
535  if (current) {
536  do {
537  if ((ignoreExpansion || current->isExpanded()) &&
538  current->getFirstVisibleChild())
539  current = current->getFirstVisibleChild();
540  else if (current->getNextVisiblePeer())
541  current = current->getNextVisiblePeer();
542  else if (current->parent())
543  current = current->parent()->getNextVisiblePeer();
544  else
545  current = NULL;
546  }
547  while (current && !itemIsInteresting(current, flags));
548  }
549 
550  return current;
551  }
552 
553 
554  void AbstractTreeModel::applyFilterDone() {
555  m_filterRunning = false;
556 
557  if (m_filterAgain) {
558  m_filterAgain = false;
559  applyFilter();
560  }
561  else {
562  emit modelModified();
563  emit filterCountsChanged(getVisibleTopLevelItemCount(),
564  getTopLevelItemCount());
565  }
566  }
567 
568 
569  void AbstractTreeModel::rebuildItemsDone() {
570  clear();
571 
572  QAtomicPointer< RootItem > newRootPtr = m_rebuildWatcher->future();
573  RootItem *newRoot = newRootPtr.loadAcquire();
574 
575  if (newRoot && newRoot->childCount()) {
576  ASSERT(rootItem);
577  delete rootItem;
578  rootItem = NULL;
579  rootItem = newRoot;
580  }
581  // Not safe - if newRoot == NULL, the condition fails - delete (NULL) is undefined
582  /*else {
583  delete newRoot;
584  newRoot = NULL;
585  }*/
586 
587  applyFilter();
588 
589  setRebuilding(false);
590  emit modelModified();
591  }
592 
593 
594  AbstractTreeModel::FilterFunctor::FilterFunctor(
595  FilterWidget *fw) : m_filter(fw) {
596  }
597 
598 
599  AbstractTreeModel::FilterFunctor::FilterFunctor(FilterFunctor const &other) {
600  m_filter = other.m_filter;
601  }
602 
603 
604  AbstractTreeModel::FilterFunctor::~FilterFunctor() {
605  }
606 
607 
608  bool AbstractTreeModel::FilterFunctor::operator()(
609  AbstractTreeItem *const &item) const {
610  filterWorker(item);
611  return true;
612  }
613 
614 
615  AbstractTreeModel::FilterFunctor &
616  AbstractTreeModel::FilterFunctor::operator=(FilterFunctor const &other) {
617  if (this != &other)
618  m_filter = other.m_filter;
619 
620  return *this;
621  }
622 
623 
624  void AbstractTreeModel::FilterFunctor::filterWorker(
625  AbstractTreeItem *item) const {
626  switch (item->getPointerType()) {
627 
628  case AbstractTreeItem::Point:
629  item->setVisible((!m_filter || m_filter->evaluate(
630  (ControlPoint *) item->getPointer())) ? true : false);
631  break;
632 
633  case AbstractTreeItem::Measure:
634  item->setVisible((!m_filter || m_filter->evaluate(
635  (ControlMeasure *) item->getPointer())) ? true : false);
636  break;
637 
638  case AbstractTreeItem::CubeGraphNode:
639  item->setVisible((!m_filter || m_filter->evaluate(
640  (ControlCubeGraphNode *) item->getPointer())) ? true : false);
641  break;
642 
643  case AbstractTreeItem::None:
644  item->setVisible(true);
645  break;
646  }
647 
648  // Destroy peer link because it will need to be recreated later.
649  if (item->getFirstVisibleChild())
650  item->setFirstVisibleChild(NULL);
651 
652  if (item->getLastVisibleChild())
653  item->setLastVisibleChild(NULL);
654 
655  item->setNextVisiblePeer(NULL);
656 
657  // Update each tree item's visible flag based on whether or not it is
658  // accepted by the filter.
659  if (item->childCount()) {
660  for (int i = 0; i < item->childCount(); i++) {
661  AbstractTreeItem *child = item->childAt(i);
662  filterWorker(child);
663 
664  if (child->isVisible()) {
665  if (!item->getFirstVisibleChild()) {
666  item->setFirstVisibleChild(child);
667  item->setLastVisibleChild(child);
668  }
669  else {
670  item->getLastVisibleChild()->setNextVisiblePeer(child);
671  item->setLastVisibleChild(child);
672  }
673  }
674  }
675  }
676  }
677 
678 
679  void AbstractTreeModel::FilterFunctor::updateTopLevelLinks(
681  AbstractTreeItem *const &item) {
682  // We will update the root if it is NULL
683  if ( root.testAndSetOrdered(NULL, item->parent()) ) {
684  AbstractTreeItem *loadedRoot = root.loadAcquire();
685  loadedRoot->setFirstVisibleChild(NULL);
686  loadedRoot->setLastVisibleChild(NULL);
687  loadedRoot->setLastVisibleFilteredItem(NULL);
688  }
689 
690  // Let's get that root ptr again
691  AbstractTreeItem *loadedRoot = root.loadAcquire();
692  if (item->isVisible()) {
693  if (!loadedRoot->getFirstVisibleChild()) {
694  loadedRoot->setFirstVisibleChild(item);
695  loadedRoot->setLastVisibleChild(item);
696  }
697  else {
698  loadedRoot->getLastVisibleChild()->setNextVisiblePeer(item);
699  loadedRoot->setLastVisibleChild(item);
700  }
701 
702  loadedRoot->setLastVisibleFilteredItem(item);
703  }
704  }
705  }
706 }
707 
This error is for when a programmer made an API call that was illegal.
Definition: IException.h:154
Base class for an item in the tree.
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:38
void filterCountsChanged(int visibleTopLevelItemCount, int topLevelItemCount)
This signal is emitted after filtering to provide the number of visible top-level items remaining aft...
QSize getVisibleSize(int indentation) const
indentation is in pixels

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 ISIS Support Center
File Modified: 07/12/2023 23:13:52