Isis 3 Programmer Reference
TableViewHeader.cpp
1
7/* SPDX-License-Identifier: CC0-1.0 */
8
9#include "TableViewHeader.h"
10
11#include <iostream>
12
13#include <QAbstractItemModel>
14#include <QFontMetrics>
15#include <QLabel>
16#include <QLinearGradient>
17#include <QLocale>
18#include <QMessageBox>
19#include <QMouseEvent>
20#include <QPainter>
21#include <QPen>
22#include <QRect>
23#include <QString>
24#include <QVBoxLayout>
25
26#include "AbstractTableModel.h"
27#include "AbstractTreeModel.h"
28#include "TableColumn.h"
29#include "TableColumnList.h"
30#include "TableView.h"
31
32
33namespace Isis {
40 nullify();
41
42 m_horizontalOffset = 0;
43 m_filterProgress = 0;
44 m_filterProgressMin = 0;
45 m_filterProgressMax = 0;
46 m_rebuildProgress = 0;
47 m_rebuildProgressMin = 0;
48 m_rebuildProgressMax = 0;
49 m_sortProgress = 0;
50 m_sortProgressMin = 0;
51 m_sortProgressMax = 0;
52 m_visibleCount = -1;
53 m_totalCount = -1;
54
55 m_clickedColumnEdge = -1;
56 m_clickedColumn = -1;
57
58 setMouseTracking(true);
59
60 setModel(someModel);
61
62 ARROW_HEIGHT = 3;
63 ARROW_WIDTH = 5;
64 }
65
66
71 m_columns = NULL;
72 }
73
74
81 m_columns = cols;
82 }
83
84
91 return QSize(0, QFontMetrics(font()).height() + 6);
92 /*QFontMetrics(font()).width(text->join("")) + 15,
93 QFontMetrics(font()).height() + 6);*/
94 }
95
96
105 return minimumSizeHint();
106 }
107
108
115 if (m_model) {
116 disconnect(m_model, SIGNAL(filterProgressChanged(int)),
117 this, SLOT(updateFilterProgress(int)));
118 disconnect(m_model, SIGNAL(rebuildProgressChanged(int)),
119 this, SLOT(updateRebuildProgress(int)));
120 disconnect(m_model, SIGNAL(sortProgressChanged(int)),
121 this, SLOT(updateSortProgress(int)));
122 disconnect(m_model, SIGNAL(filterProgressRangeChanged(int, int)),
123 this, SLOT(updateFilterProgressRange(int, int)));
124 disconnect(m_model, SIGNAL(rebuildProgressRangeChanged(int, int)),
125 this, SLOT(updateRebuildProgressRange(int, int)));
126 disconnect(m_model, SIGNAL(sortProgressRangeChanged(int, int)),
127 this, SLOT(updateSortProgressRange(int, int)));
128 disconnect(m_model, SIGNAL(filterCountsChanged(int, int)),
129 this, SLOT(handleFilterCountsChanged(int, int)));
130 disconnect(this, SIGNAL(requestedGlobalSelection(bool)),
131 m_model, SLOT(setGlobalSelection(bool)));
132 disconnect(m_model, SIGNAL(m_modelModified()),
133 this, SLOT(update()));
134 }
135
136 m_model = someModel;
137
138 connect(m_model, SIGNAL(filterProgressChanged(int)),
139 this, SLOT(updateFilterProgress(int)));
140 connect(m_model, SIGNAL(rebuildProgressChanged(int)),
141 this, SLOT(updateRebuildProgress(int)));
142 connect(m_model, SIGNAL(sortProgressChanged(int)),
143 this, SLOT(updateSortProgress(int)));
144 connect(m_model, SIGNAL(filterProgressRangeChanged(int, int)),
145 this, SLOT(updateFilterProgressRange(int, int)));
146 connect(m_model, SIGNAL(rebuildProgressRangeChanged(int, int)),
147 this, SLOT(updateRebuildProgressRange(int, int)));
148 connect(m_model, SIGNAL(sortProgressRangeChanged(int, int)),
149 this, SLOT(updateSortProgressRange(int, int)));
150 connect(m_model, SIGNAL(filterCountsChanged(int, int)),
151 this, SLOT(handleFilterCountsChanged(int, int)));
152 connect(this, SIGNAL(requestedGlobalSelection(bool)),
153 m_model, SLOT(setGlobalSelection(bool)));
154 connect(m_model, SIGNAL(modelModified()), this, SLOT(update()));
155
156
157 if (m_columns) {
158 for (int i = 0; i < m_columns->size(); i++) {
159 disconnect((*m_columns)[i], SIGNAL(visibilityChanged()),
160 this, SLOT(update()));
161 }
162 }
163
164 m_columns = m_model->getColumns();
165
166 for (int i = 0; i < m_columns->size(); i++)
167 connect((*m_columns)[i], SIGNAL(visibilityChanged()), this, SLOT(update()));
168 }
169
170
178 int visibleTopLevelItemCount, int topLevelItemCount) {
179 m_visibleCount = visibleTopLevelItemCount;
180 m_totalCount = topLevelItemCount;
181
182 if (m_visibleCount >= 0) {
183 TableColumnList visibleCols = m_columns->getVisibleColumns();
184 for (int i = 0; i < visibleCols.size(); i++) {
185 TableColumn *& col = visibleCols[i];
186
187 if (col->getTitle().isEmpty())
188 col->setWidth(QFontMetrics(font()).width(
189 QString::number(m_visibleCount)) + 22);
190 }
191 }
192
193 updateGeometry();
194 update();
195 }
196
197
204 m_horizontalOffset = newOffset;
205 update();
206 }
207
208
214 void TableViewHeader::mousePressEvent(QMouseEvent *event) {
215 QPoint mousePos = event->pos();
216
217 m_clickedColumn = getMousedColumn(mousePos);
218
219 // QRect priorityRect = getSortingPriorityRect(m_clickedColumn);
220 // QRect arrowRect = getSortingArrowRect(m_clickedColumn);
221
222
223 if (event->buttons() == Qt::LeftButton) {
224 m_clickedColumnEdge = getMousedColumnEdge(mousePos);
225 if (m_clickedColumnEdge == -1 && m_clickedColumn != -1) {
226 // The click wasn't on a column edge.
227 if (m_columns->getVisibleColumns()[m_clickedColumn]->getTitle().isEmpty()) {
228 emit requestedGlobalSelection(true);
229 }
230 else {
231 // if (priorityRect.contains(mousePos))
232 // {
233 // emit requestedColumnSelection(m_clickedColumn, true);
234 // }
235
236 }
237 }
238 }
239 }
240
241
247 void TableViewHeader::mouseMoveEvent(QMouseEvent *event) {
248 QPoint mousePos = event->pos();
249
250 if (m_clickedColumnEdge >= 0) {
251 // edge == column that we want to resize
252 QRect columnToResizeRect(getColumnRect(m_clickedColumnEdge));
253 columnToResizeRect.setRight(mousePos.x());
254
255 TableColumn *col = m_columns->getVisibleColumns()[m_clickedColumnEdge];
256
257 int newWidth = 1;
258 if (columnToResizeRect.width() > 1) {
259 newWidth = columnToResizeRect.width();
260 if (m_columns->getSortingOrder()[0] == col)
261 newWidth = qMax(newWidth, ARROW_WIDTH + SORT_ARROW_MARGIN * 2);
262 }
263
264 m_columns->getVisibleColumns()[m_clickedColumnEdge]->setWidth(newWidth);
265 }
266
267 if (mouseAtResizableColumnEdge(mousePos)) {
268 setCursor(Qt::SizeHorCursor);
269 }
270 else {
271 setCursor(Qt::ArrowCursor);
272 }
273
274 update();
275 }
276
277
283 void TableViewHeader::mouseReleaseEvent(QMouseEvent *event) {
284 bool wasLastCol =
285 m_clickedColumnEdge >= m_columns->getVisibleColumns().size() - 2;
286 if (m_clickedColumnEdge != -1) {
287 emit columnResized(wasLastCol);
288 }
289 else {
290 if (m_clickedColumn == getMousedColumn(event->pos())) {
291 TableColumn *col = m_columns->getVisibleColumns()[m_clickedColumn];
292
293 TableColumn const *sortCol =
294 m_columns->getVisibleColumns().getSortingOrder()[0];
295
296 if (col == sortCol)
297 col->setSortAscending(!col->sortAscending());
298 else
299 m_columns->raiseToTop(col);
300
301 if (!m_model->sortingOn()) {
302 QMessageBox::information(this, tr("Sorting Disabled"),
303 tr("Sorting is currently disabled for this table. Please configure your sorting "
304 "options before trying to sort by [<font color='red'>%1</font>].")
305 .arg(col->getTitle()),
306 QMessageBox::Ok);
307 }
308 }
309 }
310
311 m_clickedColumnEdge = -1;
312 m_clickedColumn = -1;
313
314 update();
315 }
316
317
323 void TableViewHeader::paintEvent(QPaintEvent *event) {
324 QPainter painter(this);
325 painter.setRenderHints(QPainter::Antialiasing |
326 QPainter::TextAntialiasing);
327
328 ARROW_HEIGHT = qMax(height() / 5, 3);
329
330 ARROW_WIDTH = ARROW_HEIGHT * 2 - 1;
331
332 paintHeader(&painter, height());
333 // painter.drawRect(0, 0, width(), height());
334 painter.end();
335 }
336
337
342 m_model = NULL;
343 m_columns = NULL;
344 }
345
346
354 QRect TableViewHeader::getColumnRect(int column) const {
355 QRect colRect;
356
357 TableColumnList visibleCols = m_columns->getVisibleColumns();
358
359 if (column < visibleCols.size() && column >= 0) {
360 int indent = 1;
361
362 for (int i = 0; i < column; i++)
363 indent += visibleCols[i]->getWidth() - 1;
364
365 colRect = QRect(indent - m_horizontalOffset, 0,
366 visibleCols[column]->getWidth(), height());
367 }
368
369 return colRect;
370 }
371
372
380 int TableViewHeader::getMousedColumn(QPoint mousePos) {
381 int mousedColumn = -1;
382
383 for (int i = 0;
384 i < m_columns->getVisibleColumns().size() && mousedColumn < 0;
385 i++) {
386 QRect columnRect(getColumnRect(i));
387 if (columnRect.contains(mousePos))
388 mousedColumn = i;
389 }
390
391 return mousedColumn;
392 }
393
394
403 int edge = -1;
404
405 if (mouseAtResizableColumnEdge(mousePos)) {
406 int mousedColumn = getMousedColumn(mousePos);
407
408 QRect columnRect(getColumnRect(mousedColumn));
409
410 // mouseAtResizableColumnEdge can't be on left of first so this won't
411 // return -1 resulting from this particular logic.
412 if (mousePos.x() - columnRect.left() < TableColumn::EDGE_WIDTH)
413 edge = mousedColumn - 1;
414 else
415 edge = mousedColumn;
416 }
417
418 return edge;
419 }
420
421
430 int columnNum = getMousedColumn(mousePos);
431
432 QRect columnRect(getColumnRect(columnNum));
433
434 bool isAtColumnEdge = false;
435
436 if (!columnRect.isNull()) {
437 bool isOnLeft =
438 mousePos.x() - columnRect.left() < TableColumn::EDGE_WIDTH;
439 bool isOnRight =
440 columnRect.right() - mousePos.x() < TableColumn::EDGE_WIDTH;
441 bool isResizable = false;
442
443 TableColumnList visCols = m_columns->getVisibleColumns();
444 if (isOnLeft && columnNum > 0)
445 isResizable = visCols[columnNum - 1]->getTitle().size();
446 else if (isOnRight)
447 isResizable = visCols[columnNum]->getTitle().size();
448
449 isAtColumnEdge = (isOnLeft || isOnRight) && isResizable;
450 }
451
452 return isAtColumnEdge;
453 }
454
455
462 void TableViewHeader::paintHeader(QPainter *painter, int rowHeight) {
463 int visibleColWidth = -m_horizontalOffset;
464 TableColumnList visibleCols = m_columns->getVisibleColumns();
465
466 for (int i = 0; i < visibleCols.size(); i++)
467 visibleColWidth += visibleCols[i]->getWidth() - 1;
468
469 QRect rect(0, 0, qMin(width(), visibleColWidth), rowHeight);
470
471 int x = rect.center().x();
472 QLinearGradient gradient(x, rect.top(), x, rect.bottom());
473
474 //FIXME: selected needs to be member variable
475 bool selected = false;
476 QColor color = selected ? palette().highlight().color() :
477 palette().button().color();
478
479 // create gradient and fill header area with it
480 int adjustment = 110;
481 gradient.setColorAt(0, color.lighter(adjustment));
482 gradient.setColorAt(1, color.darker(adjustment));
483 painter->fillRect(rect, gradient);
484
485 // Save off composition mode and brush, which will need to be restored
486 // after the progress is painted.
487 QBrush brush = painter->brush();
488 QPainter::CompositionMode compMode = painter->compositionMode();
489 painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
490
491 // draw filter progress (if not at 100%)
492 painter->setBrush(QBrush(QColor(0, 70, 100, 30)));
493 paintProgress(painter, rect, m_filterProgressMin, m_filterProgressMax,
494 m_filterProgress, false);
495
496 // draw rebuild progress (if not at 100%)
497 painter->setBrush(QBrush(QColor(100, 70, 0, 30)));
498 paintProgress(painter, rect, m_rebuildProgressMin, m_rebuildProgressMax,
499 m_rebuildProgress, false);
500
501 // draw sort progress (if not at 100%)
502 painter->setBrush(QBrush(QColor(0, 100, 0, 30)));
503 paintProgress(painter, rect, m_sortProgressMin, m_sortProgressMax,
504 m_sortProgress, true);
505
506 // draw the header's text. A rect will be drawn for each of the m_columns,
507 // with the column text in the center of it.
508 painter->setBrush(brush);
509 painter->setCompositionMode(compMode);
510
511 for (int i = 0; i < visibleCols.size(); i++) {
512 TableColumn *visibleCol = visibleCols[i];
513
514 QString columnText = visibleCol->getTitle();
515 QRect columnRect(getColumnRect(visibleCols.indexOf(visibleCol)));
516 QPen pen(palette().dark().color().darker(150));
517 pen.setCapStyle(Qt::RoundCap);
518 painter->setPen(pen);
519 painter->drawLine(columnRect.topLeft() + QPoint(0, 1),
520 columnRect.bottomLeft() + QPoint(0, 1));
521
522 painter->drawLine(columnRect.topLeft() + QPoint(1, 0),
523 columnRect.topRight() - QPoint(0, 0));
524
525 painter->drawLine(columnRect.topLeft() + QPoint(1, 1),
526 columnRect.topRight() + QPoint(0, 1));
527
528 painter->drawLine(columnRect.bottomLeft() + QPoint(1, 1),
529 columnRect.bottomRight() + QPoint(0, 1));
530
531 painter->drawLine(columnRect.bottomLeft() + QPoint(1, 1),
532 columnRect.bottomRight() + QPoint(0, 1));
533
534 painter->drawLine(columnRect.topRight() + QPoint(0, 1),
535 columnRect.bottomRight() - QPoint(0, 0));
536
537 painter->setPen(selected ? palette().highlightedText().color() :
538 palette().buttonText().color());
539
540 QRect textRect(columnRect.x(),
541 columnRect.y(),
542 columnRect.width() - (SORT_ARROW_MARGIN * 2 + ARROW_WIDTH),
543 columnRect.height());
544 painter->drawText(textRect , Qt::AlignCenter | Qt::TextSingleLine,
545 columnText);
546
547 if (visibleCol == visibleCols.getSortingOrder()[0] &&
548 visibleCol->getWidth() >= SORT_ARROW_MARGIN * 2 + ARROW_WIDTH) {
549
550 QRect arrowRect(textRect.right() + 1,
551 textRect.y(),
552 SORT_ARROW_MARGIN * 2 + ARROW_WIDTH,
553 textRect.height());
554
555 // assume ascending order (arrow looks like v)
556 QPoint left(arrowRect.left() + SORT_ARROW_MARGIN,
557 arrowRect.center().y() - ((ARROW_HEIGHT - 1) / 2));
558
559 int yOffSet = ((ARROW_HEIGHT - 1) / 2);
560 if (ARROW_HEIGHT % 2 == 0)
561 yOffSet++;
562 QPoint center(left.x() + ((ARROW_WIDTH - 1) / 2),
563 arrowRect.center().y() + yOffSet);
564
565 QPoint right(center.x() + ((ARROW_WIDTH - 1) / 2),
566 arrowRect.center().y() - ((ARROW_HEIGHT - 1) / 2));
567
568 if (!visibleCol->sortAscending()) {
569 // flip arrow (to look like ^)
570 left.setY(center.y());
571 center.setY(right.y());
572 right.setY(left.y());
573 }
574
575 if (m_model->sortingOn()) {
576 painter->drawLine(left, center);
577 painter->drawLine(center, right);
578 }
579 }
580
581 // Move the column rect to the position of the next column.
582 columnRect.moveLeft(columnRect.right());
583 }
584 }
585
586
597 void TableViewHeader::paintProgress(QPainter *painter,
598 const QRect &rect, int min, int max, int value, bool over100) {
599 double progressPercent = 1.0;
600 int progressRange = max - min;
601
602 if (progressRange > 0)
603 progressPercent = ((double)(value - min)) / progressRange;
604 else if (progressRange == 0)
605 progressPercent = 0;
606
607 if (progressPercent < 1.0 || over100) {
608 QRect progressRect(rect);
609 progressRect.setWidth((int)(progressRect.width() * progressPercent));
610 painter->fillRect(progressRect, painter->brush());
611 }
612 }
613
614
621 m_filterProgress = newProgress;
622 update();
623 }
624
625
633 m_filterProgressMin = min;
634 m_filterProgressMax = max;
635 update();
636 }
637
638
645 m_rebuildProgress = newProgress;
646 update();
647 }
648
649
657 m_rebuildProgressMin = min;
658 m_rebuildProgressMax = max;
659 update();
660 }
661
662
669 m_sortProgress = newProgress;
670 update();
671 }
672
673
681 m_sortProgressMin = min;
682 m_sortProgressMax = max;
683 update();
684 }
685}
Translates the tree model into a table model.
void updateFilterProgressRange(int min, int max)
Updates the range of the filter progress.
void paintHeader(QPainter *painter, int rowheight)
Repaints the header.
void nullify()
Sets all the member variables to NULL.
void mouseMoveEvent(QMouseEvent *event)
Overrides QWidget::mouseMoveEvent.
void mouseReleaseEvent(QMouseEvent *event)
Overrides QWidget::mouseReleaseEvent.
void updateHeaderOffset(int)
Updates the header offset.
int getMousedColumn(QPoint mousePos)
Returns the column under the mouse.
void updateRebuildProgressRange(int min, int max)
Updates the range of the rebuild progress.
void setModel(AbstractTableModel *someModel)
Connects the table model to the functions that handle changes.
QRect getColumnRect(int column) const
Returns the visible column rectangle.
bool mouseAtResizableColumnEdge(QPoint mousePos)
Returns if the mouse is at the edge of a resizeable column.
void updateSortProgress(int newProgress)
Updates the current sort progress value.
QSize sizeHint() const
Returns the minimum size based on the font.
void updateSortProgressRange(int min, int max)
Updates the range of the sort progress.
void paintProgress(QPainter *painter, const QRect &rect, int min, int max, int value, bool over100)
Updates the progress bar.
virtual ~TableViewHeader()
Destructor.
void updateRebuildProgress(int newProgress)
Updates the current rebuild progress value.
QSize minimumSizeHint() const
Returns the minimum size based on the font.
void mousePressEvent(QMouseEvent *event)
Overrides QWidget::mousePressEvent.
virtual void setColumns(TableColumnList *)
Sets the column list.
TableViewHeader(AbstractTableModel *someModel)
Constructor.
int getMousedColumnEdge(QPoint mousePos)
Returns the edge of the column under the mouse.
void paintEvent(QPaintEvent *event)
Repaints the header.
void handleFilterCountsChanged(int visibleTopLevelItemCount, int topLevelItemCount)
Updates the visible columns, and geometry when the filter count changes.
void updateFilterProgress(int newProgress)
Updates the current filter progress value.
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16