KD Chart 2  [rev.2.7]
kdganttview.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Copyright (C) 2001-2020 Klaralvdalens Datakonsult AB. All rights reserved.
3 **
4 ** This file is part of the KD Chart library.
5 **
6 ** Licensees holding valid commercial KD Chart licenses may use this file in
7 ** accordance with the KD Chart Commercial License Agreement provided with
8 ** the Software.
9 **
10 **
11 ** This file may be distributed and/or modified under the terms of the
12 ** GNU General Public License version 2 and version 3 as published by the
13 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
14 **
15 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17 **
18 ** Contact info@kdab.com if any conditions of this licensing are not
19 ** clear to you.
20 **
21 **********************************************************************/
22 
23 #include "kdganttview.h"
24 #include "kdganttview_p.h"
25 
26 #include "kdganttitemdelegate.h"
27 #include "kdganttgraphicsitem.h"
29 
30 #include <QAbstractItemModel>
31 #include <QHeaderView>
32 #include <QVBoxLayout>
33 #include <QGraphicsItem>
34 #include <QGraphicsRectItem>
35 #include <QScrollBar>
36 #include <QPaintEvent>
37 
38 #include <QDebug>
39 
40 #include <cassert>
41 
42 #if defined KDAB_EVAL
43 #include "../evaldialog/evaldialog.h"
44 #endif
45 
46 using namespace KDGantt;
47 
48 namespace {
49  class HeaderView : public QHeaderView {
50  public:
51  explicit HeaderView( QWidget* parent=0 ) : QHeaderView( Qt::Horizontal, parent ) {
52  }
53 
54  QSize sizeHint() const override { QSize s = QHeaderView::sizeHint(); s.rheight() *= 2; return s; }
55  };
56 }
57 
58 KDGanttTreeView::KDGanttTreeView( QAbstractProxyModel* proxy, QWidget* parent )
59  : QTreeView( parent ),
60  m_controller( this, proxy )
61 {
62  setHeader( new HeaderView );
63 }
64 
65 KDGanttTreeView::~KDGanttTreeView()
66 {
67 }
68 
69 void KDGanttTreeView::expandAll(QModelIndex index)
70 {
71  for (int i = 0; i < model()->rowCount(index); i++) {
72  QModelIndex indexAt = model()->index(i, 0, index);
73  if (model()->hasChildren(indexAt))
74  expandAll(indexAt);
75  if (isExpanded(indexAt))
76  continue;
77  expand(indexAt);
78  }
79 }
80 
81 void KDGanttTreeView::collapseAll(QModelIndex index)
82 {
83  for (int i = 0; i < model()->rowCount(index); i++) {
84  QModelIndex indexAt = model()->index(i, 0, index);
85  if (model()->hasChildren(indexAt))
86  collapseAll(indexAt);
87  if (!isExpanded(indexAt))
88  continue;
89  collapse(indexAt);
90  }
91 }
92 
93 View::Private::Private(View* v)
94  : q(v),
95  splitter(v),
96  rowController(0),
97  gfxview( new GraphicsView( &splitter ) ),
98  model(0)
99 {
100  //init();
101 }
102 
103 View::Private::~Private()
104 {
105  delete gfxview;
106 }
107 
108 void View::Private::init()
109 {
110  KDGanttTreeView* tw = new KDGanttTreeView( &ganttProxyModel, &splitter );
111  tw->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
112  tw->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
113 
114  q->setLeftView( tw );
115  q->setRowController( tw->rowController() );
116 
117  //gfxview.setRenderHints( QPainter::Antialiasing );
118 
119  tw->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
120 
121  QVBoxLayout* layout = new QVBoxLayout(q);
122  layout->setMargin(0);
123  layout->addWidget(&splitter);
124  q->setLayout(layout);
125 
126  constraintProxy.setProxyModel( &ganttProxyModel );
127  constraintProxy.setDestinationModel( &mappedConstraintModel );
128  setupGraphicsView();
129 }
130 
131 void View::Private::setupGraphicsView()
132 {
133  gfxview->setParent( &splitter );
134  gfxview->setAlignment(Qt::AlignTop|Qt::AlignLeft);
135  gfxview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
136  gfxview->setSelectionModel( leftWidget->selectionModel() );
137  gfxview->setConstraintModel( &mappedConstraintModel );
138  q->setLeftView( leftWidget );
139  q->setRowController( rowController );
140  updateScene();
141 }
142 
143 void View::Private::updateScene()
144 {
145  gfxview->clearItems();
146  if ( !model) return;
147 
148  if ( QTreeView* tw = qobject_cast<QTreeView*>(leftWidget)) {
149  QModelIndex idx = ganttProxyModel.mapFromSource( model->index( 0, 0, leftWidget->rootIndex() ) );
150  do {
151  gfxview->updateRow( idx );
152  } while ( ( idx = tw->indexBelow( idx ) ) != QModelIndex() &&
153  gfxview->rowController()->isRowVisible(idx) );
154  gfxview->updateSceneRect();
155  } else {
156  const QModelIndex rootidx = ganttProxyModel.mapFromSource( leftWidget->rootIndex() );
157  for ( int r = 0; r < ganttProxyModel.rowCount(rootidx); ++r ) {
158  gfxview->updateRow( ganttProxyModel.index( r, 0, rootidx ) );
159  }
160  }
161 }
162 
163 void View::Private::slotCollapsed(const QModelIndex& _idx)
164 {
165  QTreeView* tw = qobject_cast<QTreeView*>(leftWidget);
166  if (!tw) return;
167 
168  bool blocked = gfxview->blockSignals( true );
169 
170  QModelIndex idx( _idx );
171  const QAbstractItemModel* model = leftWidget->model();
172  const QModelIndex pidx = ganttProxyModel.mapFromSource(idx);
173  bool isMulti = false;
174  for ( QModelIndex treewalkidx = pidx; treewalkidx.isValid(); treewalkidx = treewalkidx.parent() ) {
175  if ( treewalkidx.data( ItemTypeRole ).toInt() == TypeMulti
176  && !gfxview->rowController()->isRowExpanded( treewalkidx ) ) {
177  isMulti = true;
178  break;
179  }
180  }
181 
182  if ( !isMulti ) {
183  for ( int i = 0; i < model->rowCount( idx ); ++i ) {
184  gfxview->deleteSubtree( ganttProxyModel.index( i, 0, pidx ) );
185  }
186  } else {
187  gfxview->updateRow(pidx);
188  }
189  //qDebug() << "Looking to update from " << idx;
190  while ( ( idx=tw->indexBelow( idx ) ) != QModelIndex() &&
191  gfxview->rowController()->isRowVisible( ganttProxyModel.mapFromSource(idx) ) ) {
192  const QModelIndex proxyidx( ganttProxyModel.mapFromSource( idx ) );
193  gfxview->updateRow(proxyidx);
194  }
195  gfxview->blockSignals( blocked );
196  gfxview->updateSceneRect();
197 }
198 
199 void View::Private::slotExpanded(const QModelIndex& _idx)
200 {
201  QModelIndex idx( ganttProxyModel.mapFromSource( _idx ) );
202  do {
203  //qDebug() << "Updating row" << idx << idx.data( Qt::DisplayRole ).toString();
204  gfxview->updateRow(idx);
205  } while ( ( idx=gfxview->rowController()->indexBelow( idx ) ) != QModelIndex()
206  && gfxview->rowController()->isRowVisible( idx ) );
207  gfxview->updateSceneRect();
208 }
209 
210 void View::Private::slotVerticalScrollValueChanged( int val )
211 {
212 #if 0
213  qDebug() << "View::Private::slotVerticalScrollValueChanged("<<val<<")="
214  << val/gfxview->verticalScrollBar()->singleStep();
215 #endif
216  leftWidget->verticalScrollBar()->setValue( val/gfxview->verticalScrollBar()->singleStep() );
217 }
218 
219 void View::Private::slotLeftWidgetVerticalRangeChanged(int min, int max )
220 {
221  //qDebug() << "View::Private::slotLeftWidgetVerticalRangeChanged("<<min<<max<<")";
222  gfxview->verticalScrollBar()->setRange( min, max );
223  gfxview->updateSceneRect();
224 }
225 
226 void View::Private::slotGfxViewVerticalRangeChanged( int min, int max )
227 {
228  //qDebug() << "View::Private::slotGfxViewVerticalRangeChanged("<<min<<max<<")";
229  if ( !leftWidget.isNull() && !gfxview.isNull() ) {
230  int leftMin = leftWidget->verticalScrollBar()->minimum();
231  int leftMax = leftWidget->verticalScrollBar()->maximum();
232  bool blocked = gfxview->verticalScrollBar()->blockSignals( true );
233  gfxview->verticalScrollBar()->setRange( qMax( min, leftMin ), qMax( max, leftMax ) );
234  gfxview->verticalScrollBar()->blockSignals( blocked );
235  }
236 }
237 
252  : QWidget(parent),
253  _d(new Private(this))
254 {
255 #if defined KDAB_EVAL
256  EvalDialog::checkEvalLicense( "KD Gantt" );
257 #endif
258  _d->init();
259 }
260 
262 {
263  delete _d;
264 }
265 
266 #define d d_func()
267 
274 {
275  assert( aiv );
276  if ( aiv==d->leftWidget ) return;
277  if ( !d->leftWidget.isNull() ) {
278  d->leftWidget->disconnect( this );
279  d->leftWidget->hide();
280  d->leftWidget->verticalScrollBar()->disconnect( d->gfxview->verticalScrollBar() );
281  d->gfxview->verticalScrollBar()->disconnect( d->leftWidget->verticalScrollBar() );
282  }
283 
284  d->leftWidget = aiv;
285  d->splitter.insertWidget( 0, d->leftWidget );
286 
287  if ( qobject_cast<QTreeView*>(d->leftWidget) ) {
288  connect( d->leftWidget, SIGNAL( collapsed( const QModelIndex& ) ),
289  this, SLOT( slotCollapsed( const QModelIndex& ) ) );
290  connect( d->leftWidget, SIGNAL( expanded( const QModelIndex& ) ),
291  this, SLOT( slotExpanded( const QModelIndex& ) ) );
292  }
293 
294  connect( d->gfxview->verticalScrollBar(), SIGNAL( valueChanged( int ) ),
295  d->leftWidget->verticalScrollBar(), SLOT( setValue( int ) ) );
296  connect( d->leftWidget->verticalScrollBar(), SIGNAL( valueChanged( int ) ),
297  d->gfxview->verticalScrollBar(), SLOT( setValue( int ) ) );
298  connect( d->leftWidget->verticalScrollBar(), SIGNAL( rangeChanged( int, int ) ),
299  this, SLOT( slotLeftWidgetVerticalRangeChanged( int, int ) ) );
300  connect( d->gfxview->verticalScrollBar(), SIGNAL( rangeChanged( int, int ) ),
301  this, SLOT( slotGfxViewVerticalRangeChanged( int, int ) ) );
302 }
303 
310 {
311  if ( ctrl == d->rowController && d->gfxview->rowController() == ctrl ) return;
312  d->rowController = ctrl;
313  d->gfxview->setRowController( d->rowController );
314 }
315 
320 {
321  return d->rowController;
322 }
323 
327 {
328  return d->rowController;
329 }
330 
336 {
337  return d->leftWidget;
338 }
339 
344 {
345  return d->leftWidget;
346 }
347 
355 {
356  if ( gv != d->gfxview ) {
357  GraphicsView* old = d->gfxview;
358  d->gfxview = gv;
359  d->setupGraphicsView();
360  d->gfxview->setGrid( old->grid() );
361  delete old;
362  }
363 }
364 
369 {
370  return d->gfxview;
371 }
372 
377 {
378  return d->gfxview;
379 }
380 
384 const QSplitter* View::splitter() const
385 {
386  return &d->splitter;
387 }
388 
392 QSplitter* View::splitter()
393 {
394  return &d->splitter;
395 }
396 
397 
400 QAbstractItemModel* View::model() const
401 {
402  return leftView()->model();
403 }
404 
410 void View::setModel( QAbstractItemModel* model )
411 {
412  leftView()->setModel( model );
413  d->ganttProxyModel.setSourceModel( model );
414  d->gfxview->setModel( &d->ganttProxyModel );
415 }
416 
419 QItemSelectionModel* View::selectionModel() const
420 {
421  return leftView()->selectionModel();
422 }
423 
427 void View::setSelectionModel( QItemSelectionModel* smodel )
428 {
429  leftView()->setSelectionModel( smodel );
430  d->gfxview->setSelectionModel( new QItemSelectionModel( &( d->ganttProxyModel ),this ) );
431 }
432 
439 {
440  d->gfxview->setGrid( grid );
441 }
442 
443 void View::expandAll( QModelIndex index )
444 {
445  KDGanttTreeView* tw = qobject_cast<KDGanttTreeView*>(leftView());
446  tw->expandAll(index);
447 }
448 
449 void View::collapseAll( QModelIndex index )
450 {
451  KDGanttTreeView* tw = qobject_cast<KDGanttTreeView*>(leftView());
452  tw->collapseAll(index);
453 }
454 
458 {
459  return d->gfxview->grid();
460 }
461 
464 QModelIndex View::rootIndex() const
465 {
466  return leftView()->rootIndex();
467 }
468 
472 void View::setRootIndex( const QModelIndex& idx )
473 {
474  leftView()->setRootIndex( idx );
475  d->gfxview->setRootIndex( idx );
476 }
477 
481 {
482  return d->gfxview->itemDelegate();
483 }
484 
489 {
490  leftView()->setItemDelegate( delegate );
491  d->gfxview->setItemDelegate( delegate );
492 }
493 
498 {
499  d->constraintProxy.setSourceModel( cm );
500  d->gfxview->setConstraintModel( &d->mappedConstraintModel );
501 }
502 
506 {
507  return d->constraintProxy.sourceModel();
508 }
509 
511 {
512  return &( d->ganttProxyModel );
513 }
514 
516 {
517  return &( d->ganttProxyModel );
518 }
519 
520 void View::ensureVisible(const QModelIndex& index)
521 {
522  QGraphicsView* view = graphicsView();
523  KDGantt::GraphicsScene* scene = static_cast<KDGantt::GraphicsScene*>(view->scene());
524  if (!scene)
525  return;
526 
528 
529  const QModelIndex pidx = d->ganttProxyModel.mapFromSource(index);
530  const QModelIndex idx = model->mapFromSource( pidx );
531  QGraphicsItem* item = scene->findItem(idx);
532  view->ensureVisible(item);
533 }
534 
535 void View::resizeEvent(QResizeEvent*ev)
536 {
537  QWidget::resizeEvent(ev);
538 }
539 
546 QModelIndex View::indexAt( const QPoint& pos ) const
547 {
548  return d->gfxview->indexAt( pos );
549 }
550 
559 void View::print( QPrinter* printer, bool drawRowLabels, bool drawColumnLabels )
560 {
561  graphicsView()->print( printer, drawRowLabels, drawColumnLabels );
562 }
563 
576 void View::print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels )
577 {
578  graphicsView()->print( printer, start, end, drawRowLabels, drawColumnLabels );
579 }
580 
587 void View::print( QPainter* painter, const QRectF& target, bool drawRowLabels, bool drawColumnLabels)
588 {
589  d->gfxview->print( painter,
590  target,
591  drawRowLabels,
592  drawColumnLabels);
593 }
594 
605 void View::print( QPainter* painter, qreal start, qreal end, const QRectF& target, bool drawRowLabels, bool drawColumnLabels)
606 {
607  d->gfxview->print( painter,
608  start, end,
609  target,
610  drawRowLabels,
611  drawColumnLabels);
612 }
613 
614 
615 #include "moc_kdganttview.cpp"
616 
617 #ifndef KDAB_NO_UNIT_TESTS
618 #include "unittest/test.h"
619 
621 #include <QApplication>
622 #include <QTimer>
623 #include <QPixmap>
624 #include <QListView>
625 
627  View view( 0 );
628 #if 0 // GUI tests do not work well on the server
629  QTimer::singleShot( 1000, qApp, SLOT( quit() ) );
630  view.show();
631 
632  qApp->exec();
633  QPixmap screenshot1 = QPixmap::grabWidget( &view );
634 
635  QTreeView* tv = new QTreeView;
636  view.setLeftView( tv );
638 
639  QTimer::singleShot( 1000, qApp, SLOT( quit() ) );
640 
641  qApp->exec();
642  QPixmap screenshot2 = QPixmap::grabWidget( &view );
643 
644  assertEqual( screenshot1.toImage(), screenshot2.toImage() );
645 
646  QListView* lv = new QListView;
647  view.setLeftView(lv);
649  view.show();
650  QTimer::singleShot( 1000, qApp, SLOT( quit() ) );
651  qApp->exec();
652 #endif
653 }
654 #endif /* KDAB_NO_UNIT_TESTS */
QAbstractProxyModel * summaryHandlingModel() const
~View() override
void setLeftView(QAbstractItemView *)
QModelIndex indexAt(int height) const override
Proxy model that supports summary gantt items.
void setSelectionModel(QItemSelectionModel *smodel)
void setItemDelegate(ItemDelegate *)
const QAbstractProxyModel * ganttProxyModel() const
#define d
Abstract baseclass for grids. A grid is used to convert between QModelIndex&#39;es and gantt chart values...
ConstraintModel * constraintModel() const
void print(QPrinter *printer, bool drawRowLabels=true, bool drawColumnLabels=true)
QAbstractItemModel * model() const
Class used to render gantt items in a KDGantt::GraphicsView.
GraphicsItem * findItem(const QModelIndex &) const
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override
const GraphicsView * graphicsView() const
void ensureVisible(const QModelIndex &index)
void setModel(QAbstractItemModel *model)
ItemDelegate * itemDelegate() const
Class only listed here to document inheritance of some KDChart classes.
const QAbstractItemView * leftView() const
void setConstraintModel(ConstraintModel *)
Class only listed here to document inheritance of some KDChart classes.
The GraphicsView class provides a model/view implementation of a gantt chart.
AbstractGrid * grid() const
View(QWidget *parent=0)
void expandAll(QModelIndex index=QModelIndex())
void print(QPrinter *printer, bool drawRowLabels=true, bool drawColumnLabels=true)
void collapseAll(QModelIndex index=QModelIndex())
void setGrid(AbstractGrid *)
AbstractRowController * rowController()
KDAB_SCOPED_UNITTEST_SIMPLE(KDGantt, View,"test")
const QSplitter * splitter() const
QModelIndex indexAt(const QPoint &pos) const
void resizeEvent(QResizeEvent *) override
This widget that consists of a QTreeView and a GraphicsView.
Definition: kdganttview.h:47
AbstractGrid * grid() const
Class only listed here to document inheritance of some KDChart classes.
void setRowController(AbstractRowController *)
QItemSelectionModel * selectionModel() const
void setRootIndex(const QModelIndex &idx)
QModelIndex rootIndex() const
void setGraphicsView(GraphicsView *)
Abstract baseclass for row controllers. A row controller is used by the GraphicsView to nagivate the ...
#define assertEqual(x, y)
Definition: test.h:43

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/