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
32using namespace KGantt;
33
34namespace {
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
44KGanttTreeView::KGanttTreeView( QAbstractProxyModel* proxy, QWidget* parent )
45 : QTreeView( parent ),
46 m_controller( this, proxy )
47{
48 setHeader( new HeaderView );
49}
50
51KGanttTreeView::~KGanttTreeView()
52{
53}
54
55void 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
67void 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
79View::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
89View::Private::~Private()
90{
91 delete gfxview;
92}
93
94void 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
117void 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
129void 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
149void 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
185void 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
196void 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
205void 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
215void 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
230View::View(QWidget* parent)
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
240View::~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
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
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
383void 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
394void 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
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
450const QAbstractProxyModel* View::ganttProxyModel() const
451{
452 return &( d->ganttProxyModel );
453}
454
455QAbstractProxyModel* View::ganttProxyModel()
456{
457 return &( d->ganttProxyModel );
458}
459
460void 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
475void View::resizeEvent(QResizeEvent*ev)
476{
478}
479
480
482{
483 return d->gfxview->indexAt( pos );
484}
485
486
487void View::print( QPrinter* printer, bool drawRowLabels, bool drawColumnLabels )
488{
489 graphicsView()->print( printer, drawRowLabels, drawColumnLabels );
490}
491
492
493void View::print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels )
494{
495 graphicsView()->print( printer, start, end, drawRowLabels, drawColumnLabels );
496}
497
498
499void 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
508void 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
517void 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
533KDAB_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 */
Abstract baseclass for grids. A grid is used to convert between QModelIndex'es and gantt chart values...
Abstract baseclass for row controllers. A row controller is used by the GraphicsView to nagivate the ...
The GraphicsView class provides a model/view implementation of a gantt chart.
QAbstractItemModel * model() const
void print(QPrinter *printer, bool drawRowLabels=true, bool drawColumnLabels=true)
void printDiagram(QPrinter *printer, const PrintingContext &context)
Class used to render gantt items in a KGantt::GraphicsView.
The PrintingContext class provides options for printing the gantt chart.
Proxy model that supports summary gantt items.
This widget that consists of a QTreeView and a GraphicsView.
void setItemDelegate(KGantt::ItemDelegate *)
QModelIndex indexAt(const QPoint &pos) const
void setLeftView(QAbstractItemView *)
const GraphicsView * graphicsView() const
void setRootIndex(const QModelIndex &idx)
void setRowController(AbstractRowController *)
void setSelectionModel(QItemSelectionModel *smodel)
void setGraphicsView(GraphicsView *)
void setModel(QAbstractItemModel *model)
QItemSelectionModel * selectionModel() const
const QAbstractItemView * leftView() const
ItemDelegate * itemDelegate() const
ConstraintModel * constraintModel() const
void printDiagram(QPrinter *printer, const PrintingContext &context=PrintingContext())
void print(QPrinter *printer, bool drawRowLabels=true, bool drawColumnLabels=true)
void setGrid(KGantt::AbstractGrid *)
AbstractGrid * grid() const
QAbstractItemModel * model() const
AbstractRowController * rowController()
const QSplitter * splitter() const
void setConstraintModel(KGantt::ConstraintModel *)
QModelIndex rootIndex() const
Q_SCRIPTABLE Q_NOREPLY void start()
Global namespace.
@ ItemTypeRole
The item type.
virtual int rowCount(const QModelIndex &parent) const const=0
QAbstractItemModel * model() const const
QModelIndex rootIndex() const const
QItemSelectionModel * selectionModel() const const
void setItemDelegate(QAbstractItemDelegate *delegate)
virtual void setModel(QAbstractItemModel *model)
virtual void setRootIndex(const QModelIndex &index)
virtual void setSelectionModel(QItemSelectionModel *selectionModel)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
void ensureVisible(const QGraphicsItem *item, int xmargin, int ymargin)
QGraphicsScene * scene() const const
virtual QSize sizeHint() const const override
void setContentsMargins(const QMargins &margins)
bool isValid() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
T qobject_cast(QObject *object)
QImage toImage() const const
int & rheight()
AlignTop
Horizontal
ScrollBarAlwaysOff
QModelIndex indexBelow(const QModelIndex &index) const const
virtual void resizeEvent(QResizeEvent *event)
void show()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Sep 27 2024 11:49:41 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.