Isis 3.0 Programmer Reference
Back | Home
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 "TableColumn.h"
21 #include "TableColumnList.h"
22 #include "TableView.h"
23 #include "AbstractTreeModel.h"
24 #include "AbstractTableModel.h"
25 
26 
27 namespace Isis {
28  namespace CnetViz {
29  TableViewHeader::TableViewHeader(AbstractTableModel *someModel) {
30  nullify();
31 
32  m_horizontalOffset = 0;
33  m_filterProgress = 0;
34  m_filterProgressMin = 0;
35  m_filterProgressMax = 0;
36  m_rebuildProgress = 0;
37  m_rebuildProgressMin = 0;
38  m_rebuildProgressMax = 0;
39  m_sortProgress = 0;
40  m_sortProgressMin = 0;
41  m_sortProgressMax = 0;
42  m_visibleCount = -1;
43  m_totalCount = -1;
44 
45  m_clickedColumnEdge = -1;
46  m_clickedColumn = -1;
47 
48  setMouseTracking(true);
49 
50  setModel(someModel);
51 
52  ARROW_HEIGHT = 3;
53  ARROW_WIDTH = 5;
54  }
55 
56 
57  TableViewHeader::~TableViewHeader() {
58  m_columns = NULL;
59  }
60 
61 
62  void TableViewHeader::setColumns(TableColumnList *cols) {
63  m_columns = cols;
64  }
65 
66 
67  QSize TableViewHeader::minimumSizeHint() const {
68  return QSize(0, QFontMetrics(font()).height() + 6);
69  /*QFontMetrics(font()).width(text->join("")) + 15,
70  QFontMetrics(font()).height() + 6);*/
71  }
72 
73 
74  QSize TableViewHeader::sizeHint() const {
75  return minimumSizeHint();
76  }
77 
78 
79  void TableViewHeader::setModel(AbstractTableModel *someModel) {
80  if (m_model) {
81  disconnect(m_model, SIGNAL(filterProgressChanged(int)),
82  this, SLOT(updateFilterProgress(int)));
83  disconnect(m_model, SIGNAL(rebuildProgressChanged(int)),
84  this, SLOT(updateRebuildProgress(int)));
85  disconnect(m_model, SIGNAL(sortProgressChanged(int)),
86  this, SLOT(updateSortProgress(int)));
87  disconnect(m_model, SIGNAL(filterProgressRangeChanged(int, int)),
88  this, SLOT(updateFilterProgressRange(int, int)));
89  disconnect(m_model, SIGNAL(rebuildProgressRangeChanged(int, int)),
90  this, SLOT(updateRebuildProgressRange(int, int)));
91  disconnect(m_model, SIGNAL(sortProgressRangeChanged(int, int)),
92  this, SLOT(updateSortProgressRange(int, int)));
93  disconnect(m_model, SIGNAL(filterCountsChanged(int, int)),
94  this, SLOT(handleFilterCountsChanged(int, int)));
95  disconnect(this, SIGNAL(requestedGlobalSelection(bool)),
96  m_model, SLOT(setGlobalSelection(bool)));
97  disconnect(m_model, SIGNAL(m_modelModified()),
98  this, SLOT(update()));
99  }
100 
101  m_model = someModel;
102 
103  connect(m_model, SIGNAL(filterProgressChanged(int)),
104  this, SLOT(updateFilterProgress(int)));
105  connect(m_model, SIGNAL(rebuildProgressChanged(int)),
106  this, SLOT(updateRebuildProgress(int)));
107  connect(m_model, SIGNAL(sortProgressChanged(int)),
108  this, SLOT(updateSortProgress(int)));
109  connect(m_model, SIGNAL(filterProgressRangeChanged(int, int)),
110  this, SLOT(updateFilterProgressRange(int, int)));
111  connect(m_model, SIGNAL(rebuildProgressRangeChanged(int, int)),
112  this, SLOT(updateRebuildProgressRange(int, int)));
113  connect(m_model, SIGNAL(sortProgressRangeChanged(int, int)),
114  this, SLOT(updateSortProgressRange(int, int)));
115  connect(m_model, SIGNAL(filterCountsChanged(int, int)),
116  this, SLOT(handleFilterCountsChanged(int, int)));
117  connect(this, SIGNAL(requestedGlobalSelection(bool)),
118  m_model, SLOT(setGlobalSelection(bool)));
119  connect(m_model, SIGNAL(modelModified()), this, SLOT(update()));
120 
121 
122  if (m_columns) {
123  for (int i = 0; i < m_columns->size(); i++) {
124  disconnect((*m_columns)[i], SIGNAL(visibilityChanged()),
125  this, SLOT(update()));
126  }
127  }
128 
129  m_columns = m_model->getColumns();
130 
131  for (int i = 0; i < m_columns->size(); i++)
132  connect((*m_columns)[i], SIGNAL(visibilityChanged()), this, SLOT(update()));
133  }
134 
135 
136  void TableViewHeader::handleFilterCountsChanged(
137  int visibleTopLevelItemCount, int topLevelItemCount) {
138  m_visibleCount = visibleTopLevelItemCount;
139  m_totalCount = topLevelItemCount;
140 
141  if (m_visibleCount >= 0) {
142  TableColumnList visibleCols = m_columns->getVisibleColumns();
143  for (int i = 0; i < visibleCols.size(); i++) {
144  TableColumn *& col = visibleCols[i];
145 
146  if (col->getTitle().isEmpty())
147  col->setWidth(QFontMetrics(font()).width(
148  QString::number(m_visibleCount)) + 22);
149  }
150  }
151 
152  updateGeometry();
153  update();
154  }
155 
156 
157  void TableViewHeader::updateHeaderOffset(int newOffset) {
158  m_horizontalOffset = newOffset;
159  update();
160  }
161 
162 
163  void TableViewHeader::mousePressEvent(QMouseEvent *event) {
164  QPoint mousePos = event->pos();
165 
166  m_clickedColumn = getMousedColumn(mousePos);
167 
168  // QRect priorityRect = getSortingPriorityRect(m_clickedColumn);
169  // QRect arrowRect = getSortingArrowRect(m_clickedColumn);
170 
171 
172  if (event->buttons() == Qt::LeftButton) {
173  m_clickedColumnEdge = getMousedColumnEdge(mousePos);
174  if (m_clickedColumnEdge == -1 && m_clickedColumn != -1) {
175  // The click wasn't on a column edge.
176  if (m_columns->getVisibleColumns()[m_clickedColumn]->getTitle().isEmpty()) {
177  emit requestedGlobalSelection(true);
178  }
179  else {
180  // if (priorityRect.contains(mousePos))
181  // {
182  // emit requestedColumnSelection(m_clickedColumn, true);
183  // }
184 
185  }
186  }
187  }
188  }
189 
190 
191  void TableViewHeader::mouseMoveEvent(QMouseEvent *event) {
192  QPoint mousePos = event->pos();
193 
194  if (m_clickedColumnEdge >= 0) {
195  // edge == column that we want to resize
196  QRect columnToResizeRect(getColumnRect(m_clickedColumnEdge));
197  columnToResizeRect.setRight(mousePos.x());
198 
199  TableColumn *col = m_columns->getVisibleColumns()[m_clickedColumnEdge];
200 
201  int newWidth = 1;
202  if (columnToResizeRect.width() > 1) {
203  newWidth = columnToResizeRect.width();
204  if (m_columns->getSortingOrder()[0] == col)
205  newWidth = qMax(newWidth, ARROW_WIDTH + SORT_ARROW_MARGIN * 2);
206  }
207 
208  m_columns->getVisibleColumns()[m_clickedColumnEdge]->setWidth(newWidth);
209  }
210 
211  if (mouseAtResizableColumnEdge(mousePos)) {
212  setCursor(Qt::SizeHorCursor);
213  }
214  else {
215  setCursor(Qt::ArrowCursor);
216  }
217 
218  update();
219  }
220 
221 
222  void TableViewHeader::mouseReleaseEvent(QMouseEvent *event) {
223  bool wasLastCol =
224  m_clickedColumnEdge >= m_columns->getVisibleColumns().size() - 2;
225  if (m_clickedColumnEdge != -1) {
226  emit columnResized(wasLastCol);
227  }
228  else {
229  if (m_clickedColumn == getMousedColumn(event->pos())) {
230  TableColumn *col = m_columns->getVisibleColumns()[m_clickedColumn];
231 
232  TableColumn const *sortCol =
233  m_columns->getVisibleColumns().getSortingOrder()[0];
234 
235  if (col == sortCol)
236  col->setSortAscending(!col->sortAscending());
237  else
238  m_columns->raiseToTop(col);
239 
240  if (!m_model->sortingOn()) {
241  QMessageBox::information(this, tr("Sorting Disabled"),
242  tr("Sorting is currently disabled for this table. Please configure your sorting "
243  "options before trying to sort by [<font color='red'>%1</font>].")
244  .arg(col->getTitle()),
245  QMessageBox::Ok);
246  }
247  }
248  }
249 
250  m_clickedColumnEdge = -1;
251  m_clickedColumn = -1;
252 
253  update();
254  }
255 
256 
257  void TableViewHeader::paintEvent(QPaintEvent *event) {
258  QPainter painter(this);
259  painter.setRenderHints(QPainter::Antialiasing |
260  QPainter::TextAntialiasing);
261 
262  ARROW_HEIGHT = qMax(height() / 5, 3);
263  ASSERT(height() > 8);
264  ARROW_WIDTH = ARROW_HEIGHT * 2 - 1;
265 
266  paintHeader(&painter, height());
267  // painter.drawRect(0, 0, width(), height());
268  painter.end();
269  }
270 
271 
272  void TableViewHeader::nullify() {
273  m_model = NULL;
274  m_columns = NULL;
275  }
276 
277 
278  QRect TableViewHeader::getColumnRect(int column) const {
279  QRect colRect;
280 
281  TableColumnList visibleCols = m_columns->getVisibleColumns();
282 
283  if (column < visibleCols.size() && column >= 0) {
284  int indent = 1;
285 
286  for (int i = 0; i < column; i++)
287  indent += visibleCols[i]->getWidth() - 1;
288 
289  colRect = QRect(indent - m_horizontalOffset, 0,
290  visibleCols[column]->getWidth(), height());
291  }
292 
293  return colRect;
294  }
295 
296 
297  int TableViewHeader::getMousedColumn(QPoint mousePos) {
298  int mousedColumn = -1;
299 
300  for (int i = 0;
301  i < m_columns->getVisibleColumns().size() && mousedColumn < 0;
302  i++) {
303  QRect columnRect(getColumnRect(i));
304  if (columnRect.contains(mousePos))
305  mousedColumn = i;
306  }
307 
308  return mousedColumn;
309  }
310 
311 
312  int TableViewHeader::getMousedColumnEdge(QPoint mousePos) {
313  int edge = -1;
314 
315  if (mouseAtResizableColumnEdge(mousePos)) {
316  int mousedColumn = getMousedColumn(mousePos);
317 
318  QRect columnRect(getColumnRect(mousedColumn));
319 
320  // mouseAtResizableColumnEdge can't be on left of first so this won't
321  // return -1 resulting from this particular logic.
322  if (mousePos.x() - columnRect.left() < TableColumn::EDGE_WIDTH)
323  edge = mousedColumn - 1;
324  else
325  edge = mousedColumn;
326  }
327 
328  return edge;
329  }
330 
331 
332  bool TableViewHeader::mouseAtResizableColumnEdge(QPoint mousePos) {
333  int columnNum = getMousedColumn(mousePos);
334 
335  QRect columnRect(getColumnRect(columnNum));
336 
337  bool isAtColumnEdge = false;
338 
339  if (!columnRect.isNull()) {
340  bool isOnLeft =
341  mousePos.x() - columnRect.left() < TableColumn::EDGE_WIDTH;
342  bool isOnRight =
343  columnRect.right() - mousePos.x() < TableColumn::EDGE_WIDTH;
344  bool isResizable = false;
345 
346  TableColumnList visCols = m_columns->getVisibleColumns();
347  if (isOnLeft && columnNum > 0)
348  isResizable = visCols[columnNum - 1]->getTitle().size();
349  else if (isOnRight)
350  isResizable = visCols[columnNum]->getTitle().size();
351 
352  isAtColumnEdge = (isOnLeft || isOnRight) && isResizable;
353  }
354 
355  return isAtColumnEdge;
356  }
357 
358 
359  void TableViewHeader::paintHeader(QPainter *painter, int rowHeight) {
360  int visibleColWidth = -m_horizontalOffset;
361  TableColumnList visibleCols = m_columns->getVisibleColumns();
362 
363  for (int i = 0; i < visibleCols.size(); i++)
364  visibleColWidth += visibleCols[i]->getWidth() - 1;
365 
366  QRect rect(0, 0, qMin(width(), visibleColWidth), rowHeight);
367 
368  int x = rect.center().x();
369  QLinearGradient gradient(x, rect.top(), x, rect.bottom());
370 
371  //FIXME: selected needs to be member variable
372  bool selected = false;
373  QColor color = selected ? palette().highlight().color() :
374  palette().button().color();
375 
376  // create gradient and fill header area with it
377  int adjustment = 110;
378  gradient.setColorAt(0, color.lighter(adjustment));
379  gradient.setColorAt(1, color.darker(adjustment));
380  painter->fillRect(rect, gradient);
381 
382  // Save off composition mode and brush, which will need to be restored
383  // after the progress is painted.
384  QBrush brush = painter->brush();
385  QPainter::CompositionMode compMode = painter->compositionMode();
386  painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
387 
388  // draw filter progress (if not at 100%)
389  painter->setBrush(QBrush(QColor(0, 70, 100, 30)));
390  paintProgress(painter, rect, m_filterProgressMin, m_filterProgressMax,
391  m_filterProgress, false);
392 
393  // draw rebuild progress (if not at 100%)
394  painter->setBrush(QBrush(QColor(100, 70, 0, 30)));
395  paintProgress(painter, rect, m_rebuildProgressMin, m_rebuildProgressMax,
396  m_rebuildProgress, false);
397 
398  // draw sort progress (if not at 100%)
399  painter->setBrush(QBrush(QColor(0, 100, 0, 30)));
400  paintProgress(painter, rect, m_sortProgressMin, m_sortProgressMax,
401  m_sortProgress, true);
402 
403  // draw the header's text. A rect will be drawn for each of the m_columns,
404  // with the column text in the center of it.
405  painter->setBrush(brush);
406  painter->setCompositionMode(compMode);
407 
408  for (int i = 0; i < visibleCols.size(); i++) {
409  TableColumn *visibleCol = visibleCols[i];
410 
411  QString columnText = visibleCol->getTitle();
412  QRect columnRect(getColumnRect(visibleCols.indexOf(visibleCol)));
413  QPen pen(palette().dark().color().darker(150));
414  pen.setCapStyle(Qt::RoundCap);
415  painter->setPen(pen);
416  painter->drawLine(columnRect.topLeft() + QPoint(0, 1),
417  columnRect.bottomLeft() + QPoint(0, 1));
418 
419  painter->drawLine(columnRect.topLeft() + QPoint(1, 0),
420  columnRect.topRight() - QPoint(0, 0));
421 
422  painter->drawLine(columnRect.topLeft() + QPoint(1, 1),
423  columnRect.topRight() + QPoint(0, 1));
424 
425  painter->drawLine(columnRect.bottomLeft() + QPoint(1, 1),
426  columnRect.bottomRight() + QPoint(0, 1));
427 
428  painter->drawLine(columnRect.bottomLeft() + QPoint(1, 1),
429  columnRect.bottomRight() + QPoint(0, 1));
430 
431  painter->drawLine(columnRect.topRight() + QPoint(0, 1),
432  columnRect.bottomRight() - QPoint(0, 0));
433 
434  painter->setPen(selected ? palette().highlightedText().color() :
435  palette().buttonText().color());
436 
437  QRect textRect(columnRect.x(),
438  columnRect.y(),
439  columnRect.width() - (SORT_ARROW_MARGIN * 2 + ARROW_WIDTH),
440  columnRect.height());
441  painter->drawText(textRect , Qt::AlignCenter | Qt::TextSingleLine,
442  columnText);
443 
444  if (visibleCol == visibleCols.getSortingOrder()[0] &&
445  visibleCol->getWidth() >= SORT_ARROW_MARGIN * 2 + ARROW_WIDTH) {
446  ASSERT(SORT_ARROW_MARGIN > 0);
447 
448  QRect arrowRect(textRect.right() + 1,
449  textRect.y(),
450  SORT_ARROW_MARGIN * 2 + ARROW_WIDTH,
451  textRect.height());
452 
453  ASSERT(arrowRect.width() + textRect.width() == columnRect.width());
454  ASSERT(arrowRect.right() == columnRect.right());
455 
456 
457  // assume ascending order (arrow looks like v)
458  QPoint left(arrowRect.left() + SORT_ARROW_MARGIN,
459  arrowRect.center().y() - ((ARROW_HEIGHT - 1) / 2));
460 
461  int yOffSet = ((ARROW_HEIGHT - 1) / 2);
462  if (ARROW_HEIGHT % 2 == 0)
463  yOffSet++;
464  QPoint center(left.x() + ((ARROW_WIDTH - 1) / 2),
465  arrowRect.center().y() + yOffSet);
466 
467  QPoint right(center.x() + ((ARROW_WIDTH - 1) / 2),
468  arrowRect.center().y() - ((ARROW_HEIGHT - 1) / 2));
469 
470  ASSERT(right.x() == arrowRect.right() - SORT_ARROW_MARGIN);
471  ASSERT(right.x() - center.x() == center.x() - left.x());
472 
473  if (!visibleCol->sortAscending()) {
474  // flip arrow (to look like ^)
475  left.setY(center.y());
476  center.setY(right.y());
477  right.setY(left.y());
478  }
479 
480  if (m_model->sortingOn()) {
481  painter->drawLine(left, center);
482  painter->drawLine(center, right);
483  }
484  }
485 
486  // Move the column rect to the position of the next column.
487  columnRect.moveLeft(columnRect.right());
488  }
489  }
490 
491 
492  void TableViewHeader::paintProgress(QPainter *painter,
493  const QRect &rect, int min, int max, int value, bool over100) {
494  double progressPercent = 1.0;
495  int progressRange = max - min;
496 
497  if (progressRange > 0)
498  progressPercent = ((double)(value - min)) / progressRange;
499  else if (progressRange == 0)
500  progressPercent = 0;
501 
502  if (progressPercent < 1.0 || over100) {
503  QRect progressRect(rect);
504  progressRect.setWidth((int)(progressRect.width() * progressPercent));
505  painter->fillRect(progressRect, painter->brush());
506  }
507  }
508 
509 
510  void TableViewHeader::updateFilterProgress(int newProgress) {
511  m_filterProgress = newProgress;
512  update();
513  }
514 
515 
516  void TableViewHeader::updateFilterProgressRange(int min, int max) {
517  m_filterProgressMin = min;
518  m_filterProgressMax = max;
519  update();
520  }
521 
522 
523  void TableViewHeader::updateRebuildProgress(int newProgress) {
524  m_rebuildProgress = newProgress;
525  update();
526  }
527 
528 
529  void TableViewHeader::updateRebuildProgressRange(int min, int max) {
530  m_rebuildProgressMin = min;
531  m_rebuildProgressMax = max;
532  update();
533  }
534 
535 
536  void TableViewHeader::updateSortProgress(int newProgress) {
537  m_sortProgress = newProgress;
538  update();
539  }
540 
541 
542  void TableViewHeader::updateSortProgressRange(int min, int max) {
543  m_sortProgressMin = min;
544  m_sortProgressMax = max;
545  update();
546  }
547  }
548 }

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