KGantt

kganttview.cpp
1 /*
2  * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
3  *
4  * This file is part of the KGantt library.
5  *
6  * SPDX-License-Identifier: GPL-2.0-or-later
7  */
8 
9 #include "kganttview.h"
10 #include "kganttview_p.h"
11 
12 #include "kganttitemdelegate.h"
13 #include "kganttgraphicsitem.h"
14 #include "kganttsummaryhandlingproxymodel.h"
15 
16 #include <QAbstractItemModel>
17 #include <QHeaderView>
18 #include <QVBoxLayout>
19 #include <QGraphicsItem>
20 #include <QGraphicsRectItem>
21 #include <QScrollBar>
22 #include <QPaintEvent>
23 
24 #include <QDebug>
25 
26 #include <cassert>
27 
28 #if defined KDAB_EVAL
29 #include "../evaldialog/evaldialog.h"
30 #endif
31 
32 using namespace KGantt;
33 
34 namespace {
35  class HeaderView : public QHeaderView {
36  public:
37  explicit HeaderView( QWidget* parent=nullptr ) : QHeaderView( Qt::Horizontal, parent ) {
38  }
39 
40  QSize sizeHint() const override { QSize s = QHeaderView::sizeHint(); s.rheight() *= 2; return s; }
41  };
42 }
43 
44 KGanttTreeView::KGanttTreeView( QAbstractProxyModel* proxy, QWidget* parent )
45  : QTreeView( parent ),
46  m_controller( this, proxy )
47 {
48  setHeader( new HeaderView );
49 }
50 
51 KGanttTreeView::~KGanttTreeView()
52 {
53 }
54 
55 void KGanttTreeView::expandAll(QModelIndex index)
56 {
57  for (int i = 0; i < model()->rowCount(index); i++) {
58  QModelIndex indexAt = model()->index(i, 0, index);
59  if (model()->hasChildren(indexAt))
60  expandAll(indexAt);
61  if (isExpanded(indexAt))
62  continue;
63  expand(indexAt);
64  }
65 }
66 
67 void KGanttTreeView::collapseAll(QModelIndex index)
68 {
69  for (int i = 0; i < model()->rowCount(index); i++) {
70  QModelIndex indexAt = model()->index(i, 0, index);
71  if (model()->hasChildren(indexAt))
72  collapseAll(indexAt);
73  if (!isExpanded(indexAt))
74  continue;
75  collapse(indexAt);
76  }
77 }
78 
79 View::Private::Private(View* v)
80  : q(v),
81  splitter(v),
82  rowController(nullptr),
83  gfxview( new GraphicsView( &splitter ) ),
84  model(nullptr)
85 {
86  //init();
87 }
88 
89 View::Private::~Private()
90 {
91  delete gfxview;
92 }
93 
94 void View::Private::init()
95 {
96  KGanttTreeView* tw = new KGanttTreeView( &ganttProxyModel, &splitter );
97  tw->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
98  tw->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
99 
100  q->setLeftView( tw );
101  q->setRowController( tw->rowController() );
102 
103  //gfxview.setRenderHints( QPainter::Antialiasing );
104 
105  tw->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
106 
107  QVBoxLayout* layout = new QVBoxLayout(q);
108  layout->setContentsMargins(0, 0, 0, 0);
109  layout->addWidget(&splitter);
110  q->setLayout(layout);
111 
112  constraintProxy.setProxyModel( &ganttProxyModel );
113  constraintProxy.setDestinationModel( &mappedConstraintModel );
114  setupGraphicsView();
115 }
116 
117 void View::Private::setupGraphicsView()
118 {
119  gfxview->setParent( &splitter );
120  gfxview->setAlignment(Qt::AlignTop|Qt::AlignLeft);
121  gfxview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
122  gfxview->setSelectionModel( leftWidget->selectionModel() );
123  gfxview->setConstraintModel( &mappedConstraintModel );
124  q->setLeftView( leftWidget );
125  q->setRowController( rowController );
126  updateScene();
127 }
128 
129 void View::Private::updateScene()
130 {
131  gfxview->clearItems();
132  if ( !model) return;
133 
134  if ( QTreeView* tw = qobject_cast<QTreeView*>(leftWidget)) {
135  QModelIndex idx = ganttProxyModel.mapFromSource( model->index( 0, 0, leftWidget->rootIndex() ) );
136  do {
137  gfxview->updateRow( idx );
138  } while ( ( idx = tw->indexBelow( idx ) ) != QModelIndex() &&
139  gfxview->rowController()->isRowVisible(idx) );
140  gfxview->updateSceneRect();
141  } else {
142  const QModelIndex rootidx = ganttProxyModel.mapFromSource( leftWidget->rootIndex() );
143  for ( int r = 0; r < ganttProxyModel.rowCount(rootidx); ++r ) {
144  gfxview->updateRow( ganttProxyModel.index( r, 0, rootidx ) );
145  }
146  }
147 }
148 
149 void View::Private::slotCollapsed(const QModelIndex& _idx)
150 {
151  QTreeView* tw = qobject_cast<QTreeView*>(leftWidget);
152  if (!tw) return;
153 
154  bool blocked = gfxview->blockSignals( true );
155 
156  QModelIndex idx( _idx );
157  const QAbstractItemModel* model = leftWidget->model();
158  const QModelIndex pidx = ganttProxyModel.mapFromSource(idx);
159  bool isMulti = false;
160  for ( QModelIndex treewalkidx = pidx; treewalkidx.isValid(); treewalkidx = treewalkidx.parent() ) {
161  if ( treewalkidx.data( ItemTypeRole ).toInt() == TypeMulti
162  && !gfxview->rowController()->isRowExpanded( treewalkidx ) ) {
163  isMulti = true;
164  break;
165  }
166  }
167 
168  if ( !isMulti ) {
169  for ( int i = 0; i < model->rowCount( idx ); ++i ) {
170  gfxview->deleteSubtree( ganttProxyModel.index( i, 0, pidx ) );
171  }
172  } else {
173  gfxview->updateRow(pidx);
174  }
175  //qDebug() << "Looking to update from " << idx;
176  while ( ( idx=tw->indexBelow( idx ) ) != QModelIndex() &&
177  gfxview->rowController()->isRowVisible( ganttProxyModel.mapFromSource(idx) ) ) {
178  const QModelIndex proxyidx( ganttProxyModel.mapFromSource( idx ) );
179  gfxview->updateRow(proxyidx);
180  }
181  gfxview->blockSignals( blocked );
182  gfxview->updateSceneRect();
183 }
184 
185 void View::Private::slotExpanded(const QModelIndex& _idx)
186 {
187  QModelIndex idx( ganttProxyModel.mapFromSource( _idx ) );
188  do {
189  //qDebug() << "Updating row" << idx << idx.data( Qt::DisplayRole ).toString();
190  gfxview->updateRow(idx);
191  } while ( ( idx=gfxview->rowController()->indexBelow( idx ) ) != QModelIndex()
192  && gfxview->rowController()->isRowVisible( idx ) );
193  gfxview->updateSceneRect();
194 }
195 
196 void View::Private::slotVerticalScrollValueChanged( int val )
197 {
198 #if 0
199  qDebug() << "View::Private::slotVerticalScrollValueChanged("<<val<<")="
200  << val/gfxview->verticalScrollBar()->singleStep();
201 #endif
202  leftWidget->verticalScrollBar()->setValue( val/gfxview->verticalScrollBar()->singleStep() );
203 }
204 
205 void View::Private::slotLeftWidgetVerticalRangeChanged(int min, int max )
206 {
207  //qDebug() << "View::Private::slotLeftWidgetVerticalRangeChanged("<<min<<max<<")";
208  // In some cases the gfxview has already been deleted when this signal arrive
209  if (!gfxview.isNull()) {
210  gfxview->verticalScrollBar()->setRange( min, max );
211  gfxview->updateSceneRect();
212  }
213 }
214 
215 void View::Private::slotGfxViewVerticalRangeChanged( int min, int max )
216 {
217  //qDebug() << "View::Private::slotGfxViewVerticalRangeChanged("<<min<<max<<")";
218  if ( !leftWidget.isNull() && !gfxview.isNull() ) {
219  int leftMin = leftWidget->verticalScrollBar()->minimum();
220  int leftMax = leftWidget->verticalScrollBar()->maximum();
221  bool blocked = gfxview->verticalScrollBar()->blockSignals( true );
222  gfxview->verticalScrollBar()->setRange( qMax( min, leftMin ), qMax( max, leftMax ) );
223  gfxview->verticalScrollBar()->blockSignals( blocked );
224  }
225 }
226 
227 
228 
229 
231  : QWidget(parent),
232  _d(new Private(this))
233 {
234 #if defined KDAB_EVAL
235  EvalDialog::checkEvalLicense( "KD Gantt" );
236 #endif
237  _d->init();
238 }
239 
240 View::~View()
241 {
242  delete _d;
243 }
244 
245 #define d d_func()
246 
247 
249 {
250  assert( aiv );
251  if ( aiv==d->leftWidget ) return;
252  if ( !d->leftWidget.isNull() ) {
253  d->leftWidget->disconnect( this );
254  d->leftWidget->hide();
255  d->leftWidget->verticalScrollBar()->disconnect( d->gfxview->verticalScrollBar() );
256  d->gfxview->verticalScrollBar()->disconnect( d->leftWidget->verticalScrollBar() );
257  }
258 
259  d->leftWidget = aiv;
260  d->splitter.insertWidget( 0, d->leftWidget );
261 
262  if ( qobject_cast<QTreeView*>(d->leftWidget) ) {
263  connect( d->leftWidget, SIGNAL(collapsed(QModelIndex)),
264  this, SLOT(slotCollapsed(QModelIndex)) );
265  connect( d->leftWidget, SIGNAL(expanded(QModelIndex)),
266  this, SLOT(slotExpanded(QModelIndex)) );
267  }
268 
269  connect( d->gfxview->verticalScrollBar(), SIGNAL(valueChanged(int)),
270  d->leftWidget->verticalScrollBar(), SLOT(setValue(int)) );
271  connect( d->leftWidget->verticalScrollBar(), SIGNAL(valueChanged(int)),
272  d->gfxview->verticalScrollBar(), SLOT(setValue(int)) );
273  connect( d->leftWidget->verticalScrollBar(), SIGNAL(rangeChanged(int,int)),
274  this, SLOT(slotLeftWidgetVerticalRangeChanged(int,int)) );
275  connect( d->gfxview->verticalScrollBar(), SIGNAL(rangeChanged(int,int)),
276  this, SLOT(slotGfxViewVerticalRangeChanged(int,int)) );
277 }
278 
279 
281 {
282  if ( ctrl == d->rowController && d->gfxview->rowController() == ctrl ) return;
283  d->rowController = ctrl;
284  d->gfxview->setRowController( d->rowController );
285 }
286 
287 
289 {
290  return d->rowController;
291 }
292 
293 
295 {
296  return d->rowController;
297 }
298 
299 
301 {
302  return d->leftWidget;
303 }
304 
305 
307 {
308  return d->leftWidget;
309 }
310 
311 
313 {
314  if ( gv != d->gfxview ) {
315  GraphicsView* old = d->gfxview;
316  AbstractGrid *grid = old->takeGrid();
317  d->gfxview = gv;
318  d->gfxview->setModel(old->model()); // use the old ForwardingProxyModel
319  d->setupGraphicsView();
320  d->gfxview->setGrid( grid );
321  delete old;
322  }
323 }
324 
325 
327 {
328  return d->gfxview;
329 }
330 
331 
333 {
334  return d->gfxview;
335 }
336 
337 
338 const QSplitter* View::splitter() const
339 {
340  return &d->splitter;
341 }
342 
343 
345 {
346  return &d->splitter;
347 }
348 
349 
350 
352 {
353  return leftView()->model();
354 }
355 
356 
358 {
359  leftView()->setModel( model );
360  d->ganttProxyModel.setSourceModel( model );
361  d->gfxview->setModel( &d->ganttProxyModel );
362 }
363 
364 
366 {
367  return leftView()->selectionModel();
368 }
369 
370 
372 {
373  leftView()->setSelectionModel( smodel );
374  d->gfxview->setSelectionModel( new QItemSelectionModel( &( d->ganttProxyModel ),this ) );
375 }
376 
377 
379 {
380  d->gfxview->setGrid( grid );
381 }
382 
383 void View::expandAll( QModelIndex index )
384 {
385  // FIXME:
386  // It is legal to call setLeftView() with any QAbstractItemView,
387  // so expandAll should be reimplemented to work with that.
388  KGanttTreeView* tw = qobject_cast<KGanttTreeView*>(leftView());
389  if (tw) {
390  tw->expandAll(index);
391  }
392 }
393 
394 void View::collapseAll( QModelIndex index )
395 {
396  // FIXME:
397  // It is legal to call setLeftView() with any QAbstractItemView,
398  // so expandAll should be reimplemented to work with that.
399  KGanttTreeView* tw = qobject_cast<KGanttTreeView*>(leftView());
400  if (tw) {
401  tw->collapseAll(index);
402  }
403 }
404 
405 
407 {
408  return d->gfxview->grid();
409 }
410 
411 
413 {
414  return leftView()->rootIndex();
415 }
416 
417 
418 void View::setRootIndex( const QModelIndex& idx )
419 {
420  leftView()->setRootIndex( idx );
421  d->gfxview->setRootIndex( idx );
422 }
423 
424 
426 {
427  return d->gfxview->itemDelegate();
428 }
429 
430 
432 {
433  leftView()->setItemDelegate( delegate );
434  d->gfxview->setItemDelegate( delegate );
435 }
436 
437 
439 {
440  d->constraintProxy.setSourceModel( cm );
441  d->gfxview->setConstraintModel( &d->mappedConstraintModel );
442 }
443 
444 
446 {
447  return d->constraintProxy.sourceModel();
448 }
449 
450 const QAbstractProxyModel* View::ganttProxyModel() const
451 {
452  return &( d->ganttProxyModel );
453 }
454 
455 QAbstractProxyModel* View::ganttProxyModel()
456 {
457  return &( d->ganttProxyModel );
458 }
459 
460 void View::ensureVisible(const QModelIndex& index)
461 {
462  QGraphicsView* view = graphicsView();
463  KGantt::GraphicsScene* scene = static_cast<KGantt::GraphicsScene*>(view->scene());
464  if (!scene)
465  return;
466 
467  KGantt::SummaryHandlingProxyModel* model = static_cast<KGantt::SummaryHandlingProxyModel*>(scene->summaryHandlingModel());
468 
469  const QModelIndex pidx = d->ganttProxyModel.mapFromSource(index);
470  const QModelIndex idx = model->mapFromSource( pidx );
471  QGraphicsItem* item = scene->findItem(idx);
472  view->ensureVisible(item);
473 }
474 
475 void View::resizeEvent(QResizeEvent*ev)
476 {
478 }
479 
480 
482 {
483  return d->gfxview->indexAt( pos );
484 }
485 
486 
487 void View::print( QPrinter* printer, bool drawRowLabels, bool drawColumnLabels )
488 {
489  graphicsView()->print( printer, drawRowLabels, drawColumnLabels );
490 }
491 
492 
493 void View::print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels )
494 {
495  graphicsView()->print( printer, start, end, drawRowLabels, drawColumnLabels );
496 }
497 
498 
499 void View::print( QPainter* painter, const QRectF& target, bool drawRowLabels, bool drawColumnLabels)
500 {
501  d->gfxview->print( painter,
502  target,
503  drawRowLabels,
504  drawColumnLabels);
505 }
506 
507 
508 void View::print( QPainter* painter, qreal start, qreal end, const QRectF& target, bool drawRowLabels, bool drawColumnLabels)
509 {
510  d->gfxview->print( painter,
511  start, end,
512  target,
513  drawRowLabels,
514  drawColumnLabels);
515 }
516 
517 void View::printDiagram( QPrinter *printer, const PrintingContext &context )
518 {
519  graphicsView()->printDiagram( printer, context );
520 }
521 
522 #include "moc_kganttview.cpp"
523 
524 #ifndef KDAB_NO_UNIT_TESTS
525 #include "unittest/test.h"
526 
527 #include "kganttlistviewrowcontroller.h"
528 #include <QApplication>
529 #include <QTimer>
530 #include <QPixmap>
531 #include <QListView>
532 
533 KDAB_SCOPED_UNITTEST_SIMPLE( KGantt, View, "test" ) {
534  View view( nullptr );
535 #if 0 // GUI tests do not work well on the server
536  QTimer::singleShot( 1000, qApp, SLOT(quit()) );
537  view.show();
538 
539  qApp->exec();
540  QPixmap screenshot1 = QPixmap::grabWidget( &view );
541 
542  QTreeView* tv = new QTreeView;
543  view.setLeftView( tv );
544  view.setRowController( new TreeViewRowController(tv,view.ganttProxyModel()) );
545 
546  QTimer::singleShot( 1000, qApp, SLOT(quit()) );
547 
548  qApp->exec();
549  QPixmap screenshot2 = QPixmap::grabWidget( &view );
550 
551  assertEqual( screenshot1.toImage(), screenshot2.toImage() );
552 
553  QListView* lv = new QListView;
554  view.setLeftView(lv);
555  view.setRowController( new ListViewRowController(lv,view.ganttProxyModel()));
556  view.show();
557  QTimer::singleShot( 1000, qApp, SLOT(quit()) );
558  qApp->exec();
559 #endif
560 }
561 #endif /* KDAB_NO_UNIT_TESTS */
AbstractRowController * rowController()
Definition: kganttview.cpp:288
virtual void setRootIndex(const QModelIndex &index)
void setModel(QAbstractItemModel *model)
Definition: kganttview.cpp:357
virtual void setSelectionModel(QItemSelectionModel *selectionModel)
ScrollBarAlwaysOff
The item type.
Definition: kganttglobal.h:217
View(QWidget *parent=nullptr)
Definition: kganttview.cpp:230
virtual int rowCount(const QModelIndex &parent) const const =0
void setContentsMargins(int left, int top, int right, int bottom)
QAbstractItemModel * model() const
AbstractGrid * grid() const
Definition: kganttview.cpp:406
QItemSelectionModel * selectionModel() const const
QPixmap grabWidget(QObject *widget, const QRect &rectangle)
void print(QPrinter *printer, bool drawRowLabels=true, bool drawColumnLabels=true)
void setSelectionModel(QItemSelectionModel *smodel)
Definition: kganttview.cpp:371
virtual QSize sizeHint() const const override
void setGraphicsView(GraphicsView *)
Definition: kganttview.cpp:312
virtual void setModel(QAbstractItemModel *model)
Abstract baseclass for row controllers. A row controller is used by the GraphicsView to nagivate the ...
void setConstraintModel(KGantt::ConstraintModel *)
Definition: kganttview.cpp:438
QAbstractItemModel * model() const
Definition: kganttview.cpp:351
QModelIndex indexBelow(const QModelIndex &index) const const
void print(QPrinter *printer, bool drawRowLabels=true, bool drawColumnLabels=true)
Definition: kganttview.cpp:487
QModelIndex rootIndex() const
Definition: kganttview.cpp:412
QGraphicsScene * scene() const const
AlignTop
int & rheight()
const QSplitter * splitter() const
Definition: kganttview.cpp:338
bool isValid() const const
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
QModelIndex indexAt(const QPoint &pos) const
Definition: kganttview.cpp:481
void printDiagram(QPrinter *printer, const PrintingContext &context)
void setGrid(KGantt::AbstractGrid *)
Definition: kganttview.cpp:378
void setItemDelegate(QAbstractItemDelegate *delegate)
void setRowController(AbstractRowController *)
Definition: kganttview.cpp:280
void setItemDelegate(KGantt::ItemDelegate *)
Definition: kganttview.cpp:431
QPoint pos() const const
void setRootIndex(const QModelIndex &idx)
Definition: kganttview.cpp:418
void setLeftView(QAbstractItemView *)
Definition: kganttview.cpp:248
Global namespace.
ConstraintModel * constraintModel() const
Definition: kganttview.cpp:445
bool blockSignals(bool block)
const QAbstractItemView * leftView() const
Definition: kganttview.cpp:300
Abstract baseclass for grids. A grid is used to convert between QModelIndex&#39;es and gantt chart values...
QModelIndex rootIndex() const const
The PrintingContext class provides options for printing the gantt chart.
void ensureVisible(const QRectF &rect, int xmargin, int ymargin)
QItemSelectionModel * selectionModel() const
Definition: kganttview.cpp:365
void printDiagram(QPrinter *printer, const PrintingContext &context=PrintingContext())
Definition: kganttview.cpp:517
ItemDelegate * itemDelegate() const
Definition: kganttview.cpp:425
const GraphicsView * graphicsView() const
Definition: kganttview.cpp:326
void show()
Horizontal
QImage toImage() const const
The GraphicsView class provides a model/view implementation of a gantt chart.
virtual void resizeEvent(QResizeEvent *event)
QAbstractItemModel * model() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
T qobject_cast(QObject *object)
Class used to render gantt items in a KGantt::GraphicsView.
Proxy model that supports summary gantt items.
This widget that consists of a QTreeView and a GraphicsView.
Definition: kganttview.h:44
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun Dec 5 2021 22:32:42 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.