KD Chart 2  [rev.2.5.1]
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Pages
kdganttgraphicsscene.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Copyright (C) 2001-2013 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 
275 void GraphicsScene::setGrid( AbstractGrid* grid )
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 
289 AbstractGrid* GraphicsScene::grid() const
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  const QAbstractItemModel* model = rowidx.model(); // why const?
383  assert( model );
384  assert( rowController() );
385  assert( model == summaryHandlingModel() );
386 
387  const QModelIndex sidx = summaryHandlingModel()->mapToSource( rowidx );
388  Span rg = rowController()->rowGeometry( sidx );
389  for ( QModelIndex treewalkidx = sidx; treewalkidx.isValid(); treewalkidx = treewalkidx.parent() ) {
390  if ( treewalkidx.data( ItemTypeRole ).toInt() == TypeMulti
391  && !rowController()->isRowExpanded( treewalkidx )) {
392  rg = rowController()->rowGeometry( treewalkidx );
393  }
394  }
395 
396  bool blocked = blockSignals( true );
397  for ( int col = 0; col < summaryHandlingModel()->columnCount( rowidx.parent() ); ++col ) {
398  const QModelIndex idx = summaryHandlingModel()->index( rowidx.row(), col, rowidx.parent() );
399  const QModelIndex sidx = summaryHandlingModel()->mapToSource( idx );
400  const int itemtype = summaryHandlingModel()->data( idx, ItemTypeRole ).toInt();
401  const bool isExpanded = rowController()->isRowExpanded( sidx );
402  if ( itemtype == TypeNone ) {
403  removeItem( idx );
404  continue;
405  }
406  if ( itemtype == TypeMulti && !isExpanded ) {
407  d->recursiveUpdateMultiItem( rg, idx );
408  } else {
409  if ( summaryHandlingModel()->data( rowidx.parent(), ItemTypeRole ).toInt() == TypeMulti && !isExpanded ) {
410  //continue;
411  }
412 
413  GraphicsItem* item = findItem( idx );
414  if (!item) {
415  item = createItem( static_cast<ItemType>( itemtype ) );
416  item->setIndex( idx );
417  insertItem(idx, item);
418  }
419  const Span span = rowController()->rowGeometry( sidx );
420  item->updateItem( span, idx );
421  }
422  }
423  blockSignals( blocked );
424 }
425 
426 void GraphicsScene::insertItem( const QPersistentModelIndex& idx, GraphicsItem* item )
427 {
428  if ( !d->constraintModel.isNull() ) {
429  // Create items for constraints
430  const QModelIndex sidx = summaryHandlingModel()->mapToSource( idx );
431  const QList<Constraint> clst = d->constraintModel->constraintsForIndex( sidx );
432  Q_FOREACH( const Constraint& c, clst ) {
433  QModelIndex other_idx;
434  if ( c.startIndex() == sidx ) {
435  other_idx = c.endIndex();
436  GraphicsItem* other_item = d->items.value(summaryHandlingModel()->mapFromSource( other_idx ),0);
437  if ( !other_item ) continue;
439  item->addStartConstraint( citem );
440  other_item->addEndConstraint( citem );
441  addItem( citem );
442  } else if ( c.endIndex() == sidx ) {
443  other_idx = c.startIndex();
444  GraphicsItem* other_item = d->items.value(summaryHandlingModel()->mapFromSource( other_idx ),0);
445  if ( !other_item ) continue;
447  other_item->addStartConstraint( citem );
448  item->addEndConstraint( citem );
449  addItem( citem );
450  } else {
451  assert( 0 ); // Impossible
452  }
453  }
454  }
455  d->items.insert( idx, item );
456  addItem( item );
457 }
458 
459 void GraphicsScene::removeItem( const QModelIndex& idx )
460 {
461  //qDebug() << "GraphicsScene::removeItem("<<idx<<")";
462  QHash<QPersistentModelIndex,GraphicsItem*>::iterator it = d->items.find( idx );
463  if ( it != d->items.end() ) {
464  GraphicsItem* item = *it;
465  assert( item );
466  // We have to remove the item from the list first because
467  // there is a good chance there will be reentrant calls
468  d->items.erase( it );
469  {
470  // Remove any constraintitems attached
471  const QSet<ConstraintGraphicsItem*> clst = QSet<ConstraintGraphicsItem*>::fromList( item->startConstraints() ) +
472  QSet<ConstraintGraphicsItem*>::fromList( item->endConstraints() );
473  Q_FOREACH( ConstraintGraphicsItem* citem, clst ) {
474  d->deleteConstraintItem( citem );
475  }
476  }
477  // Get rid of the item
478  delete item;
479  }
480 }
481 
482 GraphicsItem* GraphicsScene::findItem( const QModelIndex& idx ) const
483 {
484  if ( !idx.isValid() ) return 0;
485  assert( idx.model() == summaryHandlingModel() );
486  QHash<QPersistentModelIndex,GraphicsItem*>::const_iterator it = d->items.find( idx );
487  return ( it != d->items.end() )?*it:0;
488 }
489 
490 GraphicsItem* GraphicsScene::findItem( const QPersistentModelIndex& idx ) const
491 {
492  if ( !idx.isValid() ) return 0;
493  assert( idx.model() == summaryHandlingModel() );
494  QHash<QPersistentModelIndex,GraphicsItem*>::const_iterator it = d->items.find( idx );
495  return ( it != d->items.end() )?*it:0;
496 }
497 
499 {
500  // TODO constraints
501  QHash<QPersistentModelIndex, GraphicsItem*>::const_iterator it = d->items.constBegin();
502  for ( ; it != d->items.constEnd(); ++it ) {
503  delete *it;
504  }
505  d->items.clear();
506 }
507 
509 {
510  for ( QHash<QPersistentModelIndex,GraphicsItem*>::iterator it = d->items.begin();
511  it != d->items.end(); ++it ) {
512  GraphicsItem* const item = it.value();
513  const QPersistentModelIndex& idx = it.key();
514  item->updateItem( Span( item->pos().y(), item->rect().height() ), idx );
515  }
516  invalidate( QRectF(), QGraphicsScene::BackgroundLayer );
517 }
518 
519 void GraphicsScene::deleteSubtree( const QModelIndex& _idx )
520 {
521  QModelIndex idx = dataIndex( _idx );
522  if ( !idx.model() ) return;
523  const QModelIndex parent( idx.parent() );
524  const int colcount = idx.model()->columnCount( parent );
525  {for ( int i = 0; i < colcount; ++i ) {
526  removeItem( parent.child( idx.row(), i ) );
527  }}
528  const int rowcount = summaryHandlingModel()->rowCount( _idx );
529  {for ( int i = 0; i < rowcount; ++i ) {
530  deleteSubtree( summaryHandlingModel()->index( i, summaryHandlingModel()->columnCount(_idx)-1, _idx ) );
531  }}
532 }
533 
534 
536 {
537  return d->findConstraintItem( c );
538 }
539 
541 {
542  // TODO
543  // d->constraintItems.clearConstraintItems();
544 }
545 
546 void GraphicsScene::slotConstraintAdded( const KDGantt::Constraint& c )
547 {
548  d->createConstraintItem( c );
549 }
550 
551 void GraphicsScene::slotConstraintRemoved( const KDGantt::Constraint& c )
552 {
553  d->deleteConstraintItem( c );
554 }
555 
556 void GraphicsScene::slotGridChanged()
557 {
558  updateItems();
559  update();
560  emit gridChanged();
561 }
562 
563 void GraphicsScene::helpEvent( QGraphicsSceneHelpEvent *helpEvent )
564 {
565 #ifndef QT_NO_TOOLTIP
566  QGraphicsItem *item = itemAt( helpEvent->scenePos(), QTransform() );
567  if ( GraphicsItem* gitem = qgraphicsitem_cast<GraphicsItem*>( item ) ) {
568  QToolTip::showText(helpEvent->screenPos(), gitem->ganttToolTip());
569  } else if ( ConstraintGraphicsItem* citem = qgraphicsitem_cast<ConstraintGraphicsItem*>( item ) ) {
570  QToolTip::showText(helpEvent->screenPos(), citem->ganttToolTip());
571  } else {
572  QGraphicsScene::helpEvent( helpEvent );
573  }
574 #endif /* QT_NO_TOOLTIP */
575 }
576 
577 void GraphicsScene::drawBackground( QPainter* painter, const QRectF& _rect )
578 {
579  QRectF scn( sceneRect() );
580  QRectF rect( _rect );
581  if ( d->isPrinting && d->drawColumnLabels ) {
582  QRectF headerRect( scn.topLeft()+QPointF( d->labelsWidth, 0 ),
583  QSizeF( scn.width()-d->labelsWidth, d->rowController->headerHeight() ));
584 
585  d->grid->paintHeader( painter, headerRect, rect, 0, 0 );
586 
587 #if 0
588  /* We have to blank out the part of the header that is invisible during
589  * normal rendering when we are printing.
590  */
591  QRectF labelsTabRect( scn.topLeft(), QSizeF( d->labelsWidth, headerRect.height() ) );
592 
593  QStyleOptionHeader opt;
594  opt.rect = labelsTabRect.toRect();
595  opt.text = QLatin1String("");
596  opt.textAlignment = Qt::AlignCenter;
597 #if QT_VERSION >= QT_VERSION_CHECK(4, 4, 0)
598  style()->drawControl(QStyle::CE_Header, &opt, painter, 0);
599 #else
600  QApplication::style()->drawControl(QStyle::CE_Header, &opt, painter, 0);
601 #endif
602 #endif
603 
604  scn.setTop( headerRect.bottom() );
605  scn.setLeft( headerRect.left() );
606  rect = rect.intersected( scn );
607  }
608  d->grid->paintGrid( painter, scn, rect, d->rowController );
609 
610  d->grid->drawBackground(painter, rect);
611 }
612 
613 void GraphicsScene::drawForeground( QPainter* painter, const QRectF& rect )
614 {
615  d->grid->drawForeground(painter, rect);
616 }
617 
618 void GraphicsScene::itemEntered( const QModelIndex& idx )
619 {
620  emit entered( idx );
621 }
622 
623 void GraphicsScene::itemPressed( const QModelIndex& idx )
624 {
625  emit pressed( idx );
626 }
627 
628 void GraphicsScene::itemClicked( const QModelIndex& idx )
629 {
630  emit clicked( idx );
631 }
632 
633 void GraphicsScene::itemDoubleClicked( const QModelIndex& idx )
634 {
635  emit qrealClicked( idx );
636 }
637 
639 {
640  d->dragSource = item;
641 }
642 
644 {
645  return d->dragSource;
646 }
647 
656 void GraphicsScene::print( QPrinter* printer, bool drawRowLabels, bool drawColumnLabels )
657 {
658 #ifndef HAVE_PRINTER
659  Q_UNUSED( printer );
660  Q_UNUSED( drawRowLabels );
661  Q_UNUSED( drawColumnLabels );
662 #else
663  QPainter painter( printer );
664  doPrint( &painter, printer->pageRect(), sceneRect().left(), sceneRect().right(), printer, drawRowLabels, drawColumnLabels );
665 #endif
666 }
667 
680 void GraphicsScene::print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels )
681 {
682 #ifndef HAVE_PRINTER
683  Q_UNUSED( printer );
684  Q_UNUSED( start );
685  Q_UNUSED( end );
686  Q_UNUSED( drawRowLabels );
687  Q_UNUSED( drawColumnLabels );
688 #else
689  QPainter painter( printer );
690  doPrint( &painter, printer->pageRect(), start, end, printer, drawRowLabels, drawColumnLabels );
691 #endif
692 }
693 
700 void GraphicsScene::print( QPainter* painter, const QRectF& _targetRect, bool drawRowLabels, bool drawColumnLabels )
701 {
702  QRectF targetRect( _targetRect );
703  if ( targetRect.isNull() ) {
704  targetRect = sceneRect();
705  }
706 
707  doPrint( painter, targetRect, sceneRect().left(), sceneRect().right(), 0, drawRowLabels, drawColumnLabels );
708 }
709 
720 void GraphicsScene::print( QPainter* painter, qreal start, qreal end,
721  const QRectF& _targetRect, bool drawRowLabels, bool drawColumnLabels )
722 {
723  QRectF targetRect( _targetRect );
724  if ( targetRect.isNull() ) {
725  targetRect = sceneRect();
726  }
727 
728  doPrint( painter, targetRect, start, end, 0, drawRowLabels, drawColumnLabels );
729 }
730 
733 void GraphicsScene::doPrint( QPainter* painter, const QRectF& targetRect,
734  qreal start, qreal end,
735  QPrinter* printer, bool drawRowLabels, bool drawColumnLabels )
736 {
737  assert( painter );
738  d->isPrinting = true;
739  d->drawColumnLabels = drawColumnLabels;
740  d->labelsWidth = 0.0;
741 #if QT_VERSION >= QT_VERSION_CHECK(4, 4, 0)
742  QFont sceneFont( font() );
743 #ifdef HAVE_PRINTER
744  if ( printer ) {
745  sceneFont = QFont( font(), printer );
746  if ( font().pointSizeF() >= 0.0 )
747  sceneFont.setPointSizeF( font().pointSizeF() );
748  else if ( font().pointSize() >= 0 )
749  sceneFont.setPointSize( font().pointSize() );
750  else
751  sceneFont.setPixelSize( font().pixelSize() );
752  }
753 #endif
754 #else
755  QFont sceneFont( painter->font() );
756 #ifdef HAVE_PRINTER
757  if ( printer ) {
758  sceneFont = QFont( painter->font(), printer );
759  if ( painter->font().pointSizeF() >= 0.0 )
760  sceneFont.setPointSizeF( painter->font().pointSizeF() );
761  else if ( painter->font().pointSize() >= 0 )
762  sceneFont.setPointSize( painter->font().pointSize() );
763  else
764  sceneFont.setPixelSize( painter->font().pixelSize() );
765  }
766 #endif
767 #endif
768 
769  QGraphicsTextItem dummyTextItem( QLatin1String("X") );
770  dummyTextItem.adjustSize();
771  QFontMetrics fm(dummyTextItem.font());
772  sceneFont.setPixelSize( fm.height() );
773 
774  const QRectF oldScnRect( sceneRect() );
775  QRectF scnRect( oldScnRect );
776  scnRect.setLeft( start );
777  scnRect.setRight( end );
778  bool b = blockSignals( true );
779 
780  /* column labels */
781  if ( d->drawColumnLabels ) {
782  QRectF headerRect( scnRect );
783  headerRect.setHeight( - d->rowController->headerHeight() );
784  scnRect.setTop(scnRect.top() - d->rowController->headerHeight());
785  }
786 
787  /* row labels */
788  QVector<QGraphicsTextItem*> textLabels;
789  if ( drawRowLabels ) {
790  qreal textWidth = 0.;
791  QModelIndex sidx = summaryHandlingModel()->mapToSource( summaryHandlingModel()->index( 0, 0, rootIndex()) );
792  do {
793  QModelIndex idx = summaryHandlingModel()->mapFromSource( sidx );
794  const Span rg=rowController()->rowGeometry( sidx );
795  const QString txt = idx.data( Qt::DisplayRole ).toString();
796  QGraphicsTextItem* item = new QGraphicsTextItem( txt );
797  addItem( item );
798  textLabels << item;
799  item->adjustSize();
800  textWidth = qMax( item->textWidth(), textWidth );
801  item->setPos( 0, rg.start() );
802  } while ( ( sidx = rowController()->indexBelow( sidx ) ).isValid() );
803  // Add a little margin to textWidth
804  textWidth += QFontMetricsF(sceneFont).width( QString::fromLatin1( "X" ) );
805  Q_FOREACH( QGraphicsTextItem* item, textLabels ) {
806  item->setPos( scnRect.left()-textWidth, item->y() );
807  item->show();
808  }
809  scnRect.setLeft( scnRect.left()-textWidth );
810  d->labelsWidth = textWidth;
811  }
812 
813  setSceneRect( scnRect );
814 
815  painter->save();
816  painter->setClipRect( targetRect );
817 
818  qreal yratio = targetRect.height()/scnRect.height();
819  /* If we're not printing multiple pages,
820  * check if the span fits and adjust:
821  */
822  if ( !printer && targetRect.width()/scnRect.width() < yratio ) {
823  yratio = targetRect.width()/scnRect.width();
824  }
825 
826  qreal offset = scnRect.left();
827  int pagecount = 0;
828  while ( offset < scnRect.right() ) {
829  painter->setFont( sceneFont );
830  render( painter, targetRect, QRectF( QPointF( offset, scnRect.top()),
831  QSizeF( targetRect.width()/yratio, scnRect.height() ) ) );
832  offset += targetRect.width()/yratio;
833  ++pagecount;
834  if ( printer && offset < scnRect.right() ) {
835 #ifdef HAVE_PRINTER
836  printer->newPage();
837 #endif
838  } else {
839  break;
840  }
841  }
842 
843  d->isPrinting = false;
844  d->drawColumnLabels = true;
845  d->labelsWidth = 0.0;
846  qDeleteAll( textLabels );
847  blockSignals( b );
848  setSceneRect( oldScnRect );
849  painter->restore();
850 }
851 
852 #include "moc_kdganttgraphicsscene.cpp"
853 
854 
855 #ifndef KDAB_NO_UNIT_TESTS
856 #include "unittest/test.h"
857 
858 #include <QGraphicsLineItem>
859 #include <QPointer>
860 #include <QStandardItemModel>
861 
862 #include "kdganttgraphicsview.h"
863 
864 class SceneTestRowController : public KDGantt::AbstractRowController {
865 private:
866  static const int ROW_HEIGHT;
867  QPointer<QAbstractItemModel> m_model;
868 
869 public:
870  SceneTestRowController()
871  {
872  }
873 
874  void setModel( QAbstractItemModel* model )
875  {
876  m_model = model;
877  }
878 
879  /*reimp*/int headerHeight() const { return 40; }
880 
881  /*reimp*/ bool isRowVisible( const QModelIndex& ) const { return true;}
882  /*reimp*/ bool isRowExpanded( const QModelIndex& ) const { return false; }
883  /*reimp*/ KDGantt::Span rowGeometry( const QModelIndex& idx ) const
884  {
885  return KDGantt::Span( idx.row() * ROW_HEIGHT, ROW_HEIGHT );
886  }
887  /*reimp*/ int maximumItemHeight() const {
888  return ROW_HEIGHT/2;
889  }
890  /*reimp*/int totalHeight() const {
891  return m_model->rowCount()* ROW_HEIGHT;
892  }
893 
894  /*reimp*/ QModelIndex indexAt( int height ) const {
895  return m_model->index( height/ROW_HEIGHT, 0 );
896  }
897 
898  /*reimp*/ QModelIndex indexBelow( const QModelIndex& idx ) const {
899  if ( !idx.isValid() )return QModelIndex();
900  return idx.model()->index( idx.row()+1, idx.column(), idx.parent() );
901  }
902  /*reimp*/ QModelIndex indexAbove( const QModelIndex& idx ) const {
903  if ( !idx.isValid() )return QModelIndex();
904  return idx.model()->index( idx.row()-1, idx.column(), idx.parent() );
905  }
906 
907 };
908 
909 class TestLineItem : public QGraphicsLineItem
910 {
911 public:
912  TestLineItem( bool *destroyedFlag )
913  : QGraphicsLineItem( 0, 0, 10, 10 ), // geometry doesn't matter
914  m_destroyedFlag( destroyedFlag )
915  {}
916 
917  ~TestLineItem()
918  { *m_destroyedFlag = true; }
919 
920 private:
921  bool *m_destroyedFlag;
922 };
923 
924 const int SceneTestRowController::ROW_HEIGHT = 30;
925 
927  QStandardItemModel model;
928 
929  QStandardItem* item = new QStandardItem();
930  item->setData( KDGantt::TypeTask, KDGantt::ItemTypeRole );
931  item->setData( QString::fromLatin1( "Decide on new product" ) );
932  item->setData( QDateTime( QDate( 2007, 3, 1 ) ), KDGantt::StartTimeRole );
933  item->setData( QDateTime( QDate( 2007, 3, 3 ) ), KDGantt::EndTimeRole );
934 
935  QStandardItem* item2 = new QStandardItem();
936  item2->setData( KDGantt::TypeTask, KDGantt::ItemTypeRole );
937  item2->setData( QString::fromLatin1( "Educate personnel" ) );
938  item2->setData( QDateTime( QDate( 2007, 3, 3 ) ), KDGantt::StartTimeRole );
939  item2->setData( QDateTime( QDate( 2007, 3, 6 ) ), KDGantt::EndTimeRole );
940 
941  model.appendRow( item );
942  model.appendRow( item2 );
943 
944  SceneTestRowController rowController;
945  rowController.setModel( &model );
946 
947  KDGantt::GraphicsView graphicsView;
948  graphicsView.setRowController( &rowController );
949  graphicsView.setModel( &model );
950 
951  // Now the interesting stuff - the items above are just for a "realistic environment"
952 
953  bool foreignItemDestroyed = false;
954  TestLineItem *foreignItem = new TestLineItem( &foreignItemDestroyed );
955  graphicsView.scene()->addItem( foreignItem );
956 
957  assertFalse( foreignItemDestroyed );
958  graphicsView.updateScene();
959  assertFalse( foreignItemDestroyed );
960 }
961 #endif /* KDAB_NO_UNIT_TESTS */

Klarälvdalens Datakonsult AB (KDAB)
Qt-related services and products
http://www.kdab.com/
http://www.kdab.com/products/kd-chart/