KGantt

kganttgraphicsview.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 "kganttgraphicsview.h"
10 #include "kganttgraphicsview_p.h"
11 #include "kganttabstractrowcontroller.h"
12 #include "kganttgraphicsitem.h"
13 #include "kganttconstraintmodel.h"
14 #include "kganttdatetimetimelinedialog.h"
15 
16 #include <QMenu>
17 #include <QPainter>
18 #include <QPaintEvent>
19 #include <QResizeEvent>
20 #include <QScrollBar>
21 #include <QAbstractProxyModel>
22 #include <QPrinter>
23 #include <QItemSelectionModel>
24 #include <QGraphicsSceneMouseEvent>
25 #include <QGuiApplication>
26 
27 #include <cassert>
28 
29 #if defined KDAB_EVAL
30 #include "../evaldialog/evaldialog.h"
31 #endif
32 
33 /*\class KGantt::HeaderWidget
34  * \internal
35  */
36 
37 using namespace KGantt;
38 
39 HeaderWidget::HeaderWidget( GraphicsView* parent )
40  : QWidget( parent ), m_offset( 0. ), m_headerType( DateTimeGrid::NoHeader )
41 {
42  assert( parent ); // Parent must be set
43  setMouseTracking(true);
44 }
45 
46 HeaderWidget::~HeaderWidget()
47 {
48 }
49 
50 void HeaderWidget::scrollTo( int v )
51 {
52  m_offset = v;
53  // QWidget::scroll() won't work properly for me on Mac
54  //scroll( static_cast<int>( old-v ), 0 );
55  update();
56 }
57 
58 void HeaderWidget::paintEvent( QPaintEvent* ev )
59 {
60  QPainter p( this );
61  view()->grid()->paintHeader( &p, rect(), ev->rect(), m_offset, this );
62 }
63 
64 bool HeaderWidget::event( QEvent* event )
65 {
66  if ( event->type() == QEvent::ToolTip ) {
67  DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() );
68  if ( grid ) {
69  QHelpEvent *e = static_cast<QHelpEvent*>( event );
70  QDateTime dt = grid->mapFromChart( view()->mapToScene( e->x(), 0 ).x() ).toDateTime();
71  setToolTip( dt.toString() );
72  }
73  }
74  return QWidget::event( event );
75 }
76 
77 void HeaderWidget::mousePressEvent(QMouseEvent *event)
78 {
79  DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() );
80  int mousePosX = event->x();
81  m_headerType = grid->sectionHandleAtPos( view()->mapToScene( event->x(), 0 ).x(), event->pos().y(), geometry() );
82  if (m_headerType != DateTimeGrid::NoHeader) {
83  bool hasCursor = testAttribute(Qt::WA_SetCursor);
84  if (!hasCursor) {
85  setCursor(QCursor(Qt::SplitHCursor));
86  }
87  m_mousePosX = mousePosX;
88  event->accept();
89  return;
90  }
91  QWidget::mousePressEvent( event );
92 }
93 
94 void HeaderWidget::mouseReleaseEvent(QMouseEvent *event)
95 {
96  if ( m_headerType > 0 ) {
97  DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() );
98  int mousePosX = view()->mapToScene( event->x(), 0 ).x();
99  if ( grid->sectionHandleAtPos( mousePosX, event->pos().y(), geometry() ) == DateTimeGrid::NoHeader ) {
100  bool hasCursor = testAttribute(Qt::WA_SetCursor);
101  if (hasCursor) {
102  unsetCursor();
103  }
104  }
105  m_headerType = DateTimeGrid::NoHeader;
106  m_mousePosX = event->x();
108  }
110 }
111 
112 void HeaderWidget::mouseMoveEvent(QMouseEvent *event)
113 {
114  DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() );
115  int mousePosX = event->x();
116  qreal gridX = view()->mapToScene( event->x(), 0.0 ).x();
117  switch ( m_headerType ) {
118  case DateTimeGrid::UpperHeader:
119  {
120  if ( mousePosX > m_mousePosX ) {
121  grid->setDayWidth( qMax<qreal>( 1.0, grid->dayWidth() * 1.05 ) );
122  } else {
123  grid->setDayWidth( qMax<qreal>( 1.0, grid->dayWidth() / 1.05 ) );
124  }
125  m_mousePosX = mousePosX;
126  event->accept();
127  return;
128  }
129  case DateTimeGrid::LowerHeader:
130  {
131  if ( mousePosX > m_mousePosX ) {
132  grid->setDayWidth( qMax<qreal>( 1.0, grid->dayWidth() * 1.01 ) );
133  } else {
134  grid->setDayWidth( qMax<qreal>( 1.0, grid->dayWidth() / 1.01 ) );
135  }
136  m_mousePosX = mousePosX;
137  event->accept();
138  return;
139  }
140  default: {
141  bool hasCursor = testAttribute(Qt::WA_SetCursor);
142  DateTimeGrid::HeaderType type = grid->sectionHandleAtPos( gridX, event->pos().y(), geometry());
143  if (type != DateTimeGrid::NoHeader) {
144  if (!hasCursor) {
145  setCursor(QCursor(Qt::SplitHCursor));
146  }
147  event->accept();
148  return;
149  }
150  if (hasCursor) {
151  unsetCursor();
152  }
153  break;
154  }
155  }
157 }
158 
159 void HeaderWidget::wheelEvent( QWheelEvent *event )
160 {
161  DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() );
162  if ( event->angleDelta().y() > 0 ) {
163  grid->setDayWidth( qMax<qreal>( 1.0, grid->dayWidth() * 1.1 ) );
164  } else {
165  grid->setDayWidth( qMax<qreal>( 1.0, grid->dayWidth() / 1.1 ) );
166  }
167  event->accept();
168 }
169 
170 void HeaderWidget::contextMenuEvent( QContextMenuEvent* event )
171 {
172  QMenu contextMenu;
173 
174  DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() );
175  QAction* actionScaleAuto = nullptr;
176  QAction* actionScaleMonth = nullptr;
177  QAction* actionScaleWeek = nullptr;
178  QAction* actionScaleDay = nullptr;
179  QAction* actionScaleHour = nullptr;
180  QAction* actionZoomIn = nullptr;
181  QAction* actionZoomOut = nullptr;
182  QAction* actionTimeline = nullptr;
183  if ( grid != nullptr )
184  {
185  QMenu* menuScale = new QMenu( tr( "Scale", "@title:menu" ), &contextMenu );
186  QActionGroup* scaleGroup = new QActionGroup( &contextMenu );
187  scaleGroup->setExclusive( true );
188 
189  actionScaleAuto = new QAction( tr( "Auto", "@item:inmenu Automatic scale" ), menuScale );
190  actionScaleAuto->setCheckable( true );
191  actionScaleAuto->setChecked( grid->scale() == DateTimeGrid::ScaleAuto );
192  actionScaleMonth = new QAction( tr( "Month", "@item:inmenu" ), menuScale );
193  actionScaleMonth->setCheckable( true );
194  actionScaleMonth->setChecked( grid->scale() == DateTimeGrid::ScaleMonth );
195  actionScaleWeek = new QAction( tr( "Week", "@item:inmenu" ), menuScale );
196  actionScaleWeek->setCheckable( true );
197  actionScaleWeek->setChecked( grid->scale() == DateTimeGrid::ScaleWeek );
198  actionScaleDay = new QAction( tr( "Day", "@item:inmenu" ), menuScale );
199  actionScaleDay->setCheckable( true );
200  actionScaleDay->setChecked( grid->scale() == DateTimeGrid::ScaleDay );
201  actionScaleHour = new QAction( tr( "Hour", "@item:inmenu" ), menuScale );
202  actionScaleHour->setCheckable( true );
203  actionScaleHour->setChecked( grid->scale() == DateTimeGrid::ScaleHour );
204 
205  scaleGroup->addAction( actionScaleAuto );
206  menuScale->addAction( actionScaleAuto );
207 
208  scaleGroup->addAction( actionScaleMonth );
209  menuScale->addAction( actionScaleMonth );
210 
211  scaleGroup->addAction( actionScaleWeek );
212  menuScale->addAction( actionScaleWeek );
213 
214  scaleGroup->addAction( actionScaleDay );
215  menuScale->addAction( actionScaleDay );
216 
217  scaleGroup->addAction( actionScaleHour );
218  menuScale->addAction( actionScaleHour );
219 
220  contextMenu.addMenu( menuScale );
221 
222  contextMenu.addSeparator();
223 
224  actionZoomIn = new QAction( tr( "Zoom In", "@action:inmenu" ), &contextMenu );
225  contextMenu.addAction( actionZoomIn );
226  actionZoomOut = new QAction( tr( "Zoom Out", "@action:inmenu" ), &contextMenu );
227  contextMenu.addAction( actionZoomOut );
228 
229  contextMenu.addSeparator();
230  actionTimeline = new QAction( tr( "Timeline...", "@action:inmenu" ), &contextMenu );
231  contextMenu.addAction( actionTimeline );
232  }
233 
234  if ( contextMenu.isEmpty() )
235  {
236  event->ignore();
237  return;
238  }
239 
240  const QAction* const action = contextMenu.exec( event->globalPos() );
241  if ( action == nullptr ) {}
242  else if ( action == actionScaleAuto )
243  {
244  assert( grid != nullptr );
245  grid->setScale( DateTimeGrid::ScaleAuto );
246  }
247  else if ( action == actionScaleMonth )
248  {
249  assert( grid != nullptr );
250  grid->setScale( DateTimeGrid::ScaleMonth );
251  }
252  else if ( action == actionScaleWeek )
253  {
254  assert( grid != nullptr );
255  grid->setScale( DateTimeGrid::ScaleWeek );
256  }
257  else if ( action == actionScaleDay )
258  {
259  assert( grid != nullptr );
260  grid->setScale( DateTimeGrid::ScaleDay );
261  }
262  else if ( action == actionScaleHour )
263  {
264  assert( grid != nullptr );
265  grid->setScale( DateTimeGrid::ScaleHour );
266  }
267  else if ( action == actionZoomIn )
268  {
269  assert( grid != nullptr );
270  grid->setDayWidth( grid->dayWidth() * 1.25 );
271  }
272  else if ( action == actionZoomOut )
273  {
274  assert( grid != nullptr );
275  // daywidth *MUST NOT* go below 1.0, it is used as an integer later on
276  grid->setDayWidth( qMax<qreal>( 1.0, grid->dayWidth() * 0.8 ) );
277  }
278  else if ( action == actionTimeline )
279  {
280  assert( grid != nullptr );
281  DateTimeTimeLineDialog dlg(grid->timeLine());
282  dlg.exec();
283  }
284  event->accept();
285 }
286 
287 GraphicsView::Private::Private( GraphicsView* _q )
288  : q( _q ), rowcontroller(nullptr), headerwidget( _q )
289 {
290 }
291 
292 GraphicsView::Private::~Private()
293 {
294 }
295 
296 void GraphicsView::Private::updateHeaderGeometry()
297 {
298  q->setViewportMargins(0,rowcontroller->headerHeight(),0,0);
299  headerwidget.setGeometry( q->viewport()->x(),
300  q->viewport()->y() - rowcontroller->headerHeight(),
301  q->viewport()->width(),
302  rowcontroller->headerHeight() );
303 }
304 
305 void GraphicsView::Private::slotGridChanged()
306 {
307  updateHeaderGeometry();
308  headerwidget.update();
309  q->updateSceneRect();
310  q->update();
311 }
312 
313 void GraphicsView::Private::slotHorizontalScrollValueChanged( int val )
314 {
315  const QRectF viewRect = q->transform().mapRect( q->sceneRect() );
316  headerwidget.scrollTo( val-q->horizontalScrollBar()->minimum()+static_cast<int>( viewRect.left() ) );
317 }
318 
319 void GraphicsView::Private::slotColumnsInserted( const QModelIndex& parent, int start, int end )
320 {
321  Q_UNUSED( start );
322  Q_UNUSED( end );
323  QModelIndex idx = scene.model()->index( 0, 0, scene.summaryHandlingModel()->mapToSource( parent ) );
324  do {
325  scene.updateRow( scene.summaryHandlingModel()->mapFromSource( idx ) );
326  } while ( ( idx = rowcontroller->indexBelow( idx ) ) != QModelIndex() && rowcontroller->isRowVisible( idx ) );
327  //} while ( ( idx = d->treeview.indexBelow( idx ) ) != QModelIndex() && d->treeview.visualRect(idx).isValid() );
328  q->updateSceneRect();
329 }
330 
331 void GraphicsView::Private::slotColumnsRemoved( const QModelIndex& parent, int start, int end )
332 {
333  // TODO
334  Q_UNUSED( start );
335  Q_UNUSED( end );
336  Q_UNUSED( parent );
337  q->updateScene();
338 }
339 
340 void GraphicsView::Private::slotDataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight )
341 {
342  //qDebug() << "GraphicsView::slotDataChanged("<<topLeft<<bottomRight<<")";
343  const QModelIndex parent = topLeft.parent();
344  for ( int row = topLeft.row(); row <= bottomRight.row(); ++row ) {
345  scene.updateRow( scene.summaryHandlingModel()->index( row, 0, parent ) );
346  }
347 }
348 
349 void GraphicsView::Private::slotLayoutChanged()
350 {
351  //qDebug() << "slotLayoutChanged()";
352  q->updateScene();
353 }
354 
355 void GraphicsView::Private::slotModelReset()
356 {
357  //qDebug() << "slotModelReset()";
358  q->updateScene();
359 }
360 
361 void GraphicsView::Private::slotRowsInserted( const QModelIndex& parent, int start, int end )
362 {
363  Q_UNUSED( parent );
364  Q_UNUSED( start );
365  Q_UNUSED( end );
366  q->updateScene(); // TODO: This might be optimised
367 }
368 
369 void GraphicsView::Private::removeConstraintsRecursive( QAbstractProxyModel *summaryModel, const QModelIndex& index )
370 {
371  if ( summaryModel->hasChildren( index ) ) {
372  //qDebug() << "removing constraints from children of"<<index;
373  for ( int row = 0; row < summaryModel->rowCount( index ); ++row ) {
374  const QModelIndex child = summaryModel->index( row, index.column(), index );
375  removeConstraintsRecursive( summaryModel, child );
376  }
377  }
378  //qDebug() << "removing constraints from"<<index;
379  // NOTE: Constraints are mapped to indexes in the summaryModel->sourceModel()
380  const QList<Constraint> clst = scene.constraintModel()->constraintsForIndex( summaryModel->mapToSource( index ) );
381  for ( const Constraint &c : clst ) {
382  scene.constraintModel()->removeConstraint( c );
383  }
384 }
385 
386 void GraphicsView::Private::slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end )
387 {
388  //qDebug() << "GraphicsView::Private::slotRowsAboutToBeRemoved("<<parent<<start<<end<<")";
389  QAbstractProxyModel *summaryModel = scene.summaryHandlingModel();
390  for ( int row = start; row <= end; ++row ) {
391  for ( int col = 0; col < summaryModel->columnCount( parent ); ++col ) {
392  const QModelIndex idx = summaryModel->index( row, col, parent );
393  removeConstraintsRecursive( summaryModel, idx );
394  scene.removeItem( idx );
395  }
396  }
397 }
398 
399 void GraphicsView::Private::slotRowsRemoved( const QModelIndex& parent, int start, int end )
400 {
401  //qDebug() << "GraphicsView::Private::slotRowsRemoved("<<parent<<start<<end<<")";
402  // TODO
403  Q_UNUSED( parent );
404  Q_UNUSED( start );
405  Q_UNUSED( end );
406 
407  q->updateScene();
408 }
409 
410 void GraphicsView::Private::slotItemClicked( const QModelIndex& idx )
411 {
412  QModelIndex sidx = idx;//scene.summaryHandlingModel()->mapToSource( idx );
413  Q_EMIT q->clicked( sidx );
414  if (q->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, q))
415  Q_EMIT q->activated( sidx );
416 }
417 
418 void GraphicsView::Private::slotItemDoubleClicked( const QModelIndex& idx )
419 {
420  QModelIndex sidx = idx;//scene.summaryHandlingModel()->mapToSource( idx );
421  Q_EMIT q->qrealClicked( sidx );
422  if (!q->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, q))
423  Q_EMIT q->activated( sidx );
424 }
425 
426 void GraphicsView::Private::slotHeaderContextMenuRequested( const QPoint& pt )
427 {
428  Q_EMIT q->headerContextMenuRequested( headerwidget.mapToGlobal( pt ) );
429 }
430 
432  : QGraphicsView( parent ), _d( new Private( this ) )
433 {
434 #if defined KDAB_EVAL
435  EvalDialog::checkEvalLicense( "KD Gantt" );
436 #endif
437  connect( horizontalScrollBar(), SIGNAL(valueChanged(int)),
438  this, SLOT(slotHorizontalScrollValueChanged(int)) );
439  connect( &_d->scene, SIGNAL(gridChanged()),
440  this, SLOT(slotGridChanged()) );
441  connect( &_d->scene, SIGNAL(entered(QModelIndex)),
442  this, SIGNAL(entered(QModelIndex)) );
443  connect( &_d->scene, SIGNAL(pressed(QModelIndex)),
444  this, SIGNAL(pressed(QModelIndex)) );
445  connect( &_d->scene, SIGNAL(clicked(QModelIndex)),
446  this, SLOT(slotItemClicked(QModelIndex)) );
447  connect( &_d->scene, SIGNAL(qrealClicked(QModelIndex)),
448  this, SLOT(slotItemDoubleClicked(QModelIndex)) );
449  connect( &_d->scene, SIGNAL(sceneRectChanged(QRectF)),
450  this, SLOT(updateSceneRect()) );
451  connect( &_d->headerwidget, SIGNAL(customContextMenuRequested(QPoint)),
452  this, SLOT(slotHeaderContextMenuRequested(QPoint)) );
453  setScene( &_d->scene );
454 
455  // HACK!
456  setSummaryHandlingModel( _d->scene.summaryHandlingModel() );
457 
458  // So that AbstractGrid::drawBackground() and AbstractGrid::drawForeground()
459  // works properly
461 
462  //setCacheMode( CacheBackground );
463 }
464 
465 
467 {
468  delete _d;
469 }
470 
471 #define d d_func()
472 
473 
475 {
476  if ( d->scene.model() ) {
477  disconnect( d->scene.model() );
478  }
479 
480  d->scene.setModel( model );
481  if (model) {
482  connect( model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
483  this, SLOT(updateSceneRect()) );
484  }
485  updateScene();
486 }
487 
488 
490 {
491  return d->scene.model();
492 }
493 
494 void GraphicsView::setSummaryHandlingModel( QAbstractProxyModel* proxyModel )
495 {
496  disconnect( d->scene.summaryHandlingModel() );
497  d->scene.setSummaryHandlingModel( proxyModel );
498 
499  /* Connections. We have to rely on the treeview
500  * to receive the signals before we do(!)
501  */
502  connect( proxyModel, SIGNAL(columnsInserted(QModelIndex,int,int)),
503  this, SLOT(slotColumnsInserted(QModelIndex,int,int)) );
504  connect( proxyModel, SIGNAL(columnsRemoved(QModelIndex,int,int)),
505  this, SLOT(slotColumnsRemoved(QModelIndex,int,int)) );
506  connect( proxyModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
507  this, SLOT(slotDataChanged(QModelIndex,QModelIndex)) );
508  connect( proxyModel, SIGNAL(layoutChanged()),
509  this, SLOT(slotLayoutChanged()) );
510  connect( proxyModel, SIGNAL(modelReset()),
511  this, SLOT(slotModelReset()) );
512  connect( proxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
513  this, SLOT(slotRowsInserted(QModelIndex,int,int)) );
514  connect( proxyModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
515  this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)) );
516  connect( proxyModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
517  this, SLOT(slotRowsRemoved(QModelIndex,int,int)) );
518 
519  updateScene();
520 }
521 
522 
524 {
525  d->scene.setConstraintModel( cmodel );
526 }
527 
528 
530 {
531  return d->scene.constraintModel();
532 }
533 
534 
536 {
537  return d->scene.summaryHandlingModel();
538 }
539 
540 
542 {
543  d->scene.setRootIndex( idx );
544 }
545 
546 
548 {
549  return d->scene.rootIndex();
550 }
551 
552 
554 {
555  d->scene.setSelectionModel( model );
556 }
557 
558 
560 {
561  return d->scene.selectionModel();
562 }
563 
564 
566 {
567  d->scene.setItemDelegate( delegate );
568 }
569 
570 
572 {
573  return d->scene.itemDelegate();
574 }
575 
576 
578 {
579  d->rowcontroller = rowcontroller;
580  d->scene.setRowController( rowcontroller );
581  updateScene();
582 }
583 
584 
586 {
587  return d->rowcontroller;
588 }
589 
590 
592 {
593  d->scene.setGrid( grid );
594  d->slotGridChanged();
595 }
596 
597 
599 {
600  return d->scene.grid();
601 }
602 
603 
605 {
606  return d->scene.takeGrid();
607 }
608 
609 
611 {
612  d->scene.setReadOnly( ro );
613 }
614 
615 
617 {
618  return d->scene.isReadOnly();
619 }
620 
621 
623 {
624  d->headerwidget.setContextMenuPolicy( p );
625 }
626 
627 
629 {
630  return d->headerwidget.contextMenuPolicy();
631 }
632 
633 
635  const QModelIndex& to,
636  Qt::KeyboardModifiers modifiers )
637 {
638  if ( isReadOnly() ) return;
639  ConstraintModel* cmodel = constraintModel();
640  assert( cmodel );
641  Constraint c( from, to, ( modifiers&Qt::ShiftModifier )?Constraint::TypeHard:Constraint::TypeSoft );
642  if ( cmodel->hasConstraint( c ) ) cmodel->removeConstraint( c );
643  else cmodel->addConstraint( c );
644 }
645 
646 void GraphicsView::resizeEvent( QResizeEvent* ev )
647 {
648  d->updateHeaderGeometry();
649  QRectF r = scene()->itemsBoundingRect();
650  // To scroll more to the left than the actual item start, bug #4516
651  r.setLeft( qMin<qreal>( 0.0, r.left() ) );
652  // TODO: take scrollbars into account (if not always on)
653  // The scene should be at least the size of the viewport
654  QSizeF size = viewport()->size();
655  //TODO: why -2 below? size should be ex. frames etc?
656  if ( size.width() > r.width() ) {
657  r.setWidth( size.width() - 2 );
658  }
659  if ( size.height() > r.height() ) {
660  r.setHeight( size.height() - 2 );
661  }
662  const int totalh = rowController()->totalHeight();
663  if ( r.height() < totalh ) {
664  r.setHeight( totalh );
665  }
666 
667  scene()->setSceneRect( r );
668 
670 }
671 
672 
674 {
675  QGraphicsItem* item = itemAt( pos );
676  if ( GraphicsItem* gitem = qgraphicsitem_cast<GraphicsItem*>( item ) ) {
677  return d->scene.summaryHandlingModel()->mapToSource( gitem->index() );
678  } else {
679  return QModelIndex();
680  }
681 }
682 
683 
685 {
686  d->scene.clearItems();
687 }
688 
689 
691 {
692  d->scene.updateRow( d->scene.summaryHandlingModel()->mapFromSource( idx ) );
693 }
694 
695 
697 {
698  /* What to do with this? We need to shrink the view to
699  * make collapsing items work
700  */
701  qreal range = horizontalScrollBar()->maximum()-horizontalScrollBar()->minimum();
702  const qreal hscroll = horizontalScrollBar()->value()/( range>0?range:1 );
703  QRectF r = d->scene.itemsBoundingRect();
704  // To scroll more to the left than the actual item start, bug #4516
705  r.setTop( 0. );
706  r.setLeft( qMin<qreal>( 0.0, r.left() ) );
707  r.setSize( r.size().expandedTo( viewport()->size() ) );
708  const int totalh = rowController()->totalHeight();
709  if ( r.height() < totalh ) r.setHeight( totalh );
710  d->scene.setSceneRect( r );
711 
712  /* set scrollbar to keep the same time in view */
714  if ( range>0 ) {
715  horizontalScrollBar()->setValue( qRound( hscroll*range ) );
716  } else {
717  // keep header in sync with scene
718  d->headerwidget.scrollTo(r.left());
719  }
720  /* We have to update here to adjust for any rows with no
721  * information because they are painted with a different
722  * background brush
723  */
724  d->scene.invalidate( QRectF(), QGraphicsScene::BackgroundLayer );
725 }
726 
727 
729 {
730  clearItems();
731  if ( !model()) return;
732  if ( !rowController()) return;
733  QModelIndex idx = model()->index( 0, 0, rootIndex() );
734  do {
735  updateRow( idx );
736  } while ( ( idx = rowController()->indexBelow( idx ) ) != QModelIndex() && rowController()->isRowVisible(idx) );
737  //constraintModel()->cleanup();
738  //qDebug() << constraintModel();
739  updateSceneRect();
741 }
742 
743 #if 0
744 TODO: For 3.0
745 
746 GraphicsItem* GraphicsView::createItem( ItemType type ) const
747 {
748  Q_UNUSED(type)
749  return new GraphicsItem;
750 }
751 #endif
752 
753 
755 {
756  d->scene.deleteSubtree( d->scene.summaryHandlingModel()->mapFromSource( idx ) );
757 }
758 
759 
760 void GraphicsView::print( QPrinter* printer, bool drawRowLabels, bool drawColumnLabels )
761 {
762  d->scene.print( printer, drawRowLabels, drawColumnLabels );
763 }
764 
765 
766 void GraphicsView::print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels )
767 {
768  d->scene.print( printer, start, end, drawRowLabels, drawColumnLabels );
769 }
770 
771 
772 void GraphicsView::print( QPainter* painter, const QRectF& targetRect, bool drawRowLabels, bool drawColumnLabels )
773 {
774  d->scene.print(painter, targetRect, drawRowLabels, drawColumnLabels);
775 }
776 
777 
778 void GraphicsView::print( QPainter* painter, qreal start, qreal end,
779  const QRectF& targetRect, bool drawRowLabels, bool drawColumnLabels )
780 {
781  d->scene.print(painter, start, end, targetRect, drawRowLabels, drawColumnLabels);
782 }
783 
784 void GraphicsView::printDiagram( QPrinter *printer, const PrintingContext &context )
785 {
786  d->scene.printDiagram( printer, context );
787 }
788 
789 #include "moc_kganttgraphicsview.cpp"
void customContextMenuRequested(const QPoint &pos)
void setConstraintModel(KGantt::ConstraintModel *)
ItemDelegate * itemDelegate() const
QString toString(Qt::DateFormat format) const const
virtual bool removeConstraint(const Constraint &c)
virtual int rowCount(const QModelIndex &parent) const const =0
typedef KeyboardModifiers
QEvent::Type type() const const
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const =0
void updateRow(const QModelIndex &)
void setSelectionModel(QItemSelectionModel *)
AbstractGrid * grid() const
QAbstractItemModel * model() const
virtual void addConstraint(const QModelIndex &from, const QModelIndex &to, Qt::KeyboardModifiers modifiers)
void setViewportUpdateMode(QGraphicsView::ViewportUpdateMode mode)
AbstractGrid * grid() const
void print(QPrinter *printer, bool drawRowLabels=true, bool drawColumnLabels=true)
AbstractRowController * rowController() const
void setHeaderContextMenuPolicy(Qt::ContextMenuPolicy)
virtual int totalHeight() const =0
void setChecked(bool)
Abstract baseclass for row controllers. A row controller is used by the GraphicsView to nagivate the ...
int x() const const
QSizeF size() const const
SH_ItemView_ActivateItemOnSingleClick
void setSceneRect(const QRectF &rect)
QSizeF expandedTo(const QSizeF &otherSize) const const
QWidget * viewport() const const
void setGrid(KGantt::AbstractGrid *)
QAction * addAction(QAction *action)
QGraphicsScene * scene() const const
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
virtual void mouseReleaseEvent(QMouseEvent *event)
const QPoint & globalPos() const const
void restoreOverrideCursor()
QString tr(const char *sourceText, const char *disambiguation, int n)
QAction * addAction(const QString &text)
qreal left() const const
ContextMenuPolicy
int y() const const
void setLeft(qreal x)
void setHeight(qreal height)
QModelIndex rootIndex() const
bool isEmpty() const const
Qt::ContextMenuPolicy headerContextMenuPolicy() const
QSize size() const const
const QRect & rect() const const
A class used to represent a dependency.
virtual bool hasChildren(const QModelIndex &parent) const const override
PartitionTable::TableType type
virtual void mousePressEvent(QMouseEvent *event)
void printDiagram(QPrinter *printer, const PrintingContext &context)
virtual void mouseMoveEvent(QMouseEvent *event)
int row() const const
QGraphicsItem * itemAt(const QPoint &pos) const const
void deleteSubtree(const QModelIndex &)
QPoint pos() const const
void setScene(QGraphicsScene *scene)
SplitHCursor
void setItemDelegate(KGantt::ItemDelegate *delegate)
QAction * addSeparator()
Global namespace.
QModelIndex parent() const const
QAction * exec()
DateTimeTimeLine * timeLine() const
int x() const const
QAbstractProxyModel * summaryHandlingModel() const
QItemSelectionModel * selectionModel() const
Abstract baseclass for grids. A grid is used to convert between QModelIndex&#39;es and gantt chart values...
void setCheckable(bool)
void setExclusive(bool b)
WA_SetCursor
The PrintingContext class provides options for printing the gantt chart.
QPoint angleDelta() const const
GraphicsView(QWidget *parent=nullptr)
QModelIndex indexAt(const QPoint &pos) const
qreal width() const const
const QAbstractItemModel * model() const const
virtual bool event(QEvent *event) override
ConstraintModel * constraintModel() const
void setTop(qreal y)
void setWidth(qreal width)
void setModel(QAbstractItemModel *)
QAction * addMenu(QMenu *menu)
virtual int columnCount(const QModelIndex &parent) const const =0
void update(qreal x, qreal y, qreal w, qreal h)
void invalidate(qreal x, qreal y, qreal w, qreal h, QGraphicsScene::SceneLayers layers)
int column() const const
QScrollBar * horizontalScrollBar() const const
qreal height() const const
QRectF itemsBoundingRect() const const
void setSize(const QSizeF &size)
QPoint pos() const const
The GraphicsView class provides a model/view implementation of a gantt chart.
void setRowController(KGantt::AbstractRowController *)
qreal height() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const const =0
T qobject_cast(QObject *object)
QObject * parent() const const
void setRootIndex(const QModelIndex &)
virtual bool event(QEvent *event) override
Class used to render gantt items in a KGantt::GraphicsView.
qreal width() const const
virtual void addConstraint(const Constraint &c)
Q_EMITQ_EMIT
bool hasConstraint(const Constraint &c) const
virtual void resizeEvent(QResizeEvent *event) override
bool mapFromChart(const Span &span, const QModelIndex &idx, const QList< Constraint > &constraints=QList< Constraint >()) const override
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.