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