Isis 3 Programmer Reference
TableViewHeader.cpp
1 #include "IsisDebug.h"
2 
3 #include "TableViewHeader.h"
4 
5 #include <iostream>
6 
7 #include <QAbstractItemModel>
8 #include <QFontMetrics>
9 #include <QLabel>
10 #include <QLinearGradient>
11 #include <QLocale>
12 #include <QMessageBox>
13 #include <QMouseEvent>
14 #include <QPainter>
15 #include <QPen>
16 #include <QRect>
17 #include <QString>
18 #include <QVBoxLayout>
19 
20 #include "AbstractTableModel.h"
21 #include "AbstractTreeModel.h"
22 #include "TableColumn.h"
23 #include "TableColumnList.h"
24 #include "TableView.h"
25 
26 
27 namespace Isis {
34  nullify();
35 
36  m_horizontalOffset = 0;
37  m_filterProgress = 0;
38  m_filterProgressMin = 0;
39  m_filterProgressMax = 0;
40  m_rebuildProgress = 0;
41  m_rebuildProgressMin = 0;
42  m_rebuildProgressMax = 0;
43  m_sortProgress = 0;
44  m_sortProgressMin = 0;
45  m_sortProgressMax = 0;
46  m_visibleCount = -1;
47  m_totalCount = -1;
48 
49  m_clickedColumnEdge = -1;
50  m_clickedColumn = -1;
51 
52  setMouseTracking(true);
53 
54  setModel(someModel);
55 
56  ARROW_HEIGHT = 3;
57  ARROW_WIDTH = 5;
58  }
59 
60 
65  m_columns = NULL;
66  }
67 
68 
75  m_columns = cols;
76  }
77 
78 
85  return QSize(0, QFontMetrics(font()).height() + 6);
86  /*QFontMetrics(font()).width(text->join("")) + 15,
87  QFontMetrics(font()).height() + 6);*/
88  }
89 
90 
98  QSize TableViewHeader::sizeHint() const {
99  return minimumSizeHint();
100  }
101 
102 
109  if (m_model) {
110  disconnect(m_model, SIGNAL(filterProgressChanged(int)),
111  this, SLOT(updateFilterProgress(int)));
112  disconnect(m_model, SIGNAL(rebuildProgressChanged(int)),
113  this, SLOT(updateRebuildProgress(int)));
114  disconnect(m_model, SIGNAL(sortProgressChanged(int)),
115  this, SLOT(updateSortProgress(int)));
116  disconnect(m_model, SIGNAL(filterProgressRangeChanged(int, int)),
117  this, SLOT(updateFilterProgressRange(int, int)));
118  disconnect(m_model, SIGNAL(rebuildProgressRangeChanged(int, int)),
119  this, SLOT(updateRebuildProgressRange(int, int)));
120  disconnect(m_model, SIGNAL(sortProgressRangeChanged(int, int)),
121  this, SLOT(updateSortProgressRange(int, int)));
122  disconnect(m_model, SIGNAL(filterCountsChanged(int, int)),
123  this, SLOT(handleFilterCountsChanged(int, int)));
124  disconnect(this, SIGNAL(requestedGlobalSelection(bool)),
125  m_model, SLOT(setGlobalSelection(bool)));
126  disconnect(m_model, SIGNAL(m_modelModified()),
127  this, SLOT(update()));
128  }
129 
130  m_model = someModel;
131 
132  connect(m_model, SIGNAL(filterProgressChanged(int)),
133  this, SLOT(updateFilterProgress(int)));
134  connect(m_model, SIGNAL(rebuildProgressChanged(int)),
135  this, SLOT(updateRebuildProgress(int)));
136  connect(m_model, SIGNAL(sortProgressChanged(int)),
137  this, SLOT(updateSortProgress(int)));
138  connect(m_model, SIGNAL(filterProgressRangeChanged(int, int)),
139  this, SLOT(updateFilterProgressRange(int, int)));
140  connect(m_model, SIGNAL(rebuildProgressRangeChanged(int, int)),
141  this, SLOT(updateRebuildProgressRange(int, int)));
142  connect(m_model, SIGNAL(sortProgressRangeChanged(int, int)),
143  this, SLOT(updateSortProgressRange(int, int)));
144  connect(m_model, SIGNAL(filterCountsChanged(int, int)),
145  this, SLOT(handleFilterCountsChanged(int, int)));
146  connect(this, SIGNAL(requestedGlobalSelection(bool)),
147  m_model, SLOT(setGlobalSelection(bool)));
148  connect(m_model, SIGNAL(modelModified()), this, SLOT(update()));
149 
150 
151  if (m_columns) {
152  for (int i = 0; i < m_columns->size(); i++) {
153  disconnect((*m_columns)[i], SIGNAL(visibilityChanged()),
154  this, SLOT(update()));
155  }
156  }
157 
158  m_columns = m_model->getColumns();
159 
160  for (int i = 0; i < m_columns->size(); i++)
161  connect((*m_columns)[i], SIGNAL(visibilityChanged()), this, SLOT(update()));
162  }
163 
164 
172  int visibleTopLevelItemCount, int topLevelItemCount) {
173  m_visibleCount = visibleTopLevelItemCount;
174  m_totalCount = topLevelItemCount;
175 
176  if (m_visibleCount >= 0) {
177  TableColumnList visibleCols = m_columns->getVisibleColumns();
178  for (int i = 0; i < visibleCols.size(); i++) {
179  TableColumn *& col = visibleCols[i];
180 
181  if (col->getTitle().isEmpty())
182  col->setWidth(QFontMetrics(font()).width(
183  QString::number(m_visibleCount)) + 22);
184  }
185  }
186 
187  updateGeometry();
188  update();
189  }
190 
191 
198  m_horizontalOffset = newOffset;
199  update();
200  }
201 
202 
208  void TableViewHeader::mousePressEvent(QMouseEvent *event) {
209  QPoint mousePos = event->pos();
210 
211  m_clickedColumn = getMousedColumn(mousePos);
212 
213  // QRect priorityRect = getSortingPriorityRect(m_clickedColumn);
214  // QRect arrowRect = getSortingArrowRect(m_clickedColumn);
215 
216 
217  if (event->buttons() == Qt::LeftButton) {
218  m_clickedColumnEdge = getMousedColumnEdge(mousePos);
219  if (m_clickedColumnEdge == -1 && m_clickedColumn != -1) {
220  // The click wasn't on a column edge.
221  if (m_columns->getVisibleColumns()[m_clickedColumn]->getTitle().isEmpty()) {
222  emit requestedGlobalSelection(true);
223  }
224  else {
225  // if (priorityRect.contains(mousePos))
226  // {
227  // emit requestedColumnSelection(m_clickedColumn, true);
228  // }
229 
230  }
231  }
232  }
233  }
234 
235 
241  void TableViewHeader::mouseMoveEvent(QMouseEvent *event) {
242  QPoint mousePos = event->pos();
243 
244  if (m_clickedColumnEdge >= 0) {
245  // edge == column that we want to resize
246  QRect columnToResizeRect(getColumnRect(m_clickedColumnEdge));
247  columnToResizeRect.setRight(mousePos.x());
248 
249  TableColumn *col = m_columns->getVisibleColumns()[m_clickedColumnEdge];
250 
251  int newWidth = 1;
252  if (columnToResizeRect.width() > 1) {
253  newWidth = columnToResizeRect.width();
254  if (m_columns->getSortingOrder()[0] == col)
255  newWidth = qMax(newWidth, ARROW_WIDTH + SORT_ARROW_MARGIN * 2);
256  }
257 
258  m_columns->getVisibleColumns()[m_clickedColumnEdge]->setWidth(newWidth);
259  }
260 
261  if (mouseAtResizableColumnEdge(mousePos)) {
262  setCursor(Qt::SizeHorCursor);
263  }
264  else {
265  setCursor(Qt::ArrowCursor);
266  }
267 
268  update();
269  }
270 
271 
277  void TableViewHeader::mouseReleaseEvent(QMouseEvent *event) {
278  bool wasLastCol =
279  m_clickedColumnEdge >= m_columns->getVisibleColumns().size() - 2;
280  if (m_clickedColumnEdge != -1) {
281  emit columnResized(wasLastCol);
282  }
283  else {
284  if (m_clickedColumn == getMousedColumn(event->pos())) {
285  TableColumn *col = m_columns->getVisibleColumns()[m_clickedColumn];
286 
287  TableColumn const *sortCol =
288  m_columns->getVisibleColumns().getSortingOrder()[0];
289 
290  if (col == sortCol)
291  col->setSortAscending(!col->sortAscending());
292  else
293  m_columns->raiseToTop(col);
294 
295  if (!m_model->sortingOn()) {
296  QMessageBox::information(this, tr("Sorting Disabled"),
297  tr("Sorting is currently disabled for this table. Please configure your sorting "
298  "options before trying to sort by [<font color='red'>%1</font>].")
299  .arg(col->getTitle()),
300  QMessageBox::Ok);
301  }
302  }
303  }
304 
305  m_clickedColumnEdge = -1;
306  m_clickedColumn = -1;
307 
308  update();
309  }
310 
311 
317  void TableViewHeader::paintEvent(QPaintEvent *event) {
318  QPainter painter(this);
319  painter.setRenderHints(QPainter::Antialiasing |
320  QPainter::TextAntialiasing);
321 
322  ARROW_HEIGHT = qMax(height() / 5, 3);
323  ASSERT(height() > 8);
324  ARROW_WIDTH = ARROW_HEIGHT * 2 - 1;
325 
326  paintHeader(&painter, height());
327  // painter.drawRect(0, 0, width(), height());
328  painter.end();
329  }
330 
331 
336  m_model = NULL;
337  m_columns = NULL;
338  }
339 
340 
348  QRect TableViewHeader::getColumnRect(int column) const {
349  QRect colRect;
350 
351  TableColumnList visibleCols = m_columns->getVisibleColumns();
352 
353  if (column < visibleCols.size() && column >= 0) {
354  int indent = 1;
355 
356  for (int i = 0; i < column; i++)
357  indent += visibleCols[i]->getWidth() - 1;
358 
359  colRect = QRect(indent - m_horizontalOffset, 0,
360  visibleCols[column]->getWidth(), height());
361  }
362 
363  return colRect;
364  }
365 
366 
374  int TableViewHeader::getMousedColumn(QPoint mousePos) {
375  int mousedColumn = -1;
376 
377  for (int i = 0;
378  i < m_columns->getVisibleColumns().size() && mousedColumn < 0;
379  i++) {
380  QRect columnRect(getColumnRect(i));
381  if (columnRect.contains(mousePos))
382  mousedColumn = i;
383  }
384 
385  return mousedColumn;
386  }
387 
388 
397  int edge = -1;
398 
399  if (mouseAtResizableColumnEdge(mousePos)) {
400  int mousedColumn = getMousedColumn(mousePos);
401 
402  QRect columnRect(getColumnRect(mousedColumn));
403 
404  // mouseAtResizableColumnEdge can't be on left of first so this won't
405  // return -1 resulting from this particular logic.
406  if (mousePos.x() - columnRect.left() < TableColumn::EDGE_WIDTH)
407  edge = mousedColumn - 1;
408  else
409  edge = mousedColumn;
410  }
411 
412  return edge;
413  }
414 
415 
424  int columnNum = getMousedColumn(mousePos);
425 
426  QRect columnRect(getColumnRect(columnNum));
427 
428  bool isAtColumnEdge = false;
429 
430  if (!columnRect.isNull()) {
431  bool isOnLeft =
432  mousePos.x() - columnRect.left() < TableColumn::EDGE_WIDTH;
433  bool isOnRight =
434  columnRect.right() - mousePos.x() < TableColumn::EDGE_WIDTH;
435  bool isResizable = false;
436 
437  TableColumnList visCols = m_columns->getVisibleColumns();
438  if (isOnLeft && columnNum > 0)
439  isResizable = visCols[columnNum - 1]->getTitle().size();
440  else if (isOnRight)
441  isResizable = visCols[columnNum]->getTitle().size();
442 
443  isAtColumnEdge = (isOnLeft || isOnRight) && isResizable;
444  }
445 
446  return isAtColumnEdge;
447  }
448 
449 
456  void TableViewHeader::paintHeader(QPainter *painter, int rowHeight) {
457  int visibleColWidth = -m_horizontalOffset;
458  TableColumnList visibleCols = m_columns->getVisibleColumns();
459 
460  for (int i = 0; i < visibleCols.size(); i++)
461  visibleColWidth += visibleCols[i]->getWidth() - 1;
462 
463  QRect rect(0, 0, qMin(width(), visibleColWidth), rowHeight);
464 
465  int x = rect.center().x();
466  QLinearGradient gradient(x, rect.top(), x, rect.bottom());
467 
468  //FIXME: selected needs to be member variable
469  bool selected = false;
470  QColor color = selected ? palette().highlight().color() :
471  palette().button().color();
472 
473  // create gradient and fill header area with it
474  int adjustment = 110;
475  gradient.setColorAt(0, color.lighter(adjustment));
476  gradient.setColorAt(1, color.darker(adjustment));
477  painter->fillRect(rect, gradient);
478 
479  // Save off composition mode and brush, which will need to be restored
480  // after the progress is painted.
481  QBrush brush = painter->brush();
482  QPainter::CompositionMode compMode = painter->compositionMode();
483  painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
484 
485  // draw filter progress (if not at 100%)
486  painter->setBrush(QBrush(QColor(0, 70, 100, 30)));
487  paintProgress(painter, rect, m_filterProgressMin, m_filterProgressMax,
488  m_filterProgress, false);
489 
490  // draw rebuild progress (if not at 100%)
491  painter->setBrush(QBrush(QColor(100, 70, 0, 30)));
492  paintProgress(painter, rect, m_rebuildProgressMin, m_rebuildProgressMax,
493  m_rebuildProgress, false);
494 
495  // draw sort progress (if not at 100%)
496  painter->setBrush(QBrush(QColor(0, 100, 0, 30)));
497  paintProgress(painter, rect, m_sortProgressMin, m_sortProgressMax,
498  m_sortProgress, true);
499 
500  // draw the header's text. A rect will be drawn for each of the m_columns,
501  // with the column text in the center of it.
502  painter->setBrush(brush);
503  painter->setCompositionMode(compMode);
504 
505  for (int i = 0; i < visibleCols.size(); i++) {
506  TableColumn *visibleCol = visibleCols[i];
507 
508  QString columnText = visibleCol->getTitle();
509  QRect columnRect(getColumnRect(visibleCols.indexOf(visibleCol)));
510  QPen pen(palette().dark().color().darker(150));
511  pen.setCapStyle(Qt::RoundCap);
512  painter->setPen(pen);
513  painter->drawLine(columnRect.topLeft() + QPoint(0, 1),
514  columnRect.bottomLeft() + QPoint(0, 1));
515 
516  painter->drawLine(columnRect.topLeft() + QPoint(1, 0),
517  columnRect.topRight() - QPoint(0, 0));
518 
519  painter->drawLine(columnRect.topLeft() + QPoint(1, 1),
520  columnRect.topRight() + QPoint(0, 1));
521 
522  painter->drawLine(columnRect.bottomLeft() + QPoint(1, 1),
523  columnRect.bottomRight() + QPoint(0, 1));
524 
525  painter->drawLine(columnRect.bottomLeft() + QPoint(1, 1),
526  columnRect.bottomRight() + QPoint(0, 1));
527 
528  painter->drawLine(columnRect.topRight() + QPoint(0, 1),
529  columnRect.bottomRight() - QPoint(0, 0));
530 
531  painter->setPen(selected ? palette().highlightedText().color() :
532  palette().buttonText().color());
533 
534  QRect textRect(columnRect.x(),
535  columnRect.y(),
536  columnRect.width() - (SORT_ARROW_MARGIN * 2 + ARROW_WIDTH),
537  columnRect.height());
538  painter->drawText(textRect , Qt::AlignCenter | Qt::TextSingleLine,
539  columnText);
540 
541  if (visibleCol == visibleCols.getSortingOrder()[0] &&
542  visibleCol->getWidth() >= SORT_ARROW_MARGIN * 2 + ARROW_WIDTH) {
543  ASSERT(SORT_ARROW_MARGIN > 0);
544 
545  QRect arrowRect(textRect.right() + 1,
546  textRect.y(),
547  SORT_ARROW_MARGIN * 2 + ARROW_WIDTH,
548  textRect.height());
549 
550  ASSERT(arrowRect.width() + textRect.width() == columnRect.width());
551  ASSERT(arrowRect.right() == columnRect.right());
552 
553 
554  // assume ascending order (arrow looks like v)
555  QPoint left(arrowRect.left() + SORT_ARROW_MARGIN,
556  arrowRect.center().y() - ((ARROW_HEIGHT - 1) / 2));
557 
558  int yOffSet = ((ARROW_HEIGHT - 1) / 2);
559  if (ARROW_HEIGHT % 2 == 0)
560  yOffSet++;
561  QPoint center(left.x() + ((ARROW_WIDTH - 1) / 2),
562  arrowRect.center().y() + yOffSet);
563 
564  QPoint right(center.x() + ((ARROW_WIDTH - 1) / 2),
565  arrowRect.center().y() - ((ARROW_HEIGHT - 1) / 2));
566 
567  ASSERT(right.x() == arrowRect.right() - SORT_ARROW_MARGIN);
568  ASSERT(right.x() - center.x() == center.x() - left.x());
569 
570  if (!visibleCol->sortAscending()) {
571  // flip arrow (to look like ^)
572  left.setY(center.y());
573  center.setY(right.y());
574  right.setY(left.y());
575  }
576 
577  if (m_model->sortingOn()) {
578  painter->drawLine(left, center);
579  painter->drawLine(center, right);
580  }
581  }
582 
583  // Move the column rect to the position of the next column.
584  columnRect.moveLeft(columnRect.right());
585  }
586  }
587 
588 
599  void TableViewHeader::paintProgress(QPainter *painter,
600  const QRect &rect, int min, int max, int value, bool over100) {
601  double progressPercent = 1.0;
602  int progressRange = max - min;
603 
604  if (progressRange > 0)
605  progressPercent = ((double)(value - min)) / progressRange;
606  else if (progressRange == 0)
607  progressPercent = 0;
608 
609  if (progressPercent < 1.0 || over100) {
610  QRect progressRect(rect);
611  progressRect.setWidth((int)(progressRect.width() * progressPercent));
612  painter->fillRect(progressRect, painter->brush());
613  }
614  }
615 
616 
623  m_filterProgress = newProgress;
624  update();
625  }
626 
627 
635  m_filterProgressMin = min;
636  m_filterProgressMax = max;
637  update();
638  }
639 
640 
647  m_rebuildProgress = newProgress;
648  update();
649  }
650 
651 
659  m_rebuildProgressMin = min;
660  m_rebuildProgressMax = max;
661  update();
662  }
663 
664 
670  void TableViewHeader::updateSortProgress(int newProgress) {
671  m_sortProgress = newProgress;
672  update();
673  }
674 
675 
683  m_sortProgressMin = min;
684  m_sortProgressMax = max;
685  update();
686  }
687 }
void paintHeader(QPainter *painter, int rowheight)
Repaints the header.
int getMousedColumn(QPoint mousePos)
Returns the column under the mouse.
void updateFilterProgress(int newProgress)
Updates the current filter progress value.
void updateFilterProgressRange(int min, int max)
Updates the range of the filter progress.
QSize minimumSizeHint() const
Returns the minimum size based on the font.
void handleFilterCountsChanged(int visibleTopLevelItemCount, int topLevelItemCount)
Updates the visible columns, and geometry when the filter count changes.
void updateRebuildProgressRange(int min, int max)
Updates the range of the rebuild progress.
void updateHeaderOffset(int)
Updates the header offset.
void nullify()
Sets all the member variables to NULL.
void mousePressEvent(QMouseEvent *event)
Overrides QWidget::mousePressEvent.
int getMousedColumnEdge(QPoint mousePos)
Returns the edge of the column under the mouse.
void setModel(AbstractTableModel *someModel)
Connects the table model to the functions that handle changes.
bool mouseAtResizableColumnEdge(QPoint mousePos)
Returns if the mouse is at the edge of a resizeable column.
virtual ~TableViewHeader()
Destructor.
Translates the tree model into a table model.
void updateSortProgressRange(int min, int max)
Updates the range of the sort progress.
QRect getColumnRect(int column) const
Returns the visible column rectangle.
void mouseMoveEvent(QMouseEvent *event)
Overrides QWidget::mouseMoveEvent.
virtual void setColumns(TableColumnList *)
Sets the column list.
void updateSortProgress(int newProgress)
Updates the current sort progress value.
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
QSize sizeHint() const
Returns the minimum size based on the font.
void updateRebuildProgress(int newProgress)
Updates the current rebuild progress value.
void mouseReleaseEvent(QMouseEvent *event)
Overrides QWidget::mouseReleaseEvent.
void paintProgress(QPainter *painter, const QRect &rect, int min, int max, int value, bool over100)
Updates the progress bar.
TableViewHeader(AbstractTableModel *someModel)
Constructor.
void paintEvent(QPaintEvent *event)
Repaints the header.