KD Chart API Documentation  3.1
kdganttview.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** This file is part of the KD Chart library.
4 **
5 ** SPDX-FileCopyrightText: 2001 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
6 **
7 ** SPDX-License-Identifier: MIT
8 **
9 ****************************************************************************/
10 
11 #include "kdganttview.h"
12 #include "kdganttview_p.h"
13 
14 #include "kdganttgraphicsitem.h"
15 #include "kdganttitemdelegate.h"
17 
18 #include <QAbstractItemModel>
19 #include <QGraphicsItem>
20 #include <QGraphicsRectItem>
21 #include <QHeaderView>
22 #include <QPaintEvent>
23 #include <QScrollBar>
24 #include <QVBoxLayout>
25 
26 #include <QDebug>
27 
28 #include <cassert>
29 
30 #if defined KDAB_EVAL
31 #include "../evaldialog/evaldialog.h"
32 #endif
33 
34 using namespace KDGantt;
35 
36 namespace {
37 class HeaderView : public QHeaderView
38 {
39 public:
40  explicit HeaderView(QWidget *parent = nullptr)
41  : QHeaderView(Qt::Horizontal, parent)
42  {
43  }
44 
45  QSize sizeHint() const override
46  {
47  QSize s = QHeaderView::sizeHint();
48  s.rheight() *= 2;
49  return s;
50  }
51 };
52 }
53 
54 KDGanttTreeView::KDGanttTreeView(QAbstractProxyModel *proxy, QWidget *parent)
55  : QTreeView(parent)
56  , m_controller(this, proxy)
57 {
58  setHeader(new HeaderView);
59 }
60 
61 KDGanttTreeView::~KDGanttTreeView()
62 {
63 }
64 
65 void KDGanttTreeView::expandAll(QModelIndex index)
66 {
67  for (int i = 0; i < model()->rowCount(index); i++) {
68  QModelIndex indexAt = model()->index(i, 0, index);
69  if (model()->hasChildren(indexAt))
70  expandAll(indexAt);
71  if (isExpanded(indexAt))
72  continue;
73  expand(indexAt);
74  }
75 }
76 
77 void KDGanttTreeView::collapseAll(QModelIndex index)
78 {
79  for (int i = 0; i < model()->rowCount(index); i++) {
80  QModelIndex indexAt = model()->index(i, 0, index);
81  if (model()->hasChildren(indexAt))
82  collapseAll(indexAt);
83  if (!isExpanded(indexAt))
84  continue;
85  collapse(indexAt);
86  }
87 }
88 
89 View::Private::Private(View *v)
90  : q(v)
91  , splitter(v)
92  , rowController(nullptr)
93  , gfxview(new GraphicsView(&splitter))
94  , model(nullptr)
95 {
96  // init();
97 }
98 
99 View::Private::~Private()
100 {
101  delete gfxview;
102 }
103 
104 void View::Private::init()
105 {
106  auto *tw = new KDGanttTreeView(&ganttProxyModel, &splitter);
107  tw->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
108  tw->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
109 
110  q->setLeftView(tw);
111  q->setRowController(tw->rowController());
112 
113  // gfxview.setRenderHints( QPainter::Antialiasing );
114 
115  tw->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
116 
117  auto *layout = new QVBoxLayout(q);
118  layout->setContentsMargins(0, 0, 0, 0);
119  layout->addWidget(&splitter);
120  q->setLayout(layout);
121 
122  constraintProxy.setProxyModel(&ganttProxyModel);
123  constraintProxy.setDestinationModel(&mappedConstraintModel);
124  setupGraphicsView();
125 }
126 
127 void View::Private::setupGraphicsView()
128 {
129  gfxview->setParent(&splitter);
130  gfxview->setAlignment(Qt::AlignTop | Qt::AlignLeft);
131  gfxview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
132  gfxview->setSelectionModel(leftWidget->selectionModel());
133  gfxview->setConstraintModel(&mappedConstraintModel);
134  q->setLeftView(leftWidget);
135  q->setRowController(rowController);
136  updateScene();
137 }
138 
139 void View::Private::updateScene()
140 {
141  gfxview->clearItems();
142  if (!model)
143  return;
144 
145  if (auto *tw = qobject_cast<QTreeView *>(leftWidget)) {
146  QModelIndex idx = ganttProxyModel.mapFromSource(model->index(0, 0, leftWidget->rootIndex()));
147  do {
148  gfxview->updateRow(idx);
149  } while ((idx = tw->indexBelow(idx)) != QModelIndex() && gfxview->rowController()->isRowVisible(idx));
150  gfxview->updateSceneRect();
151  } else {
152  const QModelIndex rootidx = ganttProxyModel.mapFromSource(leftWidget->rootIndex());
153  for (int r = 0; r < ganttProxyModel.rowCount(rootidx); ++r) {
154  gfxview->updateRow(ganttProxyModel.index(r, 0, rootidx));
155  }
156  }
157 }
158 
159 void View::Private::slotCollapsed(const QModelIndex &_idx)
160 {
161  auto *tw = qobject_cast<QTreeView *>(leftWidget);
162  if (!tw)
163  return;
164 
165  bool blocked = gfxview->blockSignals(true);
166 
167  QModelIndex idx(_idx);
168  const QAbstractItemModel *model = leftWidget->model();
169  const QModelIndex pidx = ganttProxyModel.mapFromSource(idx);
170  bool isMulti = false;
171  for (QModelIndex treewalkidx = pidx; treewalkidx.isValid(); treewalkidx = treewalkidx.parent()) {
172  if (treewalkidx.data(ItemTypeRole).toInt() == TypeMulti
173  && !gfxview->rowController()->isRowExpanded(treewalkidx)) {
174  isMulti = true;
175  break;
176  }
177  }
178 
179  if (!isMulti) {
180  for (int i = 0; i < model->rowCount(idx); ++i) {
181  gfxview->deleteSubtree(ganttProxyModel.index(i, 0, pidx));
182  }
183  } else {
184  gfxview->updateRow(pidx);
185  }
186  // qDebug() << "Looking to update from " << idx;
187  while ((idx = tw->indexBelow(idx)) != QModelIndex() && gfxview->rowController()->isRowVisible(ganttProxyModel.mapFromSource(idx))) {
188  const QModelIndex proxyidx(ganttProxyModel.mapFromSource(idx));
189  gfxview->updateRow(proxyidx);
190  }
191  gfxview->blockSignals(blocked);
192  gfxview->updateSceneRect();
193 }
194 
195 void View::Private::slotExpanded(const QModelIndex &_idx)
196 {
197  QModelIndex idx(ganttProxyModel.mapFromSource(_idx));
198  do {
199  // qDebug() << "Updating row" << idx << idx.data( Qt::DisplayRole ).toString();
200  gfxview->updateRow(idx);
201  } while ((idx = gfxview->rowController()->indexBelow(idx)) != QModelIndex()
202  && gfxview->rowController()->isRowVisible(idx));
203  gfxview->updateSceneRect();
204 }
205 
206 void View::Private::slotVerticalScrollValueChanged(int val)
207 {
208 #if 0
209  qDebug() << "View::Private::slotVerticalScrollValueChanged("<<val<<")="
210  << val/gfxview->verticalScrollBar()->singleStep();
211 #endif
212  leftWidget->verticalScrollBar()->setValue(val / gfxview->verticalScrollBar()->singleStep());
213 }
214 
215 void View::Private::slotLeftWidgetVerticalRangeChanged(int min, int max)
216 {
217  // qDebug() << "View::Private::slotLeftWidgetVerticalRangeChanged("<<min<<max<<")";
218  gfxview->verticalScrollBar()->setRange(min, max);
219  gfxview->updateSceneRect();
220 }
221 
222 void View::Private::slotGfxViewVerticalRangeChanged(int min, int max)
223 {
224  // qDebug() << "View::Private::slotGfxViewVerticalRangeChanged("<<min<<max<<")";
225  if (!leftWidget.isNull() && !gfxview.isNull()) {
226  int leftMin = leftWidget->verticalScrollBar()->minimum();
227  int leftMax = leftWidget->verticalScrollBar()->maximum();
228  bool blocked = gfxview->verticalScrollBar()->blockSignals(true);
229  gfxview->verticalScrollBar()->setRange(qMax(min, leftMin), qMax(max, leftMax));
230  gfxview->verticalScrollBar()->blockSignals(blocked);
231  }
232 }
233 
247 View::View(QWidget *parent)
248  : QWidget(parent)
249  , _d(new Private(this))
250 {
251 #if defined KDAB_EVAL
252  EvalDialog::checkEvalLicense("KD Gantt");
253 #endif
254  _d->init();
255 }
256 
258 {
259  delete _d;
260 }
261 
262 #define d d_func()
263 
269 void View::setLeftView(QAbstractItemView *aiv)
270 {
271  assert(aiv);
272  if (aiv == d->leftWidget)
273  return;
274  if (!d->leftWidget.isNull()) {
275  d->leftWidget->disconnect(this);
276  d->leftWidget->hide();
277  d->leftWidget->verticalScrollBar()->disconnect(d->gfxview->verticalScrollBar());
278  d->gfxview->verticalScrollBar()->disconnect(d->leftWidget->verticalScrollBar());
279  }
280 
281  d->leftWidget = aiv;
282  d->splitter.insertWidget(0, d->leftWidget);
283 
284  auto leftWidgetTreeView = qobject_cast<QTreeView *>(d->leftWidget);
285  if (leftWidgetTreeView) {
286  connect(leftWidgetTreeView, &QTreeView::collapsed,
287  this, [this](const QModelIndex &index) {
288  d->slotCollapsed(index);
289  });
290  connect(leftWidgetTreeView, &QTreeView::expanded,
291  this, [this](const QModelIndex &index) {
292  d->slotExpanded(index);
293  });
294  }
295 
296  connect(d->gfxview->verticalScrollBar(), &QScrollBar::valueChanged,
297  d->leftWidget->verticalScrollBar(), &QScrollBar::setValue);
298  connect(d->leftWidget->verticalScrollBar(), &QScrollBar::valueChanged,
299  d->gfxview->verticalScrollBar(), &QScrollBar::setValue);
300  connect(d->leftWidget->verticalScrollBar(), &QScrollBar::rangeChanged,
301  this, [this](int min, int max) {
302  d->slotLeftWidgetVerticalRangeChanged(min, max);
303  });
304  connect(d->gfxview->verticalScrollBar(), &QScrollBar::rangeChanged,
305  this, [this](int min, int max) {
306  d->slotLeftWidgetVerticalRangeChanged(min, max);
307  });
308 }
309 
316 {
317  if (ctrl == d->rowController && d->gfxview->rowController() == ctrl)
318  return;
319  d->rowController = ctrl;
320  d->gfxview->setRowController(d->rowController);
321 }
322 
327 {
328  return d->rowController;
329 }
330 
334 {
335  return d->rowController;
336 }
337 
342 const QAbstractItemView *View::leftView() const
343 {
344  return d->leftWidget;
345 }
346 
350 QAbstractItemView *View::leftView()
351 {
352  return d->leftWidget;
353 }
354 
362 {
363  if (gv != d->gfxview) {
364  GraphicsView *old = d->gfxview;
365  d->gfxview = gv;
366  d->setupGraphicsView();
367  d->gfxview->setGrid(old->grid());
368  delete old;
369  }
370 }
371 
376 {
377  return d->gfxview;
378 }
379 
384 {
385  return d->gfxview;
386 }
387 
391 const QSplitter *View::splitter() const
392 {
393  return &d->splitter;
394 }
395 
399 QSplitter *View::splitter()
400 {
401  return &d->splitter;
402 }
403 
406 QAbstractItemModel *View::model() const
407 {
408  return leftView()->model();
409 }
410 
416 void View::setModel(QAbstractItemModel *model)
417 {
418  leftView()->setModel(model);
419  d->ganttProxyModel.setSourceModel(model);
420  d->gfxview->setModel(&d->ganttProxyModel);
421 }
422 
425 QItemSelectionModel *View::selectionModel() const
426 {
427  return leftView()->selectionModel();
428 }
429 
433 void View::setSelectionModel(QItemSelectionModel *smodel)
434 {
435  leftView()->setSelectionModel(smodel);
436  d->gfxview->setSelectionModel(new QItemSelectionModel(&(d->ganttProxyModel), this));
437 }
438 
445 {
446  d->gfxview->setGrid(grid);
447 }
448 
449 void View::expandAll(QModelIndex index)
450 {
451  auto *tw = qobject_cast<KDGanttTreeView *>(leftView());
452  tw->expandAll(index);
453 }
454 
455 void View::collapseAll(QModelIndex index)
456 {
457  auto *tw = qobject_cast<KDGanttTreeView *>(leftView());
458  tw->collapseAll(index);
459 }
460 
464 {
465  return d->gfxview->grid();
466 }
467 
470 QModelIndex View::rootIndex() const
471 {
472  return leftView()->rootIndex();
473 }
474 
478 void View::setRootIndex(const QModelIndex &idx)
479 {
480  leftView()->setRootIndex(idx);
481  d->gfxview->setRootIndex(idx);
482 }
483 
487 {
488  return d->gfxview->itemDelegate();
489 }
490 
495 {
496  leftView()->setItemDelegate(delegate);
497  d->gfxview->setItemDelegate(delegate);
498 }
499 
504 {
505  d->constraintProxy.setSourceModel(cm);
506  d->gfxview->setConstraintModel(&d->mappedConstraintModel);
507 }
508 
512 {
513  return d->constraintProxy.sourceModel();
514 }
515 
516 const QAbstractProxyModel *View::ganttProxyModel() const
517 {
518  return &(d->ganttProxyModel);
519 }
520 
521 QAbstractProxyModel *View::ganttProxyModel()
522 {
523  return &(d->ganttProxyModel);
524 }
525 
526 void View::ensureVisible(const QModelIndex &index)
527 {
528  QGraphicsView *view = graphicsView();
529  auto *scene = static_cast<KDGantt::GraphicsScene *>(view->scene());
530  if (!scene)
531  return;
532 
533  auto *model = static_cast<KDGantt::SummaryHandlingProxyModel *>(scene->summaryHandlingModel());
534 
535  const QModelIndex pidx = d->ganttProxyModel.mapFromSource(index);
536  const QModelIndex idx = model->mapFromSource(pidx);
537  QGraphicsItem *item = scene->findItem(idx);
538  view->ensureVisible(item);
539 }
540 
541 void View::resizeEvent(QResizeEvent *ev)
542 {
543  QWidget::resizeEvent(ev);
544 }
545 
552 QModelIndex View::indexAt(const QPoint &pos) const
553 {
554  return d->gfxview->indexAt(pos);
555 }
556 
565 void View::print(QPrinter *printer, bool drawRowLabels, bool drawColumnLabels)
566 {
567  graphicsView()->print(printer, drawRowLabels, drawColumnLabels);
568 }
569 
582 void View::print(QPrinter *printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels)
583 {
584  graphicsView()->print(printer, start, end, drawRowLabels, drawColumnLabels);
585 }
586 
593 void View::print(QPainter *painter, const QRectF &target, bool drawRowLabels, bool drawColumnLabels)
594 {
595  d->gfxview->print(painter,
596  target,
597  drawRowLabels,
598  drawColumnLabels);
599 }
600 
611 void View::print(QPainter *painter, qreal start, qreal end, const QRectF &target, bool drawRowLabels, bool drawColumnLabels)
612 {
613  d->gfxview->print(painter,
614  start, end,
615  target,
616  drawRowLabels,
617  drawColumnLabels);
618 }
619 
620 #include "moc_kdganttview.cpp"
621 
622 #undef d
623 
624 #ifndef KDAB_NO_UNIT_TESTS
625 #include "unittest/test.h"
626 
628 #include <QApplication>
629 #include <QListView>
630 #include <QPixmap>
631 #include <QTimer>
632 
634 {
635  View view(nullptr);
636 #if 0 // GUI tests do not work well on the server
637  QTimer::singleShot( 1000, qApp, &QCoreApplication::quit );
638  view.show();
639 
640  qApp->exec();
641  QPixmap screenshot1 = QPixmap::grabWidget( &view );
642 
643  QTreeView* tv = new QTreeView;
644  view.setLeftView( tv );
646 
647  QTimer::singleShot( 1000, qApp, &QCoreApplication::quit );
648 
649  qApp->exec();
650  QPixmap screenshot2 = QPixmap::grabWidget( &view );
651 
652  assertEqual( screenshot1.toImage(), screenshot2.toImage() );
653 
654  QListView* lv = new QListView;
655  view.setLeftView(lv);
657  view.show();
658  QTimer::singleShot( 1000, qApp, &QCoreApplication::quit );
659  qApp->exec();
660 #endif
661 }
662 #endif /* KDAB_NO_UNIT_TESTS */
Abstract baseclass for grids.
Abstract baseclass for row controllers.
The ConstraintModel keeps track of the interdependencies between gantt items in a View.
The GraphicsView class provides a model/view implementation of a gantt chart.
void print(QPrinter *printer, bool drawRowLabels=true, bool drawColumnLabels=true)
Print the Gantt chart using printer.
AbstractGrid * grid() const
Class used to render gantt items in a KDGantt::GraphicsView.
Proxy model that supports summary gantt items.
This widget that consists of a QTreeView and a GraphicsView.
Definition: kdganttview.h:36
~View() override
void ensureVisible(const QModelIndex &index)
void expandAll(QModelIndex index=QModelIndex())
QModelIndex indexAt(const QPoint &pos) const
void setLeftView(QAbstractItemView *)
Replaces the left widget with a custom QAbstractItemView.
void setConstraintModel(ConstraintModel *)
Sets the constraintmodel displayed by this view.
void setGrid(AbstractGrid *)
Sets the AbstractGrid for this view.
void setRootIndex(const QModelIndex &idx)
Sets the root index of the model displayed by this view.
void setItemDelegate(ItemDelegate *)
Sets the KDGantt::ItemDelegate used for rendering items on this view.
void setRowController(AbstractRowController *)
Sets ctrl to be the rowcontroller used by this View.
void setSelectionModel(QItemSelectionModel *smodel)
Sets the QItemSelectionModel used by this view to manage selections.
void setGraphicsView(GraphicsView *)
Set the GraphicsView to be used for this View.
const QSplitter * splitter() const
void setModel(QAbstractItemModel *model)
Sets the QAbstractItemModel to be displayed in this view to model.
QItemSelectionModel * selectionModel() const
const QAbstractProxyModel * ganttProxyModel() const
const QAbstractItemView * leftView() const
ItemDelegate * itemDelegate() const
ConstraintModel * constraintModel() const
const GraphicsView * graphicsView() const
AbstractRowController * rowController()
void resizeEvent(QResizeEvent *) override
void print(QPrinter *printer, bool drawRowLabels=true, bool drawColumnLabels=true)
Print the Gantt chart using printer.
AbstractGrid * grid() const
QAbstractItemModel * model() const
QModelIndex rootIndex() const
void collapseAll(QModelIndex index=QModelIndex())
#define d
KDAB_SCOPED_UNITTEST_SIMPLE(KDGantt, View, "test")

© 2001 Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
https://www.kdab.com/development-resources/qt-tools/kd-chart/
Generated by doxygen 1.9.1