Isis 3 Programmer Reference
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  AbstractTreeModel::AbstractTreeModel(ControlNet *controlNet, TreeView *v,
37  QObject *parent) : QObject(parent), m_view(v), m_cNet(controlNet) {
38  ASSERT(m_cNet);
39 
40  m_filterWatcher = NULL;
41  m_rebuildWatcher = NULL;
42  m_busyItem = NULL;
43  rootItem = NULL;
44  m_expandedState = NULL;
45  m_selectedState = NULL;
46  m_guisFilterWidget = NULL;
47  m_localFilterWidgetCopy = NULL;
48  m_mutex = NULL;
49 
50  m_busyItem = new BusyLeafItem(NULL);
51  rootItem = new RootItem;
52  m_expandedState = new QList< QPair< QString, QString > >;
53  m_selectedState = new QList< QPair< QString, QString > >;
54  m_mutex = new QMutex;
55 
57  m_rebuildWatcher = new QFutureWatcher< QAtomicPointer< RootItem > >;
58 
59  connect(m_filterWatcher, SIGNAL(finished()), this, SLOT(applyFilterDone()));
60  connect(m_rebuildWatcher, SIGNAL(finished()), this, SLOT(rebuildItemsDone()));
61 
62  connect(m_filterWatcher, SIGNAL(progressValueChanged(int)),
63  this, SIGNAL(filterProgressChanged(int)));
64  connect(m_filterWatcher, SIGNAL(progressRangeChanged(int, int)),
65  this, SIGNAL(filterProgressRangeChanged(int, int)));
66  connect(m_rebuildWatcher, SIGNAL(progressValueChanged(int)),
67  this, SIGNAL(rebuildProgressChanged(int)));
68  connect(m_rebuildWatcher, SIGNAL(progressRangeChanged(int, int)),
69  this, SIGNAL(rebuildProgressRangeChanged(int, int)));
70 
71  m_drivable = false;
72  m_filterAgain = false;
73  m_filterRunning = false;
74  m_rebuildRunning = false;
75  m_frozen = false;
76  m_rebuildPending = false;
77  }
78 
79 
80  AbstractTreeModel::~AbstractTreeModel() {
81  delete m_filterWatcher;
82  m_filterWatcher = NULL;
83 
84  delete m_rebuildWatcher;
85  m_rebuildWatcher = NULL;
86 
87  delete m_busyItem;
88  m_busyItem = NULL;
89 
90  delete rootItem;
91  rootItem = NULL;
92 
93  delete m_expandedState;
94  m_expandedState = NULL;
95 
96  delete m_selectedState;
97  m_selectedState = NULL;
98 
99  delete m_mutex;
100  m_mutex = NULL;
101 
102  delete m_localFilterWidgetCopy;
103  m_localFilterWidgetCopy = NULL;
104 
105  m_guisFilterWidget = NULL;
106  m_cNet = NULL;
107  m_view = NULL;
108  }
109 
110 
111  // If a negative end is passed in, grabs all items from start to the end of
112  // the tree. No busy leaf items will be inserted.
113  QList< AbstractTreeItem * > AbstractTreeModel::getItems(int start, int end,
114  InterestingItemsFlag flags, bool ignoreExpansion) {
115  QList< AbstractTreeItem * > foundItems;
116  int rowCount = end - start;
117  const AbstractTreeItem *lastVisibleFilteredItem =
118  rootItem->getLastVisibleFilteredItem();
119 
120  bool grabToEnd = (start >= 0 && end < 0);
121 
122  if (lastVisibleFilteredItem && (rowCount > 0 || grabToEnd) &&
123  rootItem->childCount()) {
124  int row = 0;
125  AbstractTreeItem *currentItem = rootItem->getFirstVisibleChild();
126 
127  if (currentItem && !itemIsInteresting(currentItem, flags)) {
128  currentItem = nextItem(currentItem, flags, ignoreExpansion);
129  }
130 
131  bool listStillValid = true;
132 
133  while (row < start && listStillValid && currentItem) {
134  row++;
135  listStillValid = (currentItem != lastVisibleFilteredItem ||
136  currentItem == currentItem->parent()->getLastVisibleChild());
137 
138  if (listStillValid)
139  currentItem = nextItem(currentItem, flags, ignoreExpansion);
140  }
141 
142  while ((row < end || grabToEnd) && listStillValid && currentItem) {
143  ASSERT(currentItem);
144  foundItems.append(currentItem);
145  listStillValid = (currentItem != lastVisibleFilteredItem ||
146  currentItem == currentItem->parent()->getLastVisibleChild());
147  row++;
148 
149  if (listStillValid)
150  currentItem = nextItem(currentItem, flags, ignoreExpansion);
151  }
152 
153  // Fill in the rest with busy items if needed. If we are grabbing all
154  // items to the end of the visible tree, we do not want any busy items
155  // added to our found items list.
156  while (!grabToEnd && isFiltering() && foundItems.size() < rowCount) {
157  foundItems.append(m_busyItem);
158  }
159  }
160 
161  return foundItems;
162  }
163 
164 
165  QList< AbstractTreeItem * > AbstractTreeModel::getItems(
166  AbstractTreeItem *item1, AbstractTreeItem *item2,
167  InterestingItemsFlag flags, bool ignoreExpansion) {
168  QList< AbstractTreeItem * > foundItems;
169 
170  if (rootItem->childCount()) {
171  AbstractTreeItem *start = NULL;
172 
173  AbstractTreeItem *curItem = rootItem->getFirstVisibleChild();
174 
175  while (!start && curItem) {
176  if (curItem == item1)
177  start = item1;
178  else if (curItem == item2)
179  start = item2;
180 
181  if (!start)
182  curItem = nextItem(curItem, flags, ignoreExpansion);
183  }
184 
185  if (!start) {
186  QString msg = "The first item passed to getItems(AbstractTreeItem*, "
187  "AbstractTreeItem*) is not visible in this model's tree";
188  throw IException(IException::Programmer, msg, _FILEINFO_);
189  }
190 
191  AbstractTreeItem *end = item2;
192 
193  // Sometimes we need to build the list forwards and sometimes backwards.
194  // This is accomplished by using either append or prepend. We abstract
195  // away which of these we should use (why should we care) by using the
196  // variable "someKindaPend" to store the appropriate method.
197  void (QList<AbstractTreeItem *>::*someKindaPend)(
198  AbstractTreeItem * const &);
199 
200  someKindaPend = &QList<AbstractTreeItem *>::append;
201  if (start == item2) {
202  end = item1;
203  someKindaPend = &QList<AbstractTreeItem *>::prepend;
204  }
205 
206  while (curItem && curItem != end) {
207  (foundItems.*someKindaPend)(curItem);
208  curItem = nextItem(curItem, flags, ignoreExpansion);
209  }
210 
211  if (!curItem) {
212  QString msg = "The second item passed to getItems(AbstractTreeItem*, "
213  "AbstractTreeItem*) is not visible in this model's tree";
214  throw IException(IException::Programmer, msg, _FILEINFO_);
215  }
216 
217  (foundItems.*someKindaPend)(end);
218  }
219 
220  return foundItems;
221  }
222 
223 
224  QList< AbstractTreeItem * > AbstractTreeModel::getSelectedItems(
225  InterestingItemsFlag flags, bool ignoreExpansion) {
226  QList< AbstractTreeItem * > selectedItems;
227 
228  ASSERT(rootItem);
229 
230  if (!isFiltering()) {
231  AbstractTreeItem *currentItem = rootItem->getFirstVisibleChild();
232 
233  if (currentItem && !itemIsInteresting(currentItem, flags))
234  currentItem = nextItem(currentItem, flags, ignoreExpansion);
235 
236  while (currentItem) {
237  if (currentItem->isSelected())
238  selectedItems.append(currentItem);
239 
240  currentItem = nextItem(currentItem, flags, ignoreExpansion);
241  }
242  }
243 
244  return selectedItems;
245  }
246 
247 
248  QMutex *AbstractTreeModel::getMutex() const {
249  return m_mutex;
250  }
251 
252 
253  int AbstractTreeModel::getItemCount(InterestingItemsFlag flags) const {
254  return getItemCount(rootItem, flags);
255  }
256 
257 
258  int AbstractTreeModel::getTopLevelItemCount() const {
259  return rootItem->childCount();
260  }
261 
262  int AbstractTreeModel::getVisibleItemCount(InterestingItemsFlag flags,
263  bool ignoreExpansion) const {
264  AbstractTreeItem *currentItem = rootItem->getFirstVisibleChild();
265  int count = -1;
266 
267  if (!isFiltering()) {
268  count = 0;
269 
270  while (currentItem) {
271  if (itemIsInteresting(currentItem, flags)) {
272  count++;
273  }
274 
275  currentItem = nextItem(currentItem, flags, ignoreExpansion);
276  }
277  }
278 
279  return count;
280  }
281 
282 
283  int AbstractTreeModel::getVisibleTopLevelItemCount() const {
284  AbstractTreeItem *currentItem = rootItem->getFirstVisibleChild();
285  int count = -1;
286 
287  if (!isFiltering()) {
288  count = 0;
289 
290  while (currentItem) {
291  count++;
292  currentItem = currentItem->getNextVisiblePeer();
293  }
294  }
295 
296  return count;
297  }
298 
299 
300  int AbstractTreeModel::indexOfVisibleItem(AbstractTreeItem const *item,
301  InterestingItemsFlag flags, bool ignoreExpansion) const {
302  AbstractTreeItem *currentItem = rootItem->getFirstVisibleChild();
303  int index = -1;
304 
305  if (!isFiltering()) {
306  while (currentItem && currentItem != item) {
307  if (itemIsInteresting(currentItem, flags))
308  index++;
309 
310  currentItem = nextItem(currentItem, flags, ignoreExpansion);
311  }
312 
313  index++;
314 
315  if (!currentItem)
316  index = -1;
317  }
318 
319  return index;
320  }
321 
322 
323  void AbstractTreeModel::setFrozen(bool newFrozenState) {
324  m_frozen = newFrozenState;
325  if (!m_frozen) {
326  if (m_rebuildPending) {
327  rebuildItems();
328  m_rebuildPending = false;
329  }
330  else {
331  applyFilter();
332  }
333  }
334  }
335 
336 
337  bool AbstractTreeModel::isFrozen() const {
338  return m_frozen;
339  }
340 
341 
342  void AbstractTreeModel::queueRebuild() {
343  m_rebuildPending = true;
344  }
345 
346 
347  bool AbstractTreeModel::isFiltering() const {
348  return m_filterRunning;
349  }
350 
351 
352  bool AbstractTreeModel::isRebuilding() const {
353  return m_rebuildRunning;
354  }
355 
356 
357  void AbstractTreeModel::setFilter(FilterWidget *fw) {
358  m_guisFilterWidget = fw;
359  if (fw) {
360  connect(m_guisFilterWidget, SIGNAL(filterChanged()),
361  this, SLOT(applyFilter()));
362  applyFilter();
363  }
364  }
365 
366 
367  void AbstractTreeModel::clear() {
368  ASSERT(rootItem);
369 
370  delete rootItem;
371  rootItem = NULL;
372  rootItem = new RootItem;
373  }
374 
375 
376  ControlNet *AbstractTreeModel::getControlNetwork() const {
377  return m_cNet;
378  }
379 
380 
382  AbstractTreeModel::getRebuildWatcher() const {
383  return m_rebuildWatcher;
384  }
385 
386 
387  RootItem *AbstractTreeModel::getRootItem() const {
388  return rootItem;
389  }
390 
391 
392  TreeView *AbstractTreeModel::getView() const {
393  return m_view;
394  }
395 
396 
397  void AbstractTreeModel::stopWorking() {
398  m_filterWatcher->cancel();
399  m_filterWatcher->waitForFinished();
400  m_rebuildWatcher->cancel();
401  m_rebuildWatcher->waitForFinished();
402  }
403 
404 
406  QSize AbstractTreeModel::getVisibleSize(int indentation) const {
407  QSize size;
408 
409  if (!isFiltering()) {
410  int visibleRowCount = 0;
411  int maxWidth = 0;
412 
413  if (rootItem && rootItem->getFirstVisibleChild()) {
414  AbstractTreeItem *current = rootItem->getFirstVisibleChild();
415 
416  while (current != NULL) {
417  int depth = current->getDepth();
418 
419  visibleRowCount++;
420  maxWidth = qMax(maxWidth,
421  current->getDataWidth() + indentation * depth);
422  current = nextItem(current, AllItems, false);
423  }
424  }
425 
426  size = QSize(maxWidth, visibleRowCount);
427  }
428 
429  return size;
430  }
431 
432 
433  void AbstractTreeModel::applyFilter() {
434  // If m_filterAgain is true, then this method will be recalled later
435  // with m_filterAgain = false.
436  if (!m_frozen && !m_filterAgain && m_guisFilterWidget &&
437  m_rebuildWatcher->isFinished()) {
438  emit cancelSort();
439  QFuture< QAtomicPointer< AbstractTreeItem> > futureRoot;
440 
441  if (m_filterRunning) {
442  m_filterAgain = true;
443  futureRoot = m_filterWatcher->future();
444  futureRoot.cancel();
445  }
446  else {
447  // filterCounts are unknown and invalid and this fact is shared to
448  // users of this class by emitting invalid (negative) information.
449  emit filterCountsChanged(-1, getTopLevelItemCount());
450 
451  // update our local copy of the gui widget
452  if (m_localFilterWidgetCopy) {
453  delete m_localFilterWidgetCopy;
454  m_localFilterWidgetCopy = NULL;
455  }
456 
457  m_localFilterWidgetCopy = new FilterWidget(*m_guisFilterWidget);
458 
459  // using the local copy (NOT the GUI's FilterWidget!!!) apply then
460  // the filter using qtconcurrent's filteredReduced. ApplyFilterDone()
461  // will get called when the filtering is finished.
462  m_filterRunning = true;
463  rootItem->setLastVisibleFilteredItem(NULL);
464  futureRoot = QtConcurrent::filteredReduced(rootItem->getChildren(),
465  FilterFunctor(m_localFilterWidgetCopy),
466  &FilterFunctor::updateTopLevelLinks,
467  QtConcurrent::OrderedReduce | QtConcurrent::SequentialReduce);
468 
469  m_filterWatcher->setFuture(futureRoot);
470  }
471  }
472  }
473 
474 
475  void AbstractTreeModel::setGlobalSelection(bool selected,
476  InterestingItemsFlag flags) {
477  selectItems(rootItem, selected, flags);
478  }
479 
480 
481  void AbstractTreeModel::selectItems(
482  AbstractTreeItem *item, bool selected, InterestingItemsFlag flags) {
483  if (item && itemIsInteresting(item, flags)) {
484  item->setSelected(selected);
485  }
486 
487  if (item->childCount()) {
488  foreach (AbstractTreeItem * childItem, item->getChildren()) {
489  selectItems(childItem, selected, flags);
490  }
491  }
492  }
493 
494 
495  bool AbstractTreeModel::itemIsInteresting(AbstractTreeItem *item,
496  InterestingItemsFlag flags) {
497  AbstractTreeItem::InternalPointerType pointerType =
498  item->getPointerType();
499 
500  if ((pointerType == AbstractTreeItem::Point && flags.testFlag(PointItems)) ||
501  (pointerType == AbstractTreeItem::Measure && flags.testFlag(MeasureItems)) ||
502  (pointerType == AbstractTreeItem::ImageAndNet && flags.testFlag(ImageItems))) {
503  return true;
504  }
505  else {
506  return false;
507  }
508  }
509 
510 
511  int AbstractTreeModel::getItemCount(AbstractTreeItem *item,
512  InterestingItemsFlag flags) const {
513  int count = 0;
514 
515  if (item && itemIsInteresting(item, flags)) {
516  count++;
517  }
518 
519  if (item->childCount()) {
520  foreach (AbstractTreeItem * childItem, item->getChildren()) {
521  count += getItemCount(childItem, flags);
522  }
523  }
524 
525  return count;
526  }
527 
528 
529  AbstractTreeItem *AbstractTreeModel::nextItem(AbstractTreeItem *current,
530  InterestingItemsFlag flags, bool ignoreExpansion) const {
531  if (current) {
532  do {
533  if ((ignoreExpansion || current->isExpanded()) &&
534  current->getFirstVisibleChild())
535  current = current->getFirstVisibleChild();
536  else if (current->getNextVisiblePeer())
537  current = current->getNextVisiblePeer();
538  else if (current->parent())
539  current = current->parent()->getNextVisiblePeer();
540  else
541  current = NULL;
542  }
543  while (current && !itemIsInteresting(current, flags));
544  }
545 
546  return current;
547  }
548 
549 
550  void AbstractTreeModel::applyFilterDone() {
551  m_filterRunning = false;
552 
553  if (m_filterAgain) {
554  m_filterAgain = false;
555  applyFilter();
556  }
557  else {
558  emit modelModified();
559  emit filterCountsChanged(getVisibleTopLevelItemCount(),
560  getTopLevelItemCount());
561  }
562  }
563 
564 
565  void AbstractTreeModel::rebuildItemsDone() {
566  clear();
567 
568  QAtomicPointer< RootItem > newRootPtr = m_rebuildWatcher->future();
569  RootItem *newRoot = newRootPtr.loadAcquire();
570 
571  if (newRoot && newRoot->childCount()) {
572  ASSERT(rootItem);
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(
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 }
QSize getVisibleSize(int indentation) const
indentation is in pixels
void filterCountsChanged(int visibleTopLevelItemCount, int topLevelItemCount)
This signal is emitted after filtering to provide the number of visible top-level items remaining aft...
This error is for when a programmer made an API call that was illegal.
Definition: IException.h:162
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
Base class for an item in the tree.