KD Chart 2  [rev.2.7]
kdganttgraphicsscene.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 "kdganttgraphicsscene.h"
24 #include "kdganttgraphicsscene_p.h"
25 #include "kdganttgraphicsitem.h"
26 #include "kdganttconstraint.h"
28 #include "kdganttitemdelegate.h"
30 #include "kdganttdatetimegrid.h"
32 
33 #include <QApplication>
34 #include <QGraphicsSceneHelpEvent>
35 #include <QPainter>
36 #include <QPrinter>
37 #include <QTextDocument>
38 #include <QToolTip>
39 #include <QSet>
40 
41 #include <QDebug>
42 
43 #include <functional>
44 #include <algorithm>
45 #include <cassert>
46 
47 /* Older Qt don't have this macro, so define it... */
48 #ifndef QT_VERSION_CHECK
49 # define QT_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch))
50 #endif
51 
52 // defines HAVE_PRINTER if support for printing should be included
53 #ifdef _WIN32_WCE
54  // There is no printer support under wince even if QT_NO_PRINTER is not set
55 #else
56 #ifndef QT_NO_PRINTER
57  #define HAVE_PRINTER
58 #endif
59 #endif
60 
65 using namespace KDGantt;
66 
67 GraphicsScene::Private::Private( GraphicsScene* _q )
68  : q( _q ),
69  dragSource( 0 ),
70  itemDelegate( new ItemDelegate( _q ) ),
71  rowController( 0 ),
72  grid( &default_grid ),
73  readOnly( false ),
74  isPrinting( false ),
75  drawColumnLabels( true ),
76  labelsWidth( 0.0 ),
77  summaryHandlingModel( new SummaryHandlingProxyModel( _q ) ),
78  selectionModel( 0 )
79 {
80  default_grid.setStartDateTime( QDateTime::currentDateTime().addDays( -1 ) );
81 }
82 
83 void GraphicsScene::Private::resetConstraintItems()
84 {
85  q->clearConstraintItems();
86  if ( constraintModel.isNull() ) return;
87  QList<Constraint> clst = constraintModel->constraints();
88  Q_FOREACH( const Constraint& c, clst ) {
89  createConstraintItem( c );
90  }
91  q->updateItems();
92 }
93 
94 void GraphicsScene::Private::createConstraintItem( const Constraint& c )
95 {
96  GraphicsItem* sitem = q->findItem( summaryHandlingModel->mapFromSource( c.startIndex() ) );
97  GraphicsItem* eitem = q->findItem( summaryHandlingModel->mapFromSource( c.endIndex() ) );
98 
99  if ( sitem && eitem ) {
101  sitem->addStartConstraint( citem );
102  eitem->addEndConstraint( citem );
103  q->addItem( citem );
104  }
105 
106  //q->insertConstraintItem( c, citem );
107 }
108 
109 // Delete the constraint item, and clean up pointers in the start- and end item
110 void GraphicsScene::Private::deleteConstraintItem( ConstraintGraphicsItem *citem )
111 {
112  //qDebug()<<"GraphicsScene::Private::deleteConstraintItem citem="<<citem;
113  if ( citem == 0 ) {
114  return;
115  }
116  Constraint c = citem->constraint();
117  GraphicsItem* item = items.value( summaryHandlingModel->mapFromSource( c.startIndex() ), 0 );
118  if ( item ) {
119  item->removeStartConstraint( citem );
120  }
121  item = items.value( summaryHandlingModel->mapFromSource( c.endIndex() ), 0 );
122  if ( item ) {
123  item->removeEndConstraint( citem );
124  }
125  delete citem;
126 }
127 
128 void GraphicsScene::Private::deleteConstraintItem( const Constraint& c )
129 {
130  deleteConstraintItem( findConstraintItem( c ) );
131 }
132 
133 ConstraintGraphicsItem* GraphicsScene::Private::findConstraintItem( const Constraint& c ) const
134 {
135  GraphicsItem* item = items.value( summaryHandlingModel->mapFromSource( c.startIndex() ), 0 );
136  if ( item ) {
139  for ( ; it != clst.end() ; ++it ) {
140  if ( c.compareIndexes((*it)->constraint()) )
141  break;
142  }
143  if ( it != clst.end() ) {
144  return *it;
145  }
146  }
147  item = items.value( summaryHandlingModel->mapFromSource( c.endIndex() ), 0 );
148  if ( item ) {
149  const QList<ConstraintGraphicsItem*> clst = item->endConstraints();
151  for ( ; it != clst.end() ; ++it ) {
152  if ( c.compareIndexes( (*it)->constraint() ) )
153  break;
154  }
155  if ( it != clst.end() ) {
156  return *it;
157  }
158  }
159  return 0;
160 }
161 
162 GraphicsScene::GraphicsScene( QObject* parent )
163  : QGraphicsScene( parent ), _d( new Private( this ) )
164 {
165  init();
166 }
167 
169 {
171  qDeleteAll( items() );
172  delete _d;
173 }
174 
175 #define d d_func()
176 
177 void GraphicsScene::init()
178 {
179  setItemIndexMethod( QGraphicsScene::NoIndex );
180  setConstraintModel( new ConstraintModel( this ) );
181  connect( d->grid, SIGNAL( gridChanged() ), this, SLOT( slotGridChanged() ) );
182 }
183 
184 /* NOTE: The delegate should really be a property
185  * of the view, but that doesn't really fit at
186  * this time
187  */
189 {
190  if ( !d->itemDelegate.isNull() && d->itemDelegate->parent()==this ) delete d->itemDelegate;
191  d->itemDelegate = delegate;
192  update();
193 }
194 
196 {
197  return d->itemDelegate;
198 }
199 
200 QAbstractItemModel* GraphicsScene::model() const
201 {
202  assert(!d->summaryHandlingModel.isNull());
203  return d->summaryHandlingModel->sourceModel();
204 }
205 
206 void GraphicsScene::setModel( QAbstractItemModel* model )
207 {
208  assert(!d->summaryHandlingModel.isNull());
209  d->summaryHandlingModel->setSourceModel(model);
210  d->grid->setModel( d->summaryHandlingModel );
211  setSelectionModel( new QItemSelectionModel( model, this ) );
212 }
213 
215 {
216  return d->summaryHandlingModel;
217 }
218 
220 {
221  proxyModel->setSourceModel( model() );
222  d->summaryHandlingModel = proxyModel;
223 }
224 
225 void GraphicsScene::setRootIndex( const QModelIndex& idx )
226 {
227  d->grid->setRootIndex( idx );
228 }
229 
230 QModelIndex GraphicsScene::rootIndex() const
231 {
232  return d->grid->rootIndex();
233 }
234 
236 {
237  return d->constraintModel;
238 }
239 
241 {
242  if ( !d->constraintModel.isNull() ) {
243  d->constraintModel->disconnect( this );
244  }
245  d->constraintModel = cm;
246 
247  connect( cm, SIGNAL( constraintAdded( const KDGantt::Constraint& ) ),
248  this, SLOT( slotConstraintAdded( const KDGantt::Constraint& ) ) );
249  connect( cm, SIGNAL( constraintRemoved( const KDGantt::Constraint& ) ),
250  this, SLOT( slotConstraintRemoved( const KDGantt::Constraint& ) ) );
251  d->resetConstraintItems();
252 }
253 
254 void GraphicsScene::setSelectionModel( QItemSelectionModel* smodel )
255 {
256  d->selectionModel = smodel;
257  // TODO: update selection from model and connect signals
258 }
259 
260 QItemSelectionModel* GraphicsScene::selectionModel() const
261 {
262  return d->selectionModel;
263 }
264 
266 {
267  d->rowController = rc;
268 }
269 
271 {
272  return d->rowController;
273 }
274 
276 {
277  QAbstractItemModel* model = 0;
278  if ( grid == 0 ) grid = &d->default_grid;
279  if ( d->grid ) {
280  d->grid->disconnect( this );
281  model = d->grid->model();
282  }
283  d->grid = grid;
284  connect( d->grid, SIGNAL( gridChanged() ), this, SLOT( slotGridChanged() ) );
285  d->grid->setModel( model );
286  slotGridChanged();
287 }
288 
290 {
291  return d->grid;
292 }
293 
295 {
296  d->readOnly = ro;
297 }
298 
300 {
301  return d->readOnly;
302 }
303 
304 /* Returns the index with column=0 fromt the
305  * same row as idx and with the same parent.
306  * This is used to traverse the tree-structure
307  * of the model
308  */
309 QModelIndex GraphicsScene::mainIndex( const QModelIndex& idx )
310 {
311 #if 0
312  if ( idx.isValid() ) {
313  return idx.model()->index( idx.row(), 0,idx.parent() );
314  } else {
315  return QModelIndex();
316  }
317 #else
318  return idx;
319 #endif
320 }
321 
326 QModelIndex GraphicsScene::dataIndex( const QModelIndex& idx )
327 {
328 #if 0
329  if ( idx.isValid() ) {
330  const QAbstractItemModel* model = idx.model();
331  return model->index( idx.row(), model->columnCount( idx.parent() )-1,idx.parent() );
332  } else {
333  return QModelIndex();
334  }
335 #else
336  return idx;
337 #endif
338 }
339 
345 {
346 #if 0
347  switch ( type ) {
348  case TypeEvent: return 0;
349  case TypeTask: return new TaskItem;
350  case TypeSummary: return new SummaryItem;
351  default: return 0;
352  }
353 #endif
354  //qDebug() << "GraphicsScene::createItem("<<type<<")";
355  Q_UNUSED( type );
356  return new GraphicsItem;
357 }
358 
359 void GraphicsScene::Private::recursiveUpdateMultiItem( const Span& span, const QModelIndex& idx )
360 {
361  //qDebug() << "recursiveUpdateMultiItem("<<span<<idx<<")";
362  GraphicsItem* item = q->findItem( idx );
363  const int itemtype = summaryHandlingModel->data( idx, ItemTypeRole ).toInt();
364  if (!item) {
365  item = q->createItem( static_cast<ItemType>( itemtype ) );
366  item->setIndex( idx );
367  q->insertItem( idx, item);
368  }
369  item->updateItem( span, idx );
370  QModelIndex child;
371  int cr = 0;
372  while ( ( child = idx.child( cr, 0 ) ).isValid() ) {
373  recursiveUpdateMultiItem( span, child );
374  ++cr;
375  }
376 }
377 
378 void GraphicsScene::updateRow( const QModelIndex& rowidx )
379 {
380  //qDebug() << "GraphicsScene::updateRow("<<rowidx<<")" << rowidx.data( Qt::DisplayRole );
381  if ( !rowidx.isValid() ) return;
382 #if !defined(NDEBUG)
383  const QAbstractItemModel* model = rowidx.model(); // why const?
384 #endif
385  assert( model );
386  assert( rowController() );
387  assert( model == summaryHandlingModel() );
388 
389  const QModelIndex sidx = summaryHandlingModel()->mapToSource( rowidx );
390  Span rg = rowController()->rowGeometry( sidx );
391  for ( QModelIndex treewalkidx = sidx; treewalkidx.isValid(); treewalkidx = treewalkidx.parent() ) {
392  if ( treewalkidx.data( ItemTypeRole ).toInt() == TypeMulti
393  && !rowController()->isRowExpanded( treewalkidx )) {
394  rg = rowController()->rowGeometry( treewalkidx );
395  }
396  }
397 
398  bool blocked = blockSignals( true );
399  for ( int col = 0; col < summaryHandlingModel()->columnCount( rowidx.parent() ); ++col ) {
400  const QModelIndex idx = summaryHandlingModel()->index( rowidx.row(), col, rowidx.parent() );
401  const QModelIndex sidx = summaryHandlingModel()->mapToSource( idx );
402  const int itemtype = summaryHandlingModel()->data( idx, ItemTypeRole ).toInt();
403  const bool isExpanded = rowController()->isRowExpanded( sidx );
404  if ( itemtype == TypeNone ) {
405  removeItem( idx );
406  continue;
407  }
408  if ( itemtype == TypeMulti && !isExpanded ) {
409  d->recursiveUpdateMultiItem( rg, idx );
410  } else {
411  if ( summaryHandlingModel()->data( rowidx.parent(), ItemTypeRole ).toInt() == TypeMulti && !isExpanded ) {
412  //continue;
413  }
414 
415  GraphicsItem* item = findItem( idx );
416  if (!item) {
417  item = createItem( static_cast<ItemType>( itemtype ) );
418  item->setIndex( idx );
419  insertItem(idx, item);
420  }
421  const Span span = rowController()->rowGeometry( sidx );
422  item->updateItem( span, idx );
423  }
424  }
425  blockSignals( blocked );
426 }
427 
428 void GraphicsScene::insertItem( const QPersistentModelIndex& idx, GraphicsItem* item )
429 {
430  if ( !d->constraintModel.isNull() ) {
431  // Create items for constraints
432  const QModelIndex sidx = summaryHandlingModel()->mapToSource( idx );
433  const QList<Constraint> clst = d->constraintModel->constraintsForIndex( sidx );
434  Q_FOREACH( const Constraint& c, clst ) {
435  QModelIndex other_idx;
436  if ( c.startIndex() == sidx ) {
437  other_idx = c.endIndex();
438  GraphicsItem* other_item = d->items.value(summaryHandlingModel()->mapFromSource( other_idx ),0);
439  if ( !other_item ) continue;
441  item->addStartConstraint( citem );
442  other_item->addEndConstraint( citem );
443  addItem( citem );
444  } else if ( c.endIndex() == sidx ) {
445  other_idx = c.startIndex();
446  GraphicsItem* other_item = d->items.value(summaryHandlingModel()->mapFromSource( other_idx ),0);
447  if ( !other_item ) continue;
449  other_item->addStartConstraint( citem );
450  item->addEndConstraint( citem );
451  addItem( citem );
452  } else {
453  assert( 0 ); // Impossible
454  }
455  }
456  }
457  d->items.insert( idx, item );
458  addItem( item );
459 }
460 
461 void GraphicsScene::removeItem( const QModelIndex& idx )
462 {
463  //qDebug() << "GraphicsScene::removeItem("<<idx<<")";
464  QHash<QPersistentModelIndex,GraphicsItem*>::iterator it = d->items.find( idx );
465  if ( it != d->items.end() ) {
466  GraphicsItem* item = *it;
467  assert( item );
468  // We have to remove the item from the list first because
469  // there is a good chance there will be reentrant calls
470  d->items.erase( it );
471  {
472  // Remove any constraintitems attached
473  const QSet<ConstraintGraphicsItem*> clst = QSet<ConstraintGraphicsItem*>::fromList( item->startConstraints() ) +
474  QSet<ConstraintGraphicsItem*>::fromList( item->endConstraints() );
475  Q_FOREACH( ConstraintGraphicsItem* citem, clst ) {
476  d->deleteConstraintItem( citem );
477  }
478  }
479  // Get rid of the item
480  delete item;
481  }
482 }
483 
484 GraphicsItem* GraphicsScene::findItem( const QModelIndex& idx ) const
485 {
486  if ( !idx.isValid() ) return 0;
487  assert( idx.model() == summaryHandlingModel() );
488  QHash<QPersistentModelIndex,GraphicsItem*>::const_iterator it = d->items.find( idx );
489  return ( it != d->items.end() )?*it:0;
490 }
491 
492 GraphicsItem* GraphicsScene::findItem( const QPersistentModelIndex& idx ) const
493 {
494  if ( !idx.isValid() ) return 0;
495  assert( idx.model() == summaryHandlingModel() );
496  QHash<QPersistentModelIndex,GraphicsItem*>::const_iterator it = d->items.find( idx );
497  return ( it != d->items.end() )?*it:0;
498 }
499 
501 {
502  // TODO constraints
503  QHash<QPersistentModelIndex, GraphicsItem*>::const_iterator it = d->items.constBegin();
504  for ( ; it != d->items.constEnd(); ++it ) {
505  delete *it;
506  }
507  d->items.clear();
508 }
509 
511 {
512  for ( QHash<QPersistentModelIndex,GraphicsItem*>::iterator it = d->items.begin();
513  it != d->items.end(); ++it ) {
514  GraphicsItem* const item = it.value();
515  const QPersistentModelIndex& idx = it.key();
516  item->updateItem( Span( item->pos().y(), item->rect().height() ), idx );
517  }
518  invalidate( QRectF(), QGraphicsScene::BackgroundLayer );
519 }
520 
521 void GraphicsScene::deleteSubtree( const QModelIndex& _idx )
522 {
523  QModelIndex idx = dataIndex( _idx );
524  if ( !idx.model() ) return;
525  const QModelIndex parent( idx.parent() );
526  const int colcount = idx.model()->columnCount( parent );
527  {for ( int i = 0; i < colcount; ++i ) {
528  removeItem( parent.child( idx.row(), i ) );
529  }}
530  const int rowcount = summaryHandlingModel()->rowCount( _idx );
531  {for ( int i = 0; i < rowcount; ++i ) {
532  deleteSubtree( summaryHandlingModel()->index( i, summaryHandlingModel()->columnCount(_idx)-1, _idx ) );
533  }}
534 }
535 
536 
538 {
539  return d->findConstraintItem( c );
540 }
541 
543 {
544  // TODO
545  // d->constraintItems.clearConstraintItems();
546 }
547 
548 void GraphicsScene::slotConstraintAdded( const KDGantt::Constraint& c )
549 {
550  d->createConstraintItem( c );
551 }
552 
553 void GraphicsScene::slotConstraintRemoved( const KDGantt::Constraint& c )
554 {
555  d->deleteConstraintItem( c );
556 }
557 
558 void GraphicsScene::slotGridChanged()
559 {
560  updateItems();
561  update();
562  emit gridChanged();
563 }
564 
565 void GraphicsScene::helpEvent( QGraphicsSceneHelpEvent *helpEvent )
566 {
567 #ifndef QT_NO_TOOLTIP
568  QGraphicsItem *item = itemAt( helpEvent->scenePos(), QTransform() );
569  if ( GraphicsItem* gitem = qgraphicsitem_cast<GraphicsItem*>( item ) ) {
570  QToolTip::showText(helpEvent->screenPos(), gitem->ganttToolTip());
571  } else if ( ConstraintGraphicsItem* citem = qgraphicsitem_cast<ConstraintGraphicsItem*>( item ) ) {
572  QToolTip::showText(helpEvent->screenPos(), citem->ganttToolTip());
573  } else {
574  QGraphicsScene::helpEvent( helpEvent );
575  }
576 #endif /* QT_NO_TOOLTIP */
577 }
578 
579 void GraphicsScene::drawBackground( QPainter* painter, const QRectF& _rect )
580 {
581  QRectF scn( sceneRect() );
582  QRectF rect( _rect );
583  if ( d->isPrinting && d->drawColumnLabels ) {
584  QRectF headerRect( scn.topLeft()+QPointF( d->labelsWidth, 0 ),
585  QSizeF( scn.width()-d->labelsWidth, d->rowController->headerHeight() ));
586 
587  d->grid->paintHeader( painter, headerRect, rect, 0, 0 );
588 
589 #if 0
590  /* We have to blank out the part of the header that is invisible during
591  * normal rendering when we are printing.
592  */
593  QRectF labelsTabRect( scn.topLeft(), QSizeF( d->labelsWidth, headerRect.height() ) );
594 
595  QStyleOptionHeader opt;
596  opt.rect = labelsTabRect.toRect();
597  opt.text = QLatin1String("");
598  opt.textAlignment = Qt::AlignCenter;
599 #if QT_VERSION >= QT_VERSION_CHECK(4, 4, 0)
600  style()->drawControl(QStyle::CE_Header, &opt, painter, 0);
601 #else
602  QApplication::style()->drawControl(QStyle::CE_Header, &opt, painter, 0);
603 #endif
604 #endif
605 
606  scn.setTop( headerRect.bottom() );
607  scn.setLeft( headerRect.left() );
608  rect = rect.intersected( scn );
609  }
610  d->grid->paintGrid( painter, scn, rect, d->rowController );
611 
612  d->grid->drawBackground(painter, rect);
613 }
614 
615 void GraphicsScene::drawForeground( QPainter* painter, const QRectF& rect )
616 {
617  d->grid->drawForeground(painter, rect);
618 }
619 
620 void GraphicsScene::itemEntered( const QModelIndex& idx )
621 {
622  emit entered( idx );
623 }
624 
625 void GraphicsScene::itemPressed( const QModelIndex& idx )
626 {
627  emit pressed( idx );
628 }
629 
630 void GraphicsScene::itemClicked( const QModelIndex& idx )
631 {
632  emit clicked( idx );
633 }
634 
635 void GraphicsScene::itemDoubleClicked( const QModelIndex& idx )
636 {
637  emit qrealClicked( idx );
638 }
639 
641 {
642  d->dragSource = item;
643 }
644 
646 {
647  return d->dragSource;
648 }
649 
658 void GraphicsScene::print( QPrinter* printer, bool drawRowLabels, bool drawColumnLabels )
659 {
660 #ifndef HAVE_PRINTER
661  Q_UNUSED( printer );
662  Q_UNUSED( drawRowLabels );
663  Q_UNUSED( drawColumnLabels );
664 #else
665  QPainter painter( printer );
666  doPrint( &painter, printer->pageRect(), sceneRect().left(), sceneRect().right(), printer, drawRowLabels, drawColumnLabels );
667 #endif
668 }
669 
682 void GraphicsScene::print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels )
683 {
684 #ifndef HAVE_PRINTER
685  Q_UNUSED( printer );
686  Q_UNUSED( start );
687  Q_UNUSED( end );
688  Q_UNUSED( drawRowLabels );
689  Q_UNUSED( drawColumnLabels );
690 #else
691  QPainter painter( printer );
692  doPrint( &painter, printer->pageRect(), start, end, printer, drawRowLabels, drawColumnLabels );
693 #endif
694 }
695 
702 void GraphicsScene::print( QPainter* painter, const QRectF& _targetRect, bool drawRowLabels, bool drawColumnLabels )
703 {
704  QRectF targetRect( _targetRect );
705  if ( targetRect.isNull() ) {
706  targetRect = sceneRect();
707  }
708 
709  doPrint( painter, targetRect, sceneRect().left(), sceneRect().right(), 0, drawRowLabels, drawColumnLabels );
710 }
711 
722 void GraphicsScene::print( QPainter* painter, qreal start, qreal end,
723  const QRectF& _targetRect, bool drawRowLabels, bool drawColumnLabels )
724 {
725  QRectF targetRect( _targetRect );
726  if ( targetRect.isNull() ) {
727  targetRect = sceneRect();
728  }
729 
730  doPrint( painter, targetRect, start, end, 0, drawRowLabels, drawColumnLabels );
731 }
732 
735 void GraphicsScene::doPrint( QPainter* painter, const QRectF& targetRect,
736  qreal start, qreal end,
737  QPrinter* printer, bool drawRowLabels, bool drawColumnLabels )
738 {
739  assert( painter );
740  d->isPrinting = true;
741  d->drawColumnLabels = drawColumnLabels;
742  d->labelsWidth = 0.0;
743 #if QT_VERSION >= QT_VERSION_CHECK(4, 4, 0)
744  QFont sceneFont( font() );
745 #ifdef HAVE_PRINTER
746  if ( printer ) {
747  sceneFont = QFont( font(), printer );
748  if ( font().pointSizeF() >= 0.0 )
749  sceneFont.setPointSizeF( font().pointSizeF() );
750  else if ( font().pointSize() >= 0 )
751  sceneFont.setPointSize( font().pointSize() );
752  else
753  sceneFont.setPixelSize( font().pixelSize() );
754  }
755 #endif
756 #else
757  QFont sceneFont( painter->font() );
758 #ifdef HAVE_PRINTER
759  if ( printer ) {
760  sceneFont = QFont( painter->font(), printer );
761  if ( painter->font().pointSizeF() >= 0.0 )
762  sceneFont.setPointSizeF( painter->font().pointSizeF() );
763  else if ( painter->font().pointSize() >= 0 )
764  sceneFont.setPointSize( painter->font().pointSize() );
765  else
766  sceneFont.setPixelSize( painter->font().pixelSize() );
767  }
768 #endif
769 #endif
770 
771  QGraphicsTextItem dummyTextItem( QLatin1String("X") );
772  dummyTextItem.adjustSize();
773  QFontMetrics fm(dummyTextItem.font());
774  sceneFont.setPixelSize( fm.height() );
775 
776  const QRectF oldScnRect( sceneRect() );
777  QRectF scnRect( oldScnRect );
778  scnRect.setLeft( start );
779  scnRect.setRight( end );
780  bool b = blockSignals( true );
781 
782  /* column labels */
783  if ( d->drawColumnLabels ) {
784  QRectF headerRect( scnRect );
785  headerRect.setHeight( - d->rowController->headerHeight() );
786  scnRect.setTop(scnRect.top() - d->rowController->headerHeight());
787  }
788 
789  /* row labels */
790  QVector<QGraphicsTextItem*> textLabels;
791  if ( drawRowLabels ) {
792  qreal textWidth = 0.;
793  QModelIndex sidx = summaryHandlingModel()->mapToSource( summaryHandlingModel()->index( 0, 0, rootIndex()) );
794  do {
795  QModelIndex idx = summaryHandlingModel()->mapFromSource( sidx );
796  const Span rg=rowController()->rowGeometry( sidx );
797  const QString txt = idx.data( Qt::DisplayRole ).toString();
798  QGraphicsTextItem* item = new QGraphicsTextItem( txt );
799  addItem( item );
800  textLabels << item;
801  item->adjustSize();
802  textWidth = qMax( item->textWidth(), textWidth );
803  item->setPos( 0, rg.start() );
804  } while ( ( sidx = rowController()->indexBelow( sidx ) ).isValid() );
805  // Add a little margin to textWidth
806  textWidth += QFontMetricsF(sceneFont).width( QString::fromLatin1( "X" ) );
807  Q_FOREACH( QGraphicsTextItem* item, textLabels ) {
808  item->setPos( scnRect.left()-textWidth, item->y() );
809  item->show();
810  }
811  scnRect.setLeft( scnRect.left()-textWidth );
812  d->labelsWidth = textWidth;
813  }
814 
815  setSceneRect( scnRect );
816 
817  painter->save();
818  painter->setClipRect( targetRect );
819 
820  qreal yratio = targetRect.height()/scnRect.height();
821  /* If we're not printing multiple pages,
822  * check if the span fits and adjust:
823  */
824  if ( !printer && targetRect.width()/scnRect.width() < yratio ) {
825  yratio = targetRect.width()/scnRect.width();
826  }
827 
828  qreal offset = scnRect.left();
829  int pagecount = 0;
830  while ( offset < scnRect.right() ) {
831  painter->setFont( sceneFont );
832  render( painter, targetRect, QRectF( QPointF( offset, scnRect.top()),
833  QSizeF( targetRect.width()/yratio, scnRect.height() ) ) );
834  offset += targetRect.width()/yratio;
835  ++pagecount;
836  if ( printer && offset < scnRect.right() ) {
837 #ifdef HAVE_PRINTER
838  printer->newPage();
839 #endif
840  } else {
841  break;
842  }
843  }
844 
845  d->isPrinting = false;
846  d->drawColumnLabels = true;
847  d->labelsWidth = 0.0;
848  qDeleteAll( textLabels );
849  blockSignals( b );
850  setSceneRect( oldScnRect );
851  painter->restore();
852 }
853 
854 #include "moc_kdganttgraphicsscene.cpp"
855 
856 
857 #ifndef KDAB_NO_UNIT_TESTS
858 #include "unittest/test.h"
859 
860 #include <QGraphicsLineItem>
861 #include <QPointer>
862 #include <QStandardItemModel>
863 
864 #include "kdganttgraphicsview.h"
865 
866 class SceneTestRowController : public KDGantt::AbstractRowController {
867 private:
868  static const int ROW_HEIGHT;
869  QPointer<QAbstractItemModel> m_model;
870 
871 public:
872  SceneTestRowController()
873  {
874  }
875 
876  void setModel( QAbstractItemModel* model )
877  {
878  m_model = model;
879  }
880 
881  /*reimp*/int headerHeight() const override { return 40; }
882 
883  /*reimp*/ bool isRowVisible( const QModelIndex& ) const override { return true;}
884  /*reimp*/ bool isRowExpanded( const QModelIndex& ) const override { return false; }
885  /*reimp*/ KDGantt::Span rowGeometry( const QModelIndex& idx ) const override
886  {
887  return KDGantt::Span( idx.row() * ROW_HEIGHT, ROW_HEIGHT );
888  }
889  /*reimp*/ int maximumItemHeight() const override {
890  return ROW_HEIGHT/2;
891  }
892  /*reimp*/int totalHeight() const override {
893  return m_model->rowCount()* ROW_HEIGHT;
894  }
895 
896  /*reimp*/ QModelIndex indexAt( int height ) const override {
897  return m_model->index( height/ROW_HEIGHT, 0 );
898  }
899 
900  /*reimp*/ QModelIndex indexBelow( const QModelIndex& idx ) const override {
901  if ( !idx.isValid() )return QModelIndex();
902  return idx.model()->index( idx.row()+1, idx.column(), idx.parent() );
903  }
904  /*reimp*/ QModelIndex indexAbove( const QModelIndex& idx ) const override {
905  if ( !idx.isValid() )return QModelIndex();
906  return idx.model()->index( idx.row()-1, idx.column(), idx.parent() );
907  }
908 
909 };
910 
911 class TestLineItem : public QGraphicsLineItem
912 {
913 public:
914  TestLineItem( bool *destroyedFlag )
915  : QGraphicsLineItem( 0, 0, 10, 10 ), // geometry doesn't matter
916  m_destroyedFlag( destroyedFlag )
917  {}
918 
919  ~TestLineItem() override
920  { *m_destroyedFlag = true; }
921 
922 private:
923  bool *m_destroyedFlag;
924 };
925 
926 const int SceneTestRowController::ROW_HEIGHT = 30;
927 
929  QStandardItemModel model;
930 
931  QStandardItem* item = new QStandardItem();
932  item->setData( KDGantt::TypeTask, KDGantt::ItemTypeRole );
933  item->setData( QString::fromLatin1( "Decide on new product" ) );
934  item->setData( QDateTime( QDate( 2007, 3, 1 ) ), KDGantt::StartTimeRole );
935  item->setData( QDateTime( QDate( 2007, 3, 3 ) ), KDGantt::EndTimeRole );
936 
937  QStandardItem* item2 = new QStandardItem();
938  item2->setData( KDGantt::TypeTask, KDGantt::ItemTypeRole );
939  item2->setData( QString::fromLatin1( "Educate personnel" ) );
940  item2->setData( QDateTime( QDate( 2007, 3, 3 ) ), KDGantt::StartTimeRole );
941  item2->setData( QDateTime( QDate( 2007, 3, 6 ) ), KDGantt::EndTimeRole );
942 
943  model.appendRow( item );
944  model.appendRow( item2 );
945 
946  SceneTestRowController rowController;
947  rowController.setModel( &model );
948 
949  KDGantt::GraphicsView graphicsView;
950  graphicsView.setRowController( &rowController );
951  graphicsView.setModel( &model );
952 
953  // Now the interesting stuff - the items above are just for a "realistic environment"
954 
955  bool foreignItemDestroyed = false;
956  TestLineItem *foreignItem = new TestLineItem( &foreignItemDestroyed );
957  graphicsView.scene()->addItem( foreignItem );
958 
959  assertFalse( foreignItemDestroyed );
960  graphicsView.updateScene();
961  assertFalse( foreignItemDestroyed );
962 }
963 #endif /* KDAB_NO_UNIT_TESTS */
qreal start() const
QAbstractProxyModel * summaryHandlingModel() const
void setGrid(AbstractGrid *grid)
KDAB_SCOPED_UNITTEST_SIMPLE(KDGantt, GraphicsView,"test")
Proxy model that supports summary gantt items.
GraphicsItem * dragSource() const
Abstract baseclass for grids. A grid is used to convert between QModelIndex&#39;es and gantt chart values...
void clicked(const QModelIndex &index)
void itemClicked(const QModelIndex &)
void updateItem(const Span &rowgeometry, const QPersistentModelIndex &idx)
void setConstraintModel(ConstraintModel *)
#define d
void qrealClicked(const QModelIndex &index)
ConstraintModel * constraintModel() const
void setModel(QAbstractItemModel *)
A class used to represent a dependency.
Class used to render gantt items in a KDGantt::GraphicsView.
void addStartConstraint(ConstraintGraphicsItem *)
GraphicsItem * findItem(const QModelIndex &) const
virtual Span rowGeometry(const QModelIndex &idx) const =0
virtual bool isRowExpanded(const QModelIndex &idx) const =0
void drawBackground(QPainter *painter, const QRectF &rect) override
void print(QPrinter *printer, bool drawRowLabels=true, bool drawColumnLabels=true)
void pressed(const QModelIndex &index)
QModelIndex rootIndex() const
GraphicsItem * createItem(ItemType type) const
static QModelIndex mainIndex(const QModelIndex &idx)
QAbstractItemModel * model() const
void setRowController(AbstractRowController *)
A class representing a start point and a length.
Class only listed here to document inheritance of some KDChart classes.
The GraphicsView class provides a model/view implementation of a gantt chart.
void entered(const QModelIndex &index)
void itemDoubleClicked(const QModelIndex &)
void drawForeground(QPainter *painter, const QRectF &rect) override
void setSelectionModel(QItemSelectionModel *selectionmodel)
void setRowController(AbstractRowController *rc)
void setItemDelegate(ItemDelegate *)
AbstractRowController * rowController() const
QItemSelectionModel * selectionModel() const
void itemEntered(const QModelIndex &)
void setModel(QAbstractItemModel *)
void removeStartConstraint(ConstraintGraphicsItem *)
void deleteSubtree(const QModelIndex &)
ConstraintGraphicsItem * findConstraintItem(const Constraint &) const
void setSummaryHandlingModel(QAbstractProxyModel *)
void insertItem(const QPersistentModelIndex &, GraphicsItem *)
void setRootIndex(const QModelIndex &idx)
QModelIndex endIndex() const
QList< ConstraintGraphicsItem * > endConstraints() const
void helpEvent(QGraphicsSceneHelpEvent *helpEvent) override
void addEndConstraint(ConstraintGraphicsItem *)
static QModelIndex dataIndex(const QModelIndex &idx)
void setIndex(const QPersistentModelIndex &idx)
void removeItem(const QModelIndex &)
void updateRow(const QModelIndex &idx)
void setDragSource(GraphicsItem *item)
Class only listed here to document inheritance of some KDChart classes.
ItemDelegate * itemDelegate() const
AbstractGrid * grid() const
#define assertFalse(x)
Definition: test.h:42
QList< ConstraintGraphicsItem * > startConstraints() const
Abstract baseclass for row controllers. A row controller is used by the GraphicsView to nagivate the ...
bool compareIndexes(const Constraint &other) const
QModelIndex startIndex() const
void removeEndConstraint(ConstraintGraphicsItem *)
void itemPressed(const QModelIndex &)

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/