USGS

Isis 3.0 Object Programmers' Reference

Home

TableViewContent.cpp
1 #include "IsisDebug.h"
2 
3 #include "TableViewContent.h"
4 
5 #include <cmath>
6 #include <iostream>
7 
8 #include <QAction>
9 #include <QDebug>
10 #include <QLabel>
11 #include <QMenu>
12 #include <QMessageBox>
13 #include <QMutex>
14 #include <QPainter>
15 #include <QPaintEvent>
16 #include <QRect>
17 #include <QScrollBar>
18 #include <QSize>
19 #include <QStyle>
20 #include <QStyleOptionViewItemV4>
21 #include <QThread>
22 #include <QVBoxLayout>
23 
24 #include "ControlMeasure.h"
25 #include "IException.h"
26 #include "IString.h"
27 
28 #include "AbstractTableDelegate.h"
29 #include "AbstractTableModel.h"
30 #include "AbstractTreeItem.h"
31 #include "TableColumn.h"
32 #include "TableColumnList.h"
33 #include <ControlPoint.h>
34 
35 
36 namespace Isis {
37  namespace CnetViz {
38  TableViewContent::TableViewContent(AbstractTableModel *someModel) {
39  nullify();
40 
41  m_model = someModel;
42  connect(m_model, SIGNAL(modelModified()), this, SLOT(refresh()));
43  connect(m_model, SIGNAL(filterProgressChanged(int)),
44  this, SLOT(updateItemList()));
45  connect(this, SIGNAL(modelDataChanged()),
46  m_model, SLOT(applyFilter()));
47  connect(this,
48  SIGNAL(tableSelectionChanged(QList< AbstractTreeItem * >)),
49  m_model,
50  SIGNAL(tableSelectionChanged(QList< AbstractTreeItem * >)));
51  connect(m_model, SIGNAL(treeSelectionChanged(QList< AbstractTreeItem * >)),
52  this, SLOT(scrollTo(QList< AbstractTreeItem * >)));
53 
54  m_columns = getModel()->getColumns();
55  for (int i = 0; i < m_columns->size(); i++) {
56  TableColumn *column = (*m_columns)[i];
57 
58  connect(column, SIGNAL(visibilityChanged()), this, SLOT(refresh()));
59  connect(column, SIGNAL(visibilityChanged()),
60  this, SLOT(updateHorizontalScrollBar()));
61  connect(column, SIGNAL(widthChanged()), this, SLOT(refresh()));
62  }
63 
64  m_items = new QList< QPointer<AbstractTreeItem> >;
65  m_mousePressPos = new QPoint;
66  m_activeCell = new QPair<AbstractTreeItem *, int>(NULL, -1);
68  m_lastShiftSelection = new QList<AbstractTreeItem *>;
69  m_lastShiftArrowSelectedCell = new QPair< AbstractTreeItem *, int >;
70  m_lastShiftArrowSelectedCell->first = NULL;
71 
72  verticalScrollBar()->setSingleStep(1);
73 
74  m_rowHeight = QFontMetrics(font()).height() + ITEM_PADDING;
75  ASSERT(m_rowHeight > 0);
76 
77  connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
78  this, SIGNAL(horizontalScrollBarValueChanged(int)));
79 
80  setMouseTracking(true);
81  updateHorizontalScrollBar();
82 
83  createActions();
84 
85  setContextMenuPolicy(Qt::CustomContextMenu);
86  connect(this, SIGNAL(customContextMenuRequested(QPoint)),
87  this, SLOT(showContextMenu(QPoint)));
88  }
89 
90 
91  TableViewContent::~TableViewContent() {
92  delete m_items;
93  m_items = NULL;
94 
95  delete m_mousePressPos;
96  m_mousePressPos = NULL;
97 
98  delete m_activeCell;
99  m_activeCell = NULL;
100 
101  delete m_editWidget;
102  m_editWidget = NULL;
103 
104  delete m_lastShiftSelection;
105  m_lastShiftSelection = NULL;
106 
107  delete m_applyToSelectionAct;
108  m_applyToSelectionAct = NULL;
109 
110  delete m_applyToAllAct;
111  m_applyToAllAct = NULL;
112 
115 
116  delete m_lastShiftArrowSelectedCell;
117  m_lastShiftArrowSelectedCell = NULL;
118 
119  m_columns = NULL;
120  }
121 
122 
123  QSize TableViewContent::minimumSizeHint() const {
124  return QWidget::minimumSizeHint();
125  }
126 
127 
128  QSize TableViewContent::sizeHint() {
129  return minimumSizeHint();
130  }
131 
132 
133  AbstractTableModel *TableViewContent::getModel() {
134  ASSERT(m_model);
135  return m_model;
136  }
137 
138 
139  // void TableViewContent::setModel(AbstractTableModel * someModel)
140  // {
141  // if (!someModel)
142  // {
143  // IString msg = "Attempted to set a NULL m_model!";
144  // throw iException::Message(iException::Programmer, msg, _FILEINFO_);
145  // }
146  //
147  // if (getModel())
148  // {
149  // disconnect(getModel(), SIGNAL(m_modelModified()), this, SLOT(refresh()));
150  // disconnect(getModel(), SIGNAL(filterProgressChanged(int)),
151  // this, SLOT(updateItemList()));
152  // disconnect(this,
153  // SIGNAL(tableSelectionChanged(QList< AbstractTreeItem * >)),
154  // getModel(),
155  // SIGNAL(tableSelectionChanged(QList< AbstractTreeItem * >)));
156  // disconnect(getModel(), SIGNAL(selectionChanged(QList<AbstractTreeItem*>)),
157  // this, SLOT(scrollTo(QList<AbstractTreeItem*>)));
158  // }
159  //
160  // m_model = someModel;
161  // m_columns = someModel->getColumns();
162  //
163  // connect(m_model, SIGNAL(m_modelModified()), this, SLOT(refresh()));
164  // connect(m_model, SIGNAL(filterProgressChanged(int)),
165  // this, SLOT(updateItemList()));
166  // connect(this, SIGNAL(m_modelDataChanged()),
167  // m_model, SLOT(applyFilter()));
168  // connect(this, SIGNAL(tableSelectionChanged(QList< AbstractTreeItem * >)),
169  // m_model, SIGNAL(tableSelectionChanged(QList< AbstractTreeItem * >)));
170  // connect(m_model, SIGNAL(treeSelectionChanged(QList<AbstractTreeItem*>)),
171  // this, SLOT(scrollTo(QList<AbstractTreeItem*>)));
172  //
173  // refresh();
174  // }
175 
176 
177  void TableViewContent::refresh() {
178  if (m_model) {
179  if (!m_model->isFiltering()) {
180  int rowCount = m_model->getVisibleRowCount();
181  verticalScrollBar()->setRange(0, qMax(rowCount - 1, 0));
182  }
183 
184  updateItemList();
186  m_lastShiftSelection->clear();
187 
188  if (m_model->getSelectedItems().size() &&
189  rowsWithActiveColumnSelected->size()) {
191  clearColumnSelection();
192  }
193 
194  viewport()->update();
195  }
196  }
197 
198 
199  void TableViewContent::updateHorizontalScrollBar(bool scrollRight) {
200  if (m_columns) {
201  int range = 0;
202  TableColumnList visibleCols = m_columns->getVisibleColumns();
203  for (int i = 0; i < visibleCols.size(); i++)
204  range += visibleCols[i]->getWidth() - 1;
205 
206  // For the border...
207  range -= 2;
208  horizontalScrollBar()->setRange(0, range - viewport()->width());
209  horizontalScrollBar()->setPageStep(viewport()->width());
210 
211  if (scrollRight)
212  horizontalScrollBar()->setValue(horizontalScrollBar()->maximum());
213  }
214  }
215 
216 
217  void TableViewContent::scrollTo(
218  QList< AbstractTreeItem * > newlySelectedItems) {
219  if (newlySelectedItems.size())
220  scrollTo(newlySelectedItems.last());
221  }
222 
223 
224  void TableViewContent::scrollTo(AbstractTreeItem *newlySelectedItem) {
225  int row = getModel()->indexOfVisibleItem(newlySelectedItem);
226 
227  if (row >= 0) {
228  int topRow = verticalScrollBar()->value();
229 
230  if (row < topRow) {
231  verticalScrollBar()->setValue(row);
232  }
233  else {
234  int wholeVisibleRowCount = viewport()->height() / m_rowHeight;
235  int bottomRow = topRow + wholeVisibleRowCount;
236  if (row > bottomRow)
237  verticalScrollBar()->setValue(row - wholeVisibleRowCount + 1);
238  }
239  }
240 
241  viewport()->update();
242  }
243 
244 
245  bool TableViewContent::eventFilter(QObject *target, QEvent *event) {
246  return QObject::eventFilter(target, event);
247  }
248 
249 
250  void TableViewContent::mouseDoubleClickEvent(QMouseEvent *event) {
251  if (event->buttons() & Qt::LeftButton) {
252  int rowNum = event->pos().y() / m_rowHeight;
253 
254  if (m_activeCell->first && cellIsEditable(rowNum, m_activeCell->second)) {
255  const AbstractTableDelegate *delegate = m_model->getDelegate();
256  TableColumn *col =
257  m_columns->getVisibleColumns()[m_activeCell->second];
258 
259  delete m_editWidget;
260  m_editWidget = NULL;
261  m_editWidget = delegate->getWidget(col);
262  delegate->readData(m_editWidget, m_activeCell->first, col);
263  m_editWidget->setParent(this);
264  m_editWidget->setFocus(Qt::OtherFocusReason);
265  }
266 
267  viewport()->update();
268  }
269  }
270 
271 
272  void TableViewContent::mousePressEvent(QMouseEvent *event) {
273  if (event->buttons() & Qt::LeftButton) {
274  // FIXME refactor this file (lol)
275  if (!(event->modifiers() & Qt::ShiftModifier))
276  updateActiveCell(event->pos());
277 
278  int rowNum = event->pos().y() / m_rowHeight;
279  int colNum = getColumnFromScreenX(event->pos().x());
280 
281  // HACK
282  // BUG
283  // TODO
284  // FIXME
285  if (colNum == 0)
286  clearActiveCell();
287 
288  if (rowNum >= 0 && rowNum < m_items->size() && m_activeCell->first) {
289  // The user clicked on a valid item, handle selection of individual
290  // cells (not rows).
291 
292  // Deselect all rows, as this will now be a cell selection.
293  m_model->setGlobalSelection(false);
294 
295  if (cellIsEditable(rowNum, m_activeCell->second)) {
296  if (event->modifiers() & Qt::ControlModifier) {
297  if (rowsWithActiveColumnSelected->indexOf(m_activeCell->first) < 0)
298  rowsWithActiveColumnSelected->append(m_activeCell->first);
299  else
300  rowsWithActiveColumnSelected->removeAll(m_activeCell->first);
301  m_lastDirectlySelectedRow = m_activeCell->first;
302 
303  m_lastShiftSelection->clear();
304  }
305  else {
306  if (event->modifiers() & Qt::ShiftModifier) {
307  updateColumnGroupSelection((*m_items)[rowNum]);
308  }
309  else {
310  // Normal click, no modifiers.
311  clearColumnSelection();
312  rowsWithActiveColumnSelected->append(m_activeCell->first);
313  m_lastDirectlySelectedRow = m_activeCell->first;
314  m_lastShiftSelection->clear();
315  }
316  }
317  }
318  }
319  else {
320  // row selections
321  if (rowNum >= 0 && rowNum < m_items->size()) {
322  int columnNum = getColumnFromScreenX(event->pos().x());
323 
324  if (columnNum != -1) {
325  TableColumn *column = m_columns->getVisibleColumns()[columnNum];
326  if (column->getTitle().isEmpty()) {
327  clearColumnSelection();
328 
329  AbstractTreeItem *const &item = m_items->at(rowNum);
330  QList< AbstractTreeItem * > newlySelectedItems;
331 
332  if (event->modifiers() & Qt::ControlModifier) {
333  if (item->getPointerType() == AbstractTreeItem::Measure)
334  item->parent()->setSelected(!item->isSelected());
335 
336  item->setSelected(!item->isSelected());
338  newlySelectedItems.append(item);
339  }
340  else {
341  if (event->modifiers() & Qt::ShiftModifier) {
342  newlySelectedItems = updateRowGroupSelection(rowNum);
343  }
344  else {
345  QList<AbstractTreeItem *> selectedItems =
346  m_model->getSelectedItems();
347 
348  foreach (AbstractTreeItem * selectedItem, selectedItems) {
349  if (selectedItem->getPointerType() ==
350  AbstractTreeItem::Measure)
351  selectedItem->parent()->setSelected(false);
352  }
353 
354  m_model->setGlobalSelection(false);
355 
356  if (item->getPointerType() == AbstractTreeItem::Measure)
357  item->parent()->setSelected(true);
358 
359  item->setSelected(true);
361  newlySelectedItems.append(item);
362  }
363  }
364 
365  QList< AbstractTreeItem * > tmp = newlySelectedItems;
366  newlySelectedItems.clear();
367  foreach (AbstractTreeItem * i, tmp) {
368  newlySelectedItems.append(i);
369  if (i->getPointerType() == AbstractTreeItem::Point) {
370  foreach (AbstractTreeItem * child, i->getChildren()) {
371  child->setSelected(true);
372  newlySelectedItems.append(child);
373  }
374  }
375  }
376 
377  emit tableSelectionChanged(newlySelectedItems);
378  }
379  }
380  }
381  }
382 
383  delete m_editWidget;
384  m_editWidget = NULL;
385 
386  viewport()->update();
387  emit tableSelectionChanged();
388  }
389  }
390 
391 
392  void TableViewContent::mouseReleaseEvent(QMouseEvent *event) {
393  }
394 
395 
396  void TableViewContent::mouseMoveEvent(QMouseEvent *event) {
397  if (!m_editWidget) {
398  if (event->buttons() & Qt::LeftButton) {
399  int rowNum = event->pos().y() / m_rowHeight;
400 
401  // used to make sure that the mouse position is inside the content
402  int yPos = event->pos().y();
403  if (yPos >= 0 && rowNum < m_items->size() && m_activeCell->first) {
404  // The user clicked on a valid item,
405  // handle selection of individual cells (not rows).
406  if (cellIsEditable(rowNum, m_activeCell->second)) {
407  updateColumnGroupSelection((*m_items)[rowNum]);
408  }
409  }
410  else {
411  // row selections
412  if (yPos >= 0 && rowNum < m_items->size()) {
413  // There is no active cell,
414  // maybe they clicked on the row number column.
415  int columnNum = getColumnFromScreenX(event->pos().x());
416 
417  if (columnNum != -1) {
418  clearColumnSelection();
419 
421  updateRowGroupSelection(rowNum);
422  QList< AbstractTreeItem * > newlySelectedItems;
423  foreach (AbstractTreeItem * i, tmp) {
424  newlySelectedItems.append(i);
425  if (i->getPointerType() == AbstractTreeItem::Point) {
426  foreach (AbstractTreeItem * child, i->getChildren()) {
427  child->setSelected(true);
428  newlySelectedItems.append(child);
429  }
430  }
431  }
432 
433  emit tableSelectionChanged(newlySelectedItems);
434  }
435  }
436  }
437 
438  QScrollBar *vertScroll = verticalScrollBar();
439 
440  if (yPos > viewport()->height() &&
441  vertScroll->value() < vertScroll->maximum()) {
442  // Scroll down to allow for more drag selections.
443  vertScroll->setValue(vertScroll->value() + 1);
444  }
445  else {
446  if (yPos < 0 && vertScroll->value() > vertScroll->minimum())
447  vertScroll->setValue(vertScroll->value() - 1);
448  }
449 
450  viewport()->update();
451  emit tableSelectionChanged();
452  }
453  }
454  }
455 
456 
457  void TableViewContent::leaveEvent(QEvent *event) {
458  // viewport()->update();
459  }
460 
461 
462  void TableViewContent::keyPressEvent(QKeyEvent *event) {
463  Qt::Key key = (Qt::Key) event->key();
464 
465  // Handle Ctrl-A (selects all rows)
466  if (key == Qt::Key_A && event->modifiers() == Qt::ControlModifier) {
467  clearActiveCell();
468  clearColumnSelection();
469  m_model->setGlobalSelection(true);
470  viewport()->update();
471 
472  emit tableSelectionChanged();
473  }
474 
475  // Handle esc key (cancel editing)
476  else if (key == Qt::Key_Escape) {
477  if (m_editWidget) {
478  delete m_editWidget;
479  m_editWidget = NULL;
480  setFocus(Qt::ActiveWindowFocusReason);
481  viewport()->update();
482  }
483  }
484 
485  // Handle delete key (delete row(s) if any are selected)
486  else if (key == Qt::Key_Delete) {
487  if (hasRowSelection())
488  deleteSelectedRows();
489  }
490 
491  // Handle return or enter (stop editing)
492  else if (key == Qt::Key_Return || key == Qt::Key_Enter) {
493  finishEditing();
494  moveActiveCellDown();
495  }
496 
497  // Handle
498  else if (key == Qt::Key_Tab) {
499  finishEditing();
500  moveActiveCellRight();
501  }
502 
503  // Handle arrow key navigation
504  else if (key == Qt::Key_Up || key == Qt::Key_Down ||
505  key == Qt::Key_Left || key == Qt::Key_Right) {
506  if (!hasActiveCell()) {
507  ASSERT(m_items);
508  if (m_items && m_items->size()) {
509  m_activeCell->first = (*m_items)[0];
510  m_activeCell->second = 1;
511  }
512  }
513 
514  if (hasActiveCell() && !m_editWidget) {
515  // should have m_items if we have an active cell
516  ASSERT(m_items->size());
517 
518  // Handle up arrow with shift pressed
519  if (key == Qt::Key_Up && event->modifiers() == Qt::ShiftModifier) {
520  ASSERT(m_lastShiftArrowSelectedCell);
521 
522  AbstractTreeItem *prevCell = m_lastShiftArrowSelectedCell->first ?
523  m_lastShiftArrowSelectedCell->first : m_activeCell->first;
524 
525  int prevCellIndex = getModel()->indexOfVisibleItem(prevCell);
526 
527  if (prevCellIndex > 0) {
528  QList< AbstractTreeItem * > itemList =
529  getModel()->getItems(prevCellIndex - 1, prevCellIndex);
530 
531  if (itemList.size()) {
532  AbstractTreeItem *curItem = itemList[0];
533 
534  if (rowsWithActiveColumnSelected->contains(curItem) ||
535  curItem == m_activeCell->first)
536  rowsWithActiveColumnSelected->removeAll(prevCell);
537  else
538  rowsWithActiveColumnSelected->append(curItem);
539 
540  if (curItem == m_activeCell->first)
541  m_lastShiftArrowSelectedCell->first = NULL;
542  else
543  m_lastShiftArrowSelectedCell->first = curItem;
544  m_lastShiftArrowSelectedCell->second = m_activeCell->second;
545 
546  // scroll if needed
547  int m_itemsPrevIndex = m_items->indexOf(prevCell);
548  int m_itemsCurIndex = m_items->indexOf(curItem);
549  if (m_itemsCurIndex == -1) {
550  if (m_itemsPrevIndex == 0) { // then need to scroll up!
551  verticalScrollBar()->setValue(qMax(0, prevCellIndex - 1));
552  }
553  else {
554  //int m_itemsLastIndex = m_items->size() - 1;
555  //ASSERT(m_itemsPrevIndex == m_items->at(m_items->size() - 1));
556  //verticalScrollBar()->setValue(qMin(prevCell + 1, ));
557  }
558  }
559 
560  viewport()->update();
561  }
562  }
563  }
564 
565  // Handle down arrow with shift pressed
566  else if (key == Qt::Key_Down && event->modifiers() == Qt::ShiftModifier) {
567  AbstractTreeItem *prevCell = m_lastShiftArrowSelectedCell->first ?
568  m_lastShiftArrowSelectedCell->first : m_activeCell->first;
569 
570  int prevCellIndex = getModel()->indexOfVisibleItem(prevCell);
571 
572  if (prevCellIndex >= 0 &&
573  prevCellIndex < getModel()->getVisibleRowCount() - 1) {
574  QList< AbstractTreeItem * > itemList =
575  getModel()->getItems(prevCellIndex + 1, prevCellIndex + 2);
576 
577  if (itemList.size()) {
578  AbstractTreeItem *curItem = itemList[0];
579 
580  if (rowsWithActiveColumnSelected->contains(curItem) ||
581  curItem == m_activeCell->first)
582  rowsWithActiveColumnSelected->removeAll(prevCell);
583  else
584  rowsWithActiveColumnSelected->append(curItem);
585 
586  if (curItem == m_activeCell->first)
587  m_lastShiftArrowSelectedCell->first = NULL;
588  else
589  m_lastShiftArrowSelectedCell->first = curItem;
590  m_lastShiftArrowSelectedCell->second = m_activeCell->second;
591  viewport()->update();
592 
593  // scroll if needed
594  int m_itemsPrevIndex = m_items->indexOf(prevCell);
595  int m_itemsCurIndex = m_items->indexOf(curItem);
596  if (m_itemsCurIndex == -1) {
597  if (m_itemsPrevIndex == m_items->size() - 1) {
598  int visibleItemCount = getModel()->getVisibleRowCount();
599  verticalScrollBar()->setValue(qMin(visibleItemCount,
600  getModel()->indexOfVisibleItem(m_items->at(1))));
601  }
602  else {
603  //int m_itemsLastIndex = m_items->size() - 1;
604  //ASSERT(m_itemsPrevIndex == m_items->at(m_items->size() - 1));
605  //verticalScrollBar()->setValue(qMin(prevCell + 1, ));
606  }
607  }
608  }
609  }
610  }
611 
612  // Handle up arrow
613  else if (key == Qt::Key_Up) {
614  moveActiveCellUp();
615  }
616 
617  // Handle down arrow
618  else if (key == Qt::Key_Down) {
619  moveActiveCellDown();
620  }
621 
622  // Handle left arrow
623  else if (key == Qt::Key_Left) {
624  moveActiveCellLeft();
625  }
626 
627  // Handle right arrow
628  else if (key == Qt::Key_Right) {
629  moveActiveCellRight();
630  }
631  }
632  }
633 
634  // start editing the active cell
635  else {
636  // event->text() will be empty if a modifier was pressed.
637  if (hasActiveCell() && !event->text().isEmpty()) {
638  if (!m_items->contains(m_activeCell->first))
639  scrollTo(m_activeCell->first);
640 
641  ASSERT(m_items->contains(m_activeCell->first));
642 
643  if (m_items->contains(m_activeCell->first) &&
644  cellIsEditable(m_items->indexOf(m_activeCell->first),
645  m_activeCell->second)) {
646  AbstractTableDelegate const *delegate = m_model->getDelegate();
647  TableColumn *col =
648  m_columns->getVisibleColumns()[m_activeCell->second];
649 
650  delete m_editWidget;
651  m_editWidget = NULL;
652  m_editWidget = delegate->getWidget(col);
653  delegate->readData(m_editWidget, m_activeCell->first, col, event->text());
654  m_editWidget->setParent(this);
655  m_editWidget->setFocus(Qt::OtherFocusReason);
656  }
657 
658  viewport()->update();
659  }
660  }
661  }
662 
663 
664  void TableViewContent::finishEditing() {
665  if (m_editWidget) {
666  TableColumn *col =
667  m_columns->getVisibleColumns()[m_activeCell->second];
668  getModel()->getDelegate()->saveData(
669  m_editWidget, m_activeCell->first, col);
670  delete m_editWidget;
671  m_editWidget = NULL;
672 
673  cellDataChanged(col);
674  setFocus(Qt::ActiveWindowFocusReason);
675  }
676  }
677 
678 
679  void TableViewContent::moveActiveCellUp() {
680  int activeIndex = m_items->indexOf(m_activeCell->first);
681  if (activeIndex != -1) {
682  if (activeIndex == 0) {
683  int row = qMax(0, getModel()->indexOfVisibleItem(
684  m_activeCell->first) - 1);
685 
686  verticalScrollBar()->setValue(row);
687  }
688 
689  m_activeCell->first = (*m_items)[qMax(0, activeIndex - 1)];
690  clearColumnSelection();
691  viewport()->update();
692  }
693  }
694 
695 
696  void TableViewContent::moveActiveCellDown() {
697  int activeIndex = m_items->indexOf(m_activeCell->first);
698  if (activeIndex != -1) {
699  if (activeIndex == m_items->size() - 1) {
700  int row = qMin(getModel()->getVisibleRowCount() - 1,
701  getModel()->indexOfVisibleItem(m_items->at(0)));
702 
703  verticalScrollBar()->setValue(row + 1);
704  activeIndex = m_items->indexOf(m_activeCell->first);
705  }
706 
707  m_activeCell->first = (*m_items)[qMin(activeIndex + 1, m_items->size() - 1)];
708  clearColumnSelection();
709  viewport()->update();
710  }
711  }
712 
713 
714  void TableViewContent::moveActiveCellLeft() {
715  m_activeCell->second = qMax(1, m_activeCell->second - 1);
716  int leftMostVisibleCol = getColumnFromScreenX(0);
717  if (leftMostVisibleCol == m_activeCell->second) {
718  horizontalScrollBar()->setValue(horizontalScrollBar()->value() -
719  m_columns->getVisibleColumns()[m_activeCell->second]->getWidth());
720  }
721 
722  clearColumnSelection();
723  viewport()->update();
724  }
725 
726 
727  void TableViewContent::moveActiveCellRight() {
728  m_activeCell->second = qMin(m_columns->getVisibleColumns().size() - 1,
729  m_activeCell->second + 1);
730  int rightMostVisibleCol = getColumnFromScreenX(viewport()->width());
731  if (rightMostVisibleCol == m_activeCell->second) {
732  horizontalScrollBar()->setValue(horizontalScrollBar()->value() +
733  m_columns->getVisibleColumns()[m_activeCell->second]->getWidth());
734  }
735 
736  clearColumnSelection();
737  viewport()->update();
738  }
739 
740 
741  void TableViewContent::paintEvent(QPaintEvent *event) {
742  ASSERT(m_model);
743  ASSERT(m_columns);
744  if (m_model && m_columns) {
745  // int startRow = verticalScrollBar()->value();
746  int rowCount = (int) ceil(viewport()->height() / (double) m_rowHeight);
747 
748  QPainter painter(viewport());
749  painter.setRenderHint(QPainter::Antialiasing, false);
750  // painter.setRenderHints(QPainter::NonCosmeticDefaultPen, true);
751 
752  bool editWidgetVisible = false;
753  for (int i = 0; i < rowCount; i++) {
754  // define the top left corner of the row and also how big the row is
755  QPoint relativeTopLeft(0, i * m_rowHeight);
756  QPoint scrollBarPos(horizontalScrollBar()->value(),
757  verticalScrollBar()->value());
758  QPoint absoluteTopLeft(relativeTopLeft + scrollBarPos);
759  QSize rowSize(viewport()->width(), (int) m_rowHeight);
760 
761  // Fill in the background with the background color
762  painter.fillRect(QRect(relativeTopLeft, rowSize), palette().base());
763 
764  if (i < m_items->size()) {
765 
766 
767 
768 
769 
770 
771  // ********************************************************
772  // HACK: FIXME: ask tree m_items if background needs to be
773  // darkened, instead figuring that out here. Also, change
774  // composition mode so that ref measure rows look different
775  // when they are highlighted as well
776  /*
777  if (m_items->at(i)->getPointerType() == AbstractTreeItem::Measure)
778  {
779  ControlMeasure * cm = (ControlMeasure *) m_items->at(i)->getPointer();
780  if (cm->Parent()->GetRefMeasure() == cm)
781  {
782  QPoint selectionTopLeft(-absoluteTopLeft.x(), relativeTopLeft.y());
783  QSize selectionSize(m_columns->getVisibleWidth(), (int) m_rowHeight);
784 
785  QRect selectionRect(selectionTopLeft, selectionSize);
786  QColor color = palette().base().color();
787  painter.fillRect(selectionRect, color.darker(110));
788  }
789  }
790  */
791  //*********************************************************
792 
793 
794 
795 
796 
797 
798 
799 
800 
801 
802 
803 
804  if (m_items->at(i)->isSelected()) {
805  QPoint selectionTopLeft(-absoluteTopLeft.x(), relativeTopLeft.y());
806  QSize selectionSize(m_columns->getVisibleWidth(), (int) m_rowHeight);
807 
808  QRect selectionRect(selectionTopLeft, selectionSize);
809  painter.fillRect(selectionRect, palette().highlight().color());
810  }
811 
812  paintRow(&painter, i, absoluteTopLeft, relativeTopLeft);
813  }
814  }
815 
816  for (int i = 0; i < rowCount; i++) {
817  if (i < m_items->size()) {
818  QPoint relativeTopLeft(0, i * m_rowHeight);
819  if (m_editWidget && m_activeCell->first == m_items->at(i)) {
820  QPair<int, int> xRange(
821  m_columns->getVisibleXRange(m_activeCell->second));
822 
823  m_editWidget->move(
824  QPoint(xRange.first - horizontalScrollBar()->value() - 1,
825  relativeTopLeft.y() + 1));
826  m_editWidget->resize(xRange.second - xRange.first, m_rowHeight + 1);
827  m_editWidget->setVisible(true);
828  editWidgetVisible = true;
829  }
830  else {
831  if (m_activeCell->first == m_items->at(i)) {
832  QPair<int, int> activeXArea =
833  m_columns->getVisibleXRange(m_activeCell->second);
834 
835  QRect activeArea(activeXArea.first, relativeTopLeft.y(),
836  activeXArea.second - activeXArea.first, m_rowHeight);
837 
838  activeArea.moveLeft(
839  activeArea.left() - horizontalScrollBar()->value());
840  activeArea.adjust(-1, -1, -2, -1);
841  QPen pen(Qt::black);
842  pen.setWidth(3);
843  painter.setPen(pen);
844  painter.drawRect(activeArea);
845  }
846  }
847  }
848  }
849 
850  if (m_editWidget && !editWidgetVisible)
851  m_editWidget->setVisible(false);
852  }
853  else {
854  QWidget::paintEvent(event);
855  }
856  }
857 
858 
859  void TableViewContent::resizeEvent(QResizeEvent *event) {
860  QAbstractScrollArea::resizeEvent(event);
861  updateHorizontalScrollBar();
862  updateItemList();
863  }
864 
865 
866  void TableViewContent::scrollContentsBy(int dx, int dy) {
867  QAbstractScrollArea::scrollContentsBy(dx, dy);
868  updateItemList();
869  }
870 
871 
872  void TableViewContent::nullify() {
873  m_parentView = NULL;
874  m_model = NULL;
875  m_items = NULL;
876  m_activeCell = NULL;
877  m_lastShiftArrowSelectedCell = NULL;
878  m_editWidget = NULL;
880  m_lastShiftSelection = NULL;
881  m_mousePressPos = NULL;
882  m_columns = NULL;
883  rowsWithActiveColumnSelected = NULL;
884  m_applyToSelectionAct = NULL;
885  m_applyToAllAct = NULL;
887  }
888 
889 
890  void TableViewContent::cellDataChanged(const TableColumn *col) {
891  if (col->hasNetworkStructureEffect())
892  emit rebuildModels(QList< AbstractTreeItem * >());
893 
894  emit modelDataChanged();
895  }
896 
897 
898  void TableViewContent::clearActiveCell() {
899  m_activeCell->first = NULL;
900  m_activeCell->second = -1;
901  }
902 
903 
904  void TableViewContent::clearColumnSelection() {
905  ASSERT(m_lastShiftArrowSelectedCell);
906  m_lastShiftArrowSelectedCell->first = NULL;
907  rowsWithActiveColumnSelected->clear();
908  }
909 
910 
911  void TableViewContent::copyCellSelection(bool allCells) {
912  if (hasActiveCell()) {
913  TableColumn *col = m_columns->getVisibleColumns()[m_activeCell->second];
914 
915  QString colTitle = col->getTitle();
916  ASSERT(colTitle.size());
917 
918  // Grab the active cell's data and copy it to the selected cells that are
919  // in the same column as the active cell.
920  QString cellData = m_activeCell->first->getFormattedData(colTitle);
921 
922  QList< AbstractTreeItem * > selection = allCells ? m_model->getItems(
923  0, m_model->getVisibleRowCount()) : *rowsWithActiveColumnSelected;
924  ASSERT(selection.size());
925 
926  bool needsDialog = true;
927  bool done = false;
928  for (int i = 0; !done && i < selection.size(); i++) {
929  AbstractTreeItem *row = selection[i];
930  bool changeData = true;
931 
932  QString warningText =
933  m_model->getWarningMessage(row, col, cellData);
934  if (needsDialog && warningText.size()) {
935  QMessageBox::StandardButton status = QMessageBox::warning(
936  this, "Change cells?", warningText, QMessageBox::Yes |
937  QMessageBox::No | QMessageBox::YesToAll | QMessageBox::NoToAll);
938 
939  switch (status) {
940  case QMessageBox::YesToAll:
941  needsDialog = false;
942  break;
943  case QMessageBox::NoToAll:
944  done = true;
945  case QMessageBox::No:
946  changeData = false;
947  default:
948  ;
949  }
950  }
951 
952  if (changeData)
953  row->setData(colTitle, cellData);
954  }
955 
956  viewport()->update();
957  cellDataChanged(col);
958  }
959  }
960 
961 
962  void TableViewContent::createActions() {
963  m_applyToSelectionAct = new QAction(tr("Copy to selected cells"), this);
964  m_applyToSelectionAct->setStatusTip(tr("Copy the contents of this cell to the"
965  "selected cells"));
966  connect(m_applyToSelectionAct, SIGNAL(triggered()),
967  this, SLOT(copySelection()));
968 
969  m_applyToAllAct = new QAction(tr("Copy to all cells"), this);
970  m_applyToAllAct->setStatusTip(tr("Copy the contents of this cell to all"
971  "cells in the current column"));
972  connect(m_applyToAllAct, SIGNAL(triggered()),
973  this, SLOT(copyAll()));
974 
975  m_deleteSelectedRowsAct = new QAction(tr("Delete selected rows"), this);
976  m_deleteSelectedRowsAct->setStatusTip(
977  tr("Delete the currently selected rows"));
978  connect(m_deleteSelectedRowsAct, SIGNAL(triggered()),
979  this, SLOT(deleteSelectedRows()));
980  }
981 
982 
983  int TableViewContent::getColumnFromScreenX(int screenX) const {
984  int column = -1;
985 
986  for (int i = 0;
987  column == -1 && i < m_columns->getVisibleColumns().size();
988  i++) {
989  QPair<int, int> cellXRange(m_columns->getVisibleXRange(i));
990  int deltaX = -horizontalScrollBar()->value();
991 
992  if (cellXRange.first + deltaX < screenX &&
993  cellXRange.second + deltaX > screenX) {
994  column = i;
995  }
996  }
997 
998  return column;
999  }
1000 
1001 
1002  int TableViewContent::getRowFromScreenY(int screenY) const {
1003  int rowNum = -1;
1004  int calculatedRowNum = screenY / m_rowHeight;
1005 
1006  if (calculatedRowNum >= 0 && calculatedRowNum < m_items->size() &&
1007  screenY >= 0 && screenY <= viewport()->height())
1008  rowNum = calculatedRowNum;
1009 
1010  return rowNum;
1011  }
1012 
1013 
1014  bool TableViewContent::hasActiveCell() const {
1015  return (m_activeCell->first && m_activeCell->second >= 0);
1016  }
1017 
1018 
1019  bool TableViewContent::hasRowSelection() const {
1020  return (m_model->getSelectedItems().size());
1021  }
1022 
1023 
1024  bool TableViewContent::mouseInCellSelection(QPoint mousePos) const {
1025  int colNum = getColumnFromScreenX(mousePos.x());
1026  AbstractTreeItem *row = m_items->at(getRowFromScreenY(mousePos.y()));
1027 
1028  return (rowsWithActiveColumnSelected->contains(row) &&
1029  m_activeCell->second == colNum);
1030  }
1031 
1032 
1033  bool TableViewContent::mouseInRowSelection(QPoint mousePos) const {
1034  AbstractTreeItem *row = m_items->at(getRowFromScreenY(mousePos.y()));
1035 
1036  return (m_model->getSelectedItems().contains(row));
1037  }
1038 
1039 
1040  bool TableViewContent::rowIsValid(int rowNum) const {
1041  bool valid = false;
1042 
1043  if (rowNum >= 0 && rowNum < m_items->size())
1044  valid = true;
1045 
1046  return valid;
1047  }
1048 
1049 
1050  bool TableViewContent::columnIsValid(int colNum) const {
1051  bool valid = false;
1052 
1053  if (colNum >= 0 && colNum < m_columns->getVisibleColumns().size())
1054  valid = true;
1055 
1056  return valid;
1057  }
1058 
1059 
1060  bool TableViewContent::cellIsEditable(int rowNum, int colNum) const {
1061  ASSERT(rowNum >= 0 && rowNum < m_items->size());
1062  ASSERT(colNum >= 0 && colNum < m_columns->getVisibleColumns().size());
1063 
1064  bool editable = false;
1065 
1066  QString colName = m_columns->getVisibleColumns()[colNum]->getTitle();
1067  if (m_items->at(rowNum)->isSelectable() &&
1068  m_items->at(rowNum)->isDataEditable(colName) &&
1069  !m_columns->getVisibleColumns()[colNum]->isReadOnly())
1070  editable = true;
1071 
1072  return editable;
1073  }
1074 
1075 
1076  bool TableViewContent::isDataColumn(int colNum) const {
1077  return m_columns->getVisibleColumns()[colNum]->getTitle().size();
1078  }
1079 
1080 
1081  void TableViewContent::paintRow(QPainter *painter, int rowNum,
1082  QPoint absolutePosition, QPoint relativePosition) {
1083  ASSERT(m_items);
1084  ASSERT(rowNum >= 0 && rowNum < m_items->size());
1085 
1086  QPoint point(-absolutePosition.x(), relativePosition.y());
1087 
1088  AbstractTreeItem *item = (*m_items)[rowNum];
1089 
1090  // should always be true, but prevents segfault in case of bug
1091  if (item) {
1092  QPen originalPen = painter->pen();
1093 
1094  QPoint textPoint(point.x() + ITEM_INDENTATION,
1095  point.y() + ITEM_PADDING / 2);
1096 
1097  // finally draw the text
1098  int textHeight = m_rowHeight - ITEM_PADDING;
1099 
1100  QFontMetrics metrics(font());
1101 
1102  QPen gridPen(Qt::gray);
1103 
1104  ASSERT(m_columns);
1105  TableColumnList visibleCols = m_columns->getVisibleColumns();
1106  for (int i = 0; i < visibleCols.size(); i++) {
1107  // draw text
1108  QPair<int, int> cellXRange(visibleCols.getVisibleXRange(i));
1109  QRect cellRect(cellXRange.first, point.y(),
1110  cellXRange.second - cellXRange.first, m_rowHeight);
1111  cellRect.moveLeft(cellRect.left() - horizontalScrollBar()->value() - 1);
1112 
1113  QString columnTitle = visibleCols[i]->getTitle();
1114  QRect textRect(textPoint, QSize(cellRect.right() - textPoint.x(),
1115  textHeight));
1116  QString text;
1117  bool textCentered = false;
1118  if (!columnTitle.isEmpty()) {
1119  text = item->getFormattedData(columnTitle);
1120 
1121  if (rowsWithActiveColumnSelected->indexOf(item) != -1 &&
1122  m_activeCell->second == i) {
1123  // This cell is selected, so render it as such.
1124  if (m_activeCell->first != item) {
1125  painter->fillRect(cellRect,
1126  QBrush(palette().highlight().color()));
1127  painter->setPen(palette().highlightedText().color());
1128  }
1129  else {
1130  painter->setPen(palette().text().color());
1131  }
1132  }
1133  else {
1134  if (item->isSelected()) {
1135  painter->setPen(palette().highlightedText().color());
1136  }
1137  else {
1138  // If the current column is not editable (i.e. read-only), or it
1139  // is locked (think edit-locked control point or measure), then
1140  // it should be grayed out.
1141  //
1142  // NOTE: The following two lines provide for graying out of edit
1143  // locked data. This is commented out because this functionality
1144  // is not fully working yet.
1145  //if (!cellIsEditable(rowNum, i) ||
1146  // item->isDataLocked(columnTitle))
1147  if (!cellIsEditable(rowNum, i)) {
1148  painter->setPen(palette().color(QPalette::Disabled,
1149  QPalette::Text));
1150  }
1151  else {
1152  painter->setPen(palette().text().color());
1153  }
1154  }
1155  }
1156  }
1157  else {
1158  // Draw the row number.
1159  text = QString::number(rowNum + verticalScrollBar()->value() + 1);
1160  textCentered = true;
1161 
1162  // We need to paint the row number column.
1163  int x = cellRect.center().x();
1164  QLinearGradient gradient(x, cellRect.top(), x, cellRect.bottom());
1165 
1166  bool selected = item->isSelected();
1167  QColor color = selected ? palette().highlight().color() :
1168  palette().button().color();
1169 
1170  int adjustment = 110;
1171  gradient.setColorAt(0, color.lighter(adjustment));
1172  gradient.setColorAt(1, color.darker(adjustment));
1173  painter->fillRect(cellRect, gradient);
1174  if (selected)
1175  painter->setPen(palette().highlightedText().color());
1176  else
1177  painter->setPen(palette().text().color());
1178  }
1179 
1180  int flags = Qt::TextDontClip;
1181  if (textCentered)
1182  flags |= Qt::AlignCenter;
1183 
1184  // TODO this shouldn't be here... an item should be able to tell
1185  // whether or not it should be bolded.
1186  QFont normalFont = painter->font();
1187 
1188  if (item->getPointerType() == AbstractTreeItem::Measure) {
1189  ControlMeasure *cm = (ControlMeasure *) item->getPointer();
1190  if (cm && cm->Parent() && cm->Parent()->GetRefMeasure() == cm) {
1191  QFont boldFont(normalFont);
1192  boldFont.setBold(true);
1193  painter->setFont(boldFont);
1194  }
1195  }
1196 
1197  painter->drawText(textRect, flags,
1198  metrics.elidedText(text, Qt::ElideRight,
1199  textRect.width() - ITEM_INDENTATION));
1200  painter->setFont(normalFont);
1201 
1202  textPoint.setX(cellRect.right() + ITEM_INDENTATION);
1203  painter->setPen(originalPen);
1204 
1205  painter->setPen(gridPen);
1206  painter->drawLine(QPoint(cellRect.right(), point.y()),
1207  QPoint(cellRect.right(), point.y() + m_rowHeight));
1208  painter->setPen(originalPen);
1209  }
1210 
1211  int left = -horizontalScrollBar()->value() - 1;
1212  int right = m_columns->getVisibleWidth();
1213 
1214  gridPen.setWidth(2);
1215  painter->setPen(gridPen);
1216  painter->drawLine(QPoint(left, point.y() + m_rowHeight),
1217  QPoint(right, point.y() + m_rowHeight));
1218  painter->setPen(originalPen);
1219  }
1220  }
1221 
1222 
1223  void TableViewContent::updateActiveCell(QPoint screenPos) {
1224  if (m_editWidget && m_activeCell->first && m_activeCell->second >= 0) {
1225  try {
1226  const TableColumn *col =
1227  m_columns->getVisibleColumns()[m_activeCell->second];
1228  m_model->getDelegate()->saveData(m_editWidget, m_activeCell->first,
1229  col);
1230 
1231  cellDataChanged(col);
1232  }
1233  catch (IException &e) {
1234  QMessageBox::critical(this, "Failed to Set Data", e.what());
1235  }
1236  }
1237 
1238  int rowNum = getRowFromScreenY(screenPos.y());
1239  int oldActiveColumn = m_activeCell->second;
1240 
1241  clearActiveCell();
1242 
1243  if (rowNum >= 0) {
1244  AbstractTreeItem *item = (*m_items)[rowNum];
1245 
1246  TableColumnList visibleCols = m_columns->getVisibleColumns();
1247  for (int i = 0; i < visibleCols.size(); i++) {
1248  QPair<int, int> cellXRange(m_columns->getVisibleXRange(i));
1249  QRect cellRect(cellXRange.first, m_rowHeight * rowNum,
1250  cellXRange.second - cellXRange.first, m_rowHeight);
1251 
1252  cellRect.moveLeft(cellRect.left() - horizontalScrollBar()->value());
1253 
1254  if (cellRect.contains(screenPos) &&
1255  (oldActiveColumn != -1 || !visibleCols[i]->getTitle().isEmpty())) {
1256  m_activeCell->first = item;
1257  m_activeCell->second = i;
1258  }
1259  }
1260  }
1261 
1262  if (oldActiveColumn != m_activeCell->second) {
1263  clearColumnSelection();
1265  }
1266 
1267  clearColumnSelection();
1268  }
1269 
1270 
1271  void TableViewContent::updateColumnGroupSelection(AbstractTreeItem *item) {
1272  // delete current row selection
1273  foreach (AbstractTreeItem * row, *m_lastShiftSelection) {
1274  if (rowsWithActiveColumnSelected->indexOf(row) != -1)
1275  rowsWithActiveColumnSelected->removeOne(row);
1276  }
1277 
1278 
1280  *m_lastShiftSelection = m_model->getItems(
1282  }
1283  else {
1284  m_lastShiftSelection->clear();
1285  }
1286 
1287  foreach (AbstractTreeItem * row, *m_lastShiftSelection) {
1288  if (rowsWithActiveColumnSelected->indexOf(row) == -1)
1289  rowsWithActiveColumnSelected->append(row);
1290  }
1291  }
1292 
1293 
1295  TableViewContent::updateRowGroupSelection(int lastRow) {
1296  foreach (AbstractTreeItem * row, *m_lastShiftSelection) {
1297  if (row->getPointerType() == AbstractTreeItem::Point)
1298  foreach (AbstractTreeItem * child, row->getChildren())
1299  child->setSelected(false);
1300 
1301  if (row->getPointerType() == AbstractTreeItem::Measure)
1302  row->parent()->setSelected(false);
1303 
1304  row->setSelected(false);
1305  }
1306 
1308  *m_lastShiftSelection = m_model->getItems(
1309  m_lastDirectlySelectedRow, m_items->at(lastRow));
1310  }
1311  else {
1312  m_lastShiftSelection->clear();
1313  }
1314 
1315  QList< AbstractTreeItem * > newlySelectedItems;
1316  foreach (AbstractTreeItem * row, *m_lastShiftSelection) {
1317  row->setSelected(true);
1318 
1319  if (row->getPointerType() == AbstractTreeItem::Measure)
1320  row->parent()->setSelected(true);
1321 
1322  newlySelectedItems.append(row);
1323  }
1324 
1325  return newlySelectedItems;
1326  }
1327 
1328 
1329  void TableViewContent::copySelection() {
1330  copyCellSelection(false);
1331  }
1332 
1333 
1334  void TableViewContent::copyAll() {
1335  copyCellSelection(true);
1336  }
1337 
1338 
1339  void TableViewContent::deleteSelectedRows() {
1340  // Prompt the user for confirmation before deletion.
1341  QMessageBox::StandardButton status = QMessageBox::warning(
1342  this, "Delete row(s)?", "Delete selected row(s)?",
1343  QMessageBox::Yes | QMessageBox::No);
1344 
1345  if (status == QMessageBox::Yes) {
1346  // TODO should we store off the selected rows for efficiency?
1347  QList<AbstractTreeItem *> selectedRows = m_model->getSelectedItems();
1348 
1349  emit rebuildModels(selectedRows);
1350  emit modelDataChanged();
1351 
1352  m_lastShiftSelection->clear();
1353  }
1354  }
1355 
1356 
1357  void TableViewContent::updateItemList() {
1358  ASSERT(m_items);
1359 
1360  if (m_model) {
1361  int startRow = verticalScrollBar()->value();
1362  int rowCount = (int) ceil(viewport()->height() / (double) m_rowHeight);
1363  m_items->clear();
1364  foreach (AbstractTreeItem * item,
1365  m_model->getItems(startRow, startRow + rowCount)) {
1366  m_items->append(item);
1367  }
1368 
1369  viewport()->update();
1370  }
1371  }
1372 
1373 
1374  void TableViewContent::showContextMenu(QPoint mouseLocation) {
1375  QMenu contextMenu(this);
1376 
1377  // If there is a row selection, show a context menu if the user clicked
1378  // anywhere on any of the selected row(s).
1379  if (hasRowSelection() && mouseInRowSelection(mouseLocation)) {
1380  contextMenu.addAction(m_deleteSelectedRowsAct);
1381  contextMenu.exec(mapToGlobal(mouseLocation));
1382  }
1383 
1384  // Only show the context menu for cells if the user right-clicked on the
1385  // active cell.
1386  if (hasActiveCell() && mouseInCellSelection(mouseLocation)) {
1387  if (rowsWithActiveColumnSelected->size() > 1)
1388  contextMenu.addAction(m_applyToSelectionAct);
1389 
1390  contextMenu.addAction(m_applyToAllAct);
1391  contextMenu.exec(mapToGlobal(mouseLocation));
1392  }
1393  }
1394  }
1395 }