Isis 3.0 Programmer Reference
Back | Home
AbstractTableModel.cpp
1 #include "IsisDebug.h"
2 
3 #include <cmath>
4 #include <iostream>
5 
6 #include <QCoreApplication>
7 #include <QDateTime>
8 #include <QDebug>
9 #include <QFutureWatcher>
10 #include <QSettings>
11 #include <QtConcurrentRun>
12 #include <QTimer>
13 
14 #include "IException.h"
15 #include "IString.h"
16 
17 #include "AbstractTableDelegate.h"
18 #include "AbstractTableModel.h"
19 #include "BusyLeafItem.h"
20 #include "TableColumn.h"
21 #include "TableColumnList.h"
22 #include "TableView.h"
23 #include "AbstractTreeModel.h"
24 
25 
26 namespace Isis {
27  namespace CnetViz {
28  AbstractTableModel::AbstractTableModel(AbstractTreeModel *model,
29  AbstractTableDelegate *someDelegate) {
30  nullify();
31 
32  m_dataModel = model;
33  connect(model, SIGNAL(cancelSort()), this, SLOT(cancelSort()));
34 
35  m_delegate = someDelegate;
36 
37  m_sortingEnabled = false;
38  m_sortLimit = 10000;
39  m_sorting = false;
40 
41  m_sortedItems = new QList<AbstractTreeItem *>;
42  m_busyItem = new BusyLeafItem;
43  m_sortStatusPoller = new QTimer;
44 
45  m_sortingWatcher = new QFutureWatcher< QList< AbstractTreeItem * > >;
46  connect(m_sortingWatcher, SIGNAL(finished()), this, SLOT(sortFinished()));
47 
48  connect(m_sortStatusPoller, SIGNAL(timeout()),
49  this, SLOT(sortStatusUpdated()));
50 
51  // Signal forwarding
52  connect(model, SIGNAL(modelModified()), SLOT(rebuildSort()));
53 
54  connect(model, SIGNAL(filterProgressChanged(int)),
55  this, SIGNAL(filterProgressChanged(int)));
56 
57  connect(model, SIGNAL(rebuildProgressChanged(int)),
58  this, SIGNAL(rebuildProgressChanged(int)));
59 
60  connect(model, SIGNAL(filterProgressRangeChanged(int, int)),
61  this, SIGNAL(filterProgressRangeChanged(int, int)));
62 
63  connect(model, SIGNAL(rebuildProgressRangeChanged(int, int)),
64  this, SIGNAL(rebuildProgressRangeChanged(int, int)));
65 
66  connect(this, SIGNAL(tableSelectionChanged(QList<AbstractTreeItem *>)),
67  model, SIGNAL(tableSelectionChanged(QList<AbstractTreeItem *>)));
68  connect(model, SIGNAL(filterCountsChanged(int, int)),
69  this, SIGNAL(filterCountsChanged(int, int)));
70  }
71 
72 
73  AbstractTableModel::~AbstractTableModel() {
74  cancelSort();
75 
76  m_dataModel = NULL;
77 
78  delete m_delegate;
79  m_delegate = NULL;
80 
81  delete m_sortedItems;
82  m_sortedItems = NULL;
83 
84  delete m_busyItem;
85  m_busyItem = NULL;
86 
87  delete m_sortStatusPoller;
88  m_sortStatusPoller = NULL;
89 
90  delete m_lessThanFunctor;
91  m_lessThanFunctor = NULL;
92 
93  if (m_columns) {
94  for (int i = 0; i < m_columns->size(); i++)
95  delete(*m_columns)[i];
96 
97  delete m_columns;
98  m_columns = NULL;
99  }
100 
101  delete m_sortingWatcher;
102  m_sortingWatcher = NULL;
103  }
104 
105 
106  bool AbstractTableModel::isSorting() const {
107  return m_sorting;
108  }
109 
110 
111  bool AbstractTableModel::isFiltering() const {
112  return m_dataModel && m_dataModel->isFiltering();
113  }
114 
115 
116  bool AbstractTableModel::sortingIsEnabled() const {
117  return m_sortingEnabled;
118  }
119 
120 
121  void AbstractTableModel::setSortingEnabled(bool enabled) {
122  if (m_sortingEnabled != enabled) {
123  m_sortingEnabled = enabled;
124  rebuildSort();
125  }
126  }
127 
128 
129  int AbstractTableModel::sortLimit() const {
130  return m_sortLimit;
131  }
132 
133 
134  void AbstractTableModel::setSortLimit(int limit) {
135  if (m_sortLimit != limit) {
136  m_sortLimit = limit;
137  rebuildSort();
138  }
139  }
140 
141 
142  bool AbstractTableModel::sortingOn() const {
143  return (sortingIsEnabled() && (getVisibleRowCount() <= sortLimit()));
144  }
145 
146 
147  TableColumnList *AbstractTableModel::getColumns() {
148  if (!m_columns) {
149  m_columns = createColumns();
150  connect(m_columns, SIGNAL(sortOutDated()), this, SLOT(sort()));
151  }
152 
153  return m_columns;
154  }
155 
156 
157  const AbstractTableDelegate *AbstractTableModel::getDelegate() const {
158  return m_delegate;
159  }
160 
161 
162  void AbstractTableModel::applyFilter() {
163  getDataModel()->applyFilter();
164  }
165 
166 
167  void AbstractTableModel::sort() {
168  if (sortingOn() && m_sortedItems->size() && !m_dataModel->isFiltering() &&
169  !m_dataModel->isRebuilding()) {
170  if (isSorting()) {
171  cancelSort();
172  }
173  else if (!m_lessThanFunctor) {
174  // Create a new comparison functor to be used in the m_sort. It will
175  // keep track of the number of comparisons made so that we can make a
176  // guess at the progress of the m_sort.
177  m_lessThanFunctor = new LessThanFunctor(
178  m_columns->getSortingOrder().first());
179 
180  // Sorting is always done on a COPY of the items list.
181  QFuture< QList< AbstractTreeItem * > > future =
182  QtConcurrent::run(this, &AbstractTableModel::doSort,
183  *m_sortedItems);
184  m_sortingWatcher->setFuture(future);
185 
186  emit modelModified();
187  }
188  }
189  }
190 
191 
192  void AbstractTableModel::reverseOrder(TableColumn *column) {
193  }
194 
195 
196  void AbstractTableModel::updateSort() {
197  }
198 
199 
200  AbstractTreeModel *AbstractTableModel::getDataModel() {
201  ASSERT(m_dataModel);
202  return m_dataModel;
203  }
204 
205 
206  const AbstractTreeModel *AbstractTableModel::getDataModel() const {
207  ASSERT(m_dataModel);
208  return m_dataModel;
209  }
210 
211 
212  QList< AbstractTreeItem * > AbstractTableModel::getSortedItems(
213  int start, int end, AbstractTreeModel::InterestingItems flags) {
214  QList< AbstractTreeItem * > sortedSubsetOfItems;
215 
216  if (sortingOn()) {
217  while (start <= end) {
218  if (start < m_sortedItems->size())
219  sortedSubsetOfItems.append(m_sortedItems->at(start));
220  else if (isFiltering())
221  sortedSubsetOfItems.append(m_busyItem);
222 
223  start++;
224  }
225  }
226  else {
227  sortedSubsetOfItems = getDataModel()->getItems(start, end, flags, true);
228  }
229 
230  return sortedSubsetOfItems;
231  }
232 
233 
234  QList< AbstractTreeItem * > AbstractTableModel::getSortedItems(
235  AbstractTreeItem *item1, AbstractTreeItem *item2,
236  AbstractTreeModel::InterestingItems flags) {
237  QList< AbstractTreeItem * > sortedSubsetOfItems;
238 
239  if (!sortingOn()) {
240  sortedSubsetOfItems = getDataModel()->getItems(item1, item2, flags, true);
241  }
242  else {
243  AbstractTreeItem *start = NULL;
244 
245  int currentIndex = 0;
246 
247  while (!start && currentIndex < m_sortedItems->size()) {
248  AbstractTreeItem *current = m_sortedItems->at(currentIndex);
249  if (current == item1)
250  start = item1;
251  else if (current == item2)
252  start = item2;
253 
254  if (!start)
255  currentIndex++;
256  }
257 
258  if (!start) {
259  IString msg = "Could not find the first item";
260  throw IException(IException::Programmer, msg, _FILEINFO_);
261  }
262 
263  AbstractTreeItem *end = item2;
264 
265  // Sometimes we need to build the list forwards and sometimes backwards.
266  // This is accomplished by using either append or prepend. We abstract
267  // away which of these we should use (why should we care) by using the
268  // variable "someKindaPend" to store the appropriate method.
269  void (QList< AbstractTreeItem * >::*someKindaPend)(
270  AbstractTreeItem * const &);
271  someKindaPend = &QList< AbstractTreeItem * >::append;
272 
273  if (start == item2) {
274  end = item1;
275  someKindaPend = &QList< AbstractTreeItem * >::prepend;
276  }
277 
278  while (currentIndex < m_sortedItems->size() &&
279  m_sortedItems->at(currentIndex) != end) {
280  (sortedSubsetOfItems.*someKindaPend)(m_sortedItems->at(currentIndex));
281  currentIndex++;
282  }
283 
284  if (currentIndex >= m_sortedItems->size()) {
285  IString msg = "Could not find the second item";
286  throw IException(IException::Programmer, msg, _FILEINFO_);
287  }
288 
289  (sortedSubsetOfItems.*someKindaPend)(end);
290  }
291 
292  return sortedSubsetOfItems;
293  }
294 
295 
296  void AbstractTableModel::handleTreeSelectionChanged(
297  QList< AbstractTreeItem * > newlySelectedItems,
298  AbstractTreeItem::InternalPointerType pointerType) {
299  QList< AbstractTreeItem * > interestingSelectedItems;
300  foreach (AbstractTreeItem * item, newlySelectedItems) {
301  if (item->getPointerType() == pointerType)
302  interestingSelectedItems.append(item);
303  }
304 
305  if (interestingSelectedItems.size()) {
306  emit treeSelectionChanged(interestingSelectedItems);
307  }
308  }
309 
310 
311  void AbstractTableModel::sortStatusUpdated() {
312  if (m_lessThanFunctor)
313  emit sortProgressChanged(m_lessThanFunctor->getCompareCount());
314  }
315 
316 
317  void AbstractTableModel::sortFinished() {
318  bool interrupted = m_lessThanFunctor->interrupted();
319  delete m_lessThanFunctor;
320  m_lessThanFunctor = NULL;
321 
322  if (!interrupted) {
323  QList< AbstractTreeItem * > newSortedItems = m_sortingWatcher->result();
324 
325  if (!m_dataModel->isFiltering() && !m_dataModel->isRebuilding()) {
326  *m_sortedItems = newSortedItems;
327  emit modelModified();
328  }
329  }
330  else {
331  sort();
332  }
333  }
334 
335 
336  void AbstractTableModel::cancelSort() {
337  if (m_lessThanFunctor) {
338  m_lessThanFunctor->interrupt();
339  m_sortingWatcher->waitForFinished();
340  }
341  }
342 
343 
344  void AbstractTableModel::itemsLost() {
345  cancelSort();
346  m_sortedItems->clear();
347  }
348 
349 
350  QList< AbstractTreeItem * > AbstractTableModel::doSort(
351  QList< AbstractTreeItem * > itemsToSort) {
352  ASSERT(!isSorting());
353  if (!isSorting()) {
354  setSorting(true);
355 
356  QList< TableColumn * > columnsToSortOn = m_columns->getSortingOrder();
357  if (sortingOn()) {
358  // Reset the timer so that it will begin polling the status of the
359  // m_sort.
360  m_sortStatusPoller->start(SORT_UPDATE_FREQUENCY);
361 
362  // Use n*log2(n) as our estimate of the number of comparisons that it
363  // should take to m_sort the list.
364  int numItems = itemsToSort.size();
365  double a = 1.0;
366  double b = 1.0;
367  emit sortProgressRangeChanged(0,
368  (int)((a * numItems) * (log2(b * numItems))));
369 
370  try {
371  qStableSort(itemsToSort.begin(), itemsToSort.end(),
372  *m_lessThanFunctor);
373  }
374  catch (SortingCanceledException &e) {
375  m_sortStatusPoller->stop();
376  emit sortProgressRangeChanged(0, 0);
377  emit sortProgressChanged(0);
378  emit modelModified();
379 
380  setSorting(false);
382  }
383 
384  // The m_sort is done, so stop emiting status updates and make sure we
385  // let the listeners know that the m_sort is done (since the status
386  // will not always reach 100% as we are estimating the progress).
387  m_sortStatusPoller->stop();
388  emit sortProgressRangeChanged(0, 0);
389  emit sortProgressChanged(0);
390  emit modelModified();
391  }
392 
393  setSorting(false);
394  }
395 
396  return itemsToSort;
397  }
398 
399 
400  void AbstractTableModel::nullify() {
401  m_dataModel = NULL;
402  m_delegate = NULL;
403  m_sortedItems = NULL;
404  m_busyItem = NULL;
405  m_sortStatusPoller = NULL;
406  m_lessThanFunctor = NULL;
407  m_columns = NULL;
408  m_sortingWatcher = NULL;
409  }
410 
411 
412  void AbstractTableModel::setSorting(bool isSorting) {
413  m_sorting = isSorting;
414  }
415 
416 
417  void AbstractTableModel::rebuildSort() {
418  ASSERT(m_dataModel);
419  ASSERT(m_sortedItems);
420  m_sortedItems->clear();
421  cancelSort();
422 
423  if (sortingOn()) {
424  m_sortingEnabled = false;
425  *m_sortedItems = getItems(0, -1);
426 
427  foreach (AbstractTreeItem * item, *m_sortedItems) {
428  connect(item, SIGNAL(destroyed(QObject *)), this, SLOT(itemsLost()));
429  }
430 
431  m_sortingEnabled = true;
432  sort();
433 
434  emit userWarning(None);
435  }
436  else {
437  cancelSort();
438  emit modelModified();
439 
440  if (!m_sortingEnabled)
441  emit userWarning(SortingDisabled);
442  else
443  emit userWarning(SortingTableSizeLimitReached);
444  }
445  }
446 
447 
448  // *********** LessThanFunctor implementation *************
449 
450 
451  AbstractTableModel::LessThanFunctor::LessThanFunctor(
452  TableColumn const *someColumn) : m_column(someColumn) {
453  m_sharedData = new LessThanFunctorData;
454  }
455 
456 
457  AbstractTableModel::LessThanFunctor::LessThanFunctor(
458  LessThanFunctor const &other) : m_sharedData(other.m_sharedData) {
459  m_column = other.m_column;
460  }
461 
462 
463  AbstractTableModel::LessThanFunctor::~LessThanFunctor() {
464  m_column = NULL;
465  }
466 
467 
468  int AbstractTableModel::LessThanFunctor::getCompareCount() const {
469  return m_sharedData->getCompareCount();
470  }
471 
472 
473  void AbstractTableModel::LessThanFunctor::interrupt() {
474  m_sharedData->setInterrupted(true);
475  }
476 
477 
478  bool AbstractTableModel::LessThanFunctor::interrupted() {
479  return m_sharedData->interrupted();
480  }
481 
482 
483  void AbstractTableModel::LessThanFunctor::reset() {
484  m_sharedData->setInterrupted(false);
485  }
486 
487 
488  bool AbstractTableModel::LessThanFunctor::operator()(
489  AbstractTreeItem *const &left, AbstractTreeItem *const &right) {
490  if (left->getPointerType() != right->getPointerType()) {
491  IString msg = "Tried to compare apples to oranges";
492  throw IException(IException::Programmer, msg, _FILEINFO_);
493  }
494 
495  if (m_sharedData->interrupted()) {
496  throw SortingCanceledException();
497  }
498 
499  m_sharedData->incrementCompareCount();
500 
501  QVariant leftData = left->getData(m_column->getTitle());
502  QVariant rightData = right->getData(m_column->getTitle());
503  QString busy = BusyLeafItem().getData().toString();
504 
505  bool lessThan;
506  if (leftData.type() == QVariant::String &&
507  rightData.type() == QVariant::String) {
508  lessThan = leftData.toString() < rightData.toString();
509  }
510  else if (leftData.type() == QVariant::Double &&
511  rightData.type() == QVariant::Double) {
512  lessThan = (leftData.toDouble() < rightData.toDouble());
513  }
514  else if (leftData.type() == QVariant::Double ||
515  rightData.type() == QVariant::Double) {
516  // We are comparing a BusyLeafItem to a double. BusyLeafItem's should
517  // always be less than the double.
518  lessThan = (leftData.toString() == busy);
519  }
520  else {
521  lessThan = leftData.toString() < rightData.toString();
522  }
523 
524  return lessThan ^ m_column->sortAscending();
525  }
526 
527 
528  AbstractTableModel::LessThanFunctor &
529  AbstractTableModel::LessThanFunctor::operator=(
530  LessThanFunctor const &other) {
531  if (this != &other) {
532  m_column = other.m_column;
533  m_sharedData = other.m_sharedData;
534  }
535 
536  return *this;
537  }
538 
539 
540  // *********** LessThanFunctorData implementation *************
541 
542 
543  AbstractTableModel::LessThanFunctorData::LessThanFunctorData() {
544  m_compareCount.fetchAndStoreRelaxed(0);
545  m_interruptFlag.fetchAndStoreRelaxed(0);
546  }
547 
548 
549  AbstractTableModel::LessThanFunctorData::LessThanFunctorData(
550  LessThanFunctorData const &other) : QSharedData(other),
551  m_compareCount(other.m_compareCount), m_interruptFlag(other.m_interruptFlag) {
552  }
553 
554 
555  AbstractTableModel::LessThanFunctorData::~LessThanFunctorData() {
556  }
557 
558 
559  int AbstractTableModel::LessThanFunctorData::getCompareCount() const {
560  return m_compareCount;
561  }
562 
563 
564  void AbstractTableModel::LessThanFunctorData::incrementCompareCount() {
565  m_compareCount.fetchAndAddRelaxed(1);
566  }
567 
568 
569  void AbstractTableModel::LessThanFunctorData::setInterrupted(bool newStatus) {
570  newStatus ? m_interruptFlag.fetchAndStoreRelaxed(1) :
571  m_interruptFlag.fetchAndStoreRelaxed(0);
572  }
573 
574 
575  bool AbstractTableModel::LessThanFunctorData::interrupted() {
576  return m_interruptFlag != 0;
577  }
578  }
579 }
580 
This error is for when a programmer made an API call that was illegal.
Definition: IException.h:154
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:38

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:50