17 #include "kdganttgraphicsscene_p.h"
21 #include <QApplication>
22 #include <QGraphicsSceneHelpEvent>
26 #include <QTextDocument>
54 , rowController(nullptr)
58 , drawColumnLabels(true)
61 , selectionModel(nullptr)
63 default_grid.setStartDateTime(QDateTime::currentDateTime().addDays(-1));
66 void GraphicsScene::Private::resetConstraintItems()
68 q->clearConstraintItems();
69 if (constraintModel.isNull())
71 const QList<Constraint> clst = constraintModel->constraints();
73 createConstraintItem(c);
78 void GraphicsScene::Private::createConstraintItem(
const Constraint &c)
97 if (citem ==
nullptr) {
105 item = items.value(summaryHandlingModel->mapFromSource(c.
endIndex()), 0);
112 void GraphicsScene::Private::deleteConstraintItem(
const Constraint &c)
114 deleteConstraintItem(findConstraintItem(c));
122 QList<ConstraintGraphicsItem *>::const_iterator it = clst.begin();
123 for (; it != clst.end(); ++it) {
127 if (it != clst.end()) {
131 item = items.value(summaryHandlingModel->mapFromSource(c.
endIndex()), 0);
133 const QList<ConstraintGraphicsItem *> clst = item->
endConstraints();
134 QList<ConstraintGraphicsItem *>::const_iterator it = clst.begin();
135 for (; it != clst.end(); ++it) {
139 if (it != clst.end()) {
146 GraphicsScene::GraphicsScene(QObject *parent)
147 : QGraphicsScene(parent)
148 , _d(new Private(this))
162 void GraphicsScene::init()
164 setItemIndexMethod(QGraphicsScene::NoIndex);
175 if (!
d->itemDelegate.isNull() &&
d->itemDelegate->parent() ==
this)
176 delete d->itemDelegate;
177 d->itemDelegate = delegate;
183 return d->itemDelegate;
188 assert(!
d->summaryHandlingModel.isNull());
189 return d->summaryHandlingModel->sourceModel();
194 assert(!
d->summaryHandlingModel.isNull());
195 d->summaryHandlingModel->setSourceModel(
model);
196 d->grid->setModel(
d->summaryHandlingModel);
202 return d->summaryHandlingModel;
207 proxyModel->setSourceModel(
model());
208 d->summaryHandlingModel = proxyModel;
213 d->grid->setRootIndex(idx);
218 return d->grid->rootIndex();
223 return d->constraintModel;
228 if (!
d->constraintModel.isNull()) {
229 d->constraintModel->disconnect(
this);
231 d->constraintModel = cm;
234 this, &GraphicsScene::slotConstraintAdded);
236 this, &GraphicsScene::slotConstraintRemoved);
237 d->resetConstraintItems();
242 d->selectionModel = smodel;
248 return d->selectionModel;
253 d->rowController = rc;
258 return d->rowController;
263 QAbstractItemModel *
model =
nullptr;
265 grid = &
d->default_grid;
267 d->grid->disconnect(
this);
299 if ( idx.isValid() ) {
300 return idx.model()->index( idx.row(), 0,idx.parent() );
302 return QModelIndex();
316 if ( idx.isValid() ) {
317 const QAbstractItemModel*
model = idx.model();
318 return model->index( idx.row(),
model->columnCount( idx.parent() )-1,idx.parent() );
320 return QModelIndex();
346 void GraphicsScene::Private::recursiveUpdateMultiItem(
const Span &span,
const QModelIndex &idx)
350 const int itemtype = summaryHandlingModel->data(idx,
ItemTypeRole).toInt();
352 item = q->createItem(
static_cast<ItemType>(itemtype));
354 q->insertItem(idx, item);
359 while ((child = idx.model()->index(cr, 0, idx)).isValid()) {
360 recursiveUpdateMultiItem(span, child);
368 if (!rowidx.isValid())
371 const QAbstractItemModel *
model = rowidx.model();
379 for (QModelIndex treewalkidx = sidx; treewalkidx.isValid(); treewalkidx = treewalkidx.parent()) {
386 bool blocked = blockSignals(
true);
396 if (itemtype ==
TypeMulti && !isExpanded) {
397 d->recursiveUpdateMultiItem(rg, idx);
413 blockSignals(blocked);
418 if (!
d->constraintModel.isNull()) {
421 const QList<Constraint> clst =
d->constraintModel->constraintsForIndex(sidx);
423 QModelIndex other_idx;
447 d->items.insert(idx, item);
454 QHash<QPersistentModelIndex, GraphicsItem *>::iterator it =
d->items.find(idx);
455 if (it !=
d->items.end()) {
465 const QSet<ConstraintGraphicsItem *> clst =
466 #if QT_VERSION > QT_VERSION_CHECK(5, 14, 0)
467 QSet<ConstraintGraphicsItem *>(startConstraints.begin(), startConstraints.end()) + QSet<ConstraintGraphicsItem *>(endConstraints.begin(), endConstraints.end());
469 QSet<ConstraintGraphicsItem *>::fromList(startConstraints) + QSet<ConstraintGraphicsItem *>::fromList(endConstraints);
473 d->deleteConstraintItem(citem);
486 QHash<QPersistentModelIndex, GraphicsItem *>::const_iterator it =
d->items.find(idx);
487 return (it !=
d->items.end()) ? *it : 0;
495 QHash<QPersistentModelIndex, GraphicsItem *>::const_iterator it =
d->items.find(idx);
496 return (it !=
d->items.end()) ? *it : 0;
501 QHash<QPersistentModelIndex, GraphicsItem *>::const_iterator it =
d->items.constBegin();
502 for (; it !=
d->items.constEnd(); ++it) {
508 QList<QGraphicsItem *> items =
d->q->items();
514 for (QHash<QPersistentModelIndex, GraphicsItem *>::iterator it =
d->items.begin();
515 it !=
d->items.end(); ++it) {
517 const QPersistentModelIndex &idx = it.key();
520 invalidate(QRectF(), QGraphicsScene::BackgroundLayer);
528 const QModelIndex parent(idx.parent());
529 const int colcount = idx.model()->columnCount(parent);
531 for (
int i = 0; i < colcount; ++i) {
532 removeItem(parent.model()->index(idx.row(), i, parent));
537 for (
int i = 0; i < rowcount; ++i) {
545 return d->findConstraintItem(c);
556 d->createConstraintItem(c);
561 d->deleteConstraintItem(c);
564 void GraphicsScene::slotGridChanged()
573 #ifndef QT_NO_TOOLTIP
574 QGraphicsItem *item = itemAt(
helpEvent->scenePos(), QTransform());
575 if (
auto *gitem = qgraphicsitem_cast<GraphicsItem *>(item)) {
576 QToolTip::showText(
helpEvent->screenPos(), gitem->ganttToolTip());
577 }
else if (
auto *citem = qgraphicsitem_cast<ConstraintGraphicsItem *>(item)) {
587 QRectF scn(sceneRect());
589 if (
d->isPrinting &&
d->drawColumnLabels) {
590 QRectF headerRect(scn.topLeft() + QPointF(
d->labelsWidth, 0),
591 QSizeF(scn.width() -
d->labelsWidth,
d->rowController->headerHeight()));
593 d->grid->paintHeader(painter, headerRect, rect, 0,
nullptr);
599 QRectF labelsTabRect( scn.topLeft(), QSizeF(
d->labelsWidth, headerRect.height() ) );
601 QStyleOptionHeader opt;
602 opt.rect = labelsTabRect.toRect();
603 opt.text = QLatin1String(
"");
604 opt.textAlignment = Qt::AlignCenter;
605 style()->drawControl(QStyle::CE_Header, &opt, painter, 0);
608 scn.setTop(headerRect.bottom());
609 scn.setLeft(headerRect.left());
610 rect = rect.intersected(scn);
612 d->grid->paintGrid(painter, scn, rect,
d->rowController);
614 d->grid->drawBackground(painter, rect);
619 d->grid->drawForeground(painter, rect);
644 d->dragSource = item;
649 return d->dragSource;
664 Q_UNUSED(drawRowLabels);
665 Q_UNUSED(drawColumnLabels);
667 QPainter painter(printer);
668 doPrint(&painter, printer->pageLayout().paintRectPixels(printer->resolution()), sceneRect().left(), sceneRect().right(), printer, drawRowLabels, drawColumnLabels);
684 void GraphicsScene::print(QPrinter *printer, qreal start, qreal end,
bool drawRowLabels,
bool drawColumnLabels)
690 Q_UNUSED(drawRowLabels);
691 Q_UNUSED(drawColumnLabels);
693 QPainter painter(printer);
694 doPrint(&painter, printer->pageLayout().paintRectPixels(printer->resolution()), start, end, printer, drawRowLabels, drawColumnLabels);
704 void GraphicsScene::print(QPainter *painter,
const QRectF &_targetRect,
bool drawRowLabels,
bool drawColumnLabels)
706 QRectF targetRect(_targetRect);
707 if (targetRect.isNull()) {
708 targetRect = sceneRect();
711 doPrint(painter, targetRect, sceneRect().left(), sceneRect().right(),
nullptr, drawRowLabels, drawColumnLabels);
725 const QRectF &_targetRect,
bool drawRowLabels,
bool drawColumnLabels)
727 QRectF targetRect(_targetRect);
728 if (targetRect.isNull()) {
729 targetRect = sceneRect();
732 doPrint(painter, targetRect, start, end,
nullptr, drawRowLabels, drawColumnLabels);
737 void GraphicsScene::doPrint(QPainter *painter,
const QRectF &targetRect,
738 qreal start, qreal end,
739 QPrinter *printer,
bool drawRowLabels,
bool drawColumnLabels)
742 d->isPrinting =
true;
743 d->drawColumnLabels = drawColumnLabels;
744 d->labelsWidth = 0.0;
745 QFont sceneFont(font());
748 sceneFont = QFont(font(), printer);
749 if (font().pointSizeF() >= 0.0)
750 sceneFont.setPointSizeF(font().pointSizeF());
751 else if (font().pointSize() >= 0)
752 sceneFont.setPointSize(font().pointSize());
754 sceneFont.setPixelSize(font().pixelSize());
758 QGraphicsTextItem dummyTextItem(QLatin1String(
"X"));
759 dummyTextItem.adjustSize();
760 QFontMetrics fm(dummyTextItem.font());
761 sceneFont.setPixelSize(fm.height());
763 const QRectF oldScnRect(sceneRect());
764 QRectF scnRect(oldScnRect);
765 scnRect.setLeft(start);
766 scnRect.setRight(end);
767 bool b = blockSignals(
true);
770 if (
d->drawColumnLabels) {
771 QRectF headerRect(scnRect);
772 headerRect.setHeight(-
d->rowController->headerHeight());
773 scnRect.setTop(scnRect.top() -
d->rowController->headerHeight());
777 QVector<QGraphicsTextItem *> textLabels;
779 qreal textWidth = 0.;
784 const QString txt = idx.data(Qt::DisplayRole).toString();
785 auto *item =
new QGraphicsTextItem(txt);
789 textWidth = qMax(item->textWidth(), textWidth);
790 item->setPos(0, rg.
start());
791 }
while ((sidx =
rowController()->indexBelow(sidx)).isValid());
793 textWidth += QFontMetricsF(sceneFont).horizontalAdvance(QString::fromLatin1(
"X"));
794 for (QGraphicsTextItem *item : qAsConst(textLabels)) {
795 item->setPos(scnRect.left() - textWidth, item->y());
798 scnRect.setLeft(scnRect.left() - textWidth);
799 d->labelsWidth = textWidth;
802 setSceneRect(scnRect);
805 painter->setClipRect(targetRect);
807 qreal yratio = targetRect.height() / scnRect.height();
811 if (!printer && targetRect.width() / scnRect.width() < yratio) {
812 yratio = targetRect.width() / scnRect.width();
815 qreal offset = scnRect.left();
817 while (offset < scnRect.right()) {
818 painter->setFont(sceneFont);
819 render(painter, targetRect, QRectF(QPointF(offset, scnRect.top()), QSizeF(targetRect.width() / yratio, scnRect.height())));
820 offset += targetRect.width() / yratio;
822 if (printer && offset < scnRect.right()) {
831 d->isPrinting =
false;
832 d->drawColumnLabels =
true;
833 d->labelsWidth = 0.0;
834 qDeleteAll(textLabels);
836 setSceneRect(oldScnRect);
840 #include "moc_kdganttgraphicsscene.cpp"
842 #ifndef KDAB_NO_UNIT_TESTS
843 #include "unittest/test.h"
845 #include <QGraphicsLineItem>
847 #include <QStandardItemModel>
854 static const int ROW_HEIGHT;
855 QPointer<QAbstractItemModel> m_model;
858 SceneTestRowController()
862 void setModel(QAbstractItemModel *model)
867 int headerHeight()
const override
872 bool isRowVisible(
const QModelIndex &)
const override
876 bool isRowExpanded(
const QModelIndex &)
const override
880 KDGantt::Span rowGeometry(
const QModelIndex &idx)
const override
884 int maximumItemHeight()
const override
886 return ROW_HEIGHT / 2;
888 int totalHeight()
const override
890 return m_model->rowCount() * ROW_HEIGHT;
893 QModelIndex indexAt(
int height)
const override
895 return m_model->index(height / ROW_HEIGHT, 0);
898 QModelIndex indexBelow(
const QModelIndex &idx)
const override
901 return QModelIndex();
902 return idx.model()->index(idx.row() + 1, idx.column(), idx.parent());
904 QModelIndex indexAbove(
const QModelIndex &idx)
const override
907 return QModelIndex();
908 return idx.model()->index(idx.row() - 1, idx.column(), idx.parent());
912 class TestLineItem :
public QGraphicsLineItem
915 TestLineItem(
bool *destroyedFlag)
916 : QGraphicsLineItem(0, 0, 10, 10)
918 m_destroyedFlag(destroyedFlag)
922 ~TestLineItem()
override
924 *m_destroyedFlag =
true;
928 bool *m_destroyedFlag;
931 const int SceneTestRowController::ROW_HEIGHT = 30;
935 QStandardItemModel model;
937 auto *item =
new QStandardItem();
939 item->setData(QString::fromLatin1(
"Decide on new product"));
943 auto *item2 =
new QStandardItem();
945 item2->setData(QString::fromLatin1(
"Educate personnel"));
949 model.appendRow(item);
950 model.appendRow(item2);
952 SceneTestRowController rowController;
953 rowController.setModel(&model);
961 bool foreignItemDestroyed =
false;
962 auto *foreignItem =
new TestLineItem(&foreignItemDestroyed);
963 graphicsView.scene()->addItem(foreignItem);
965 assertFalse(foreignItemDestroyed);
967 assertFalse(foreignItemDestroyed);
Abstract baseclass for grids.
Abstract baseclass for row controllers.
virtual bool isRowExpanded(const QModelIndex &idx) const =0
virtual Span rowGeometry(const QModelIndex &idx) const =0
const Constraint & constraint() const
QString ganttToolTip() const
The ConstraintModel keeps track of the interdependencies between gantt items in a View.
void constraintRemoved(const KDGantt::Constraint &)
void constraintAdded(const KDGantt::Constraint &)
A class used to represent a dependency.
QModelIndex endIndex() const
bool compareIndexes(const Constraint &other) const
QModelIndex startIndex() const
void removeStartConstraint(ConstraintGraphicsItem *)
void addEndConstraint(ConstraintGraphicsItem *)
void updateItem(const Span &rowgeometry, const QPersistentModelIndex &idx)
void removeEndConstraint(ConstraintGraphicsItem *)
QList< ConstraintGraphicsItem * > endConstraints() const
void setIndex(const QPersistentModelIndex &idx)
void addStartConstraint(ConstraintGraphicsItem *)
QList< ConstraintGraphicsItem * > startConstraints() const
void entered(const QModelIndex &index)
void itemDoubleClicked(const QModelIndex &)
void setConstraintModel(ConstraintModel *)
AbstractRowController * rowController() const
ItemDelegate * itemDelegate() const
void itemClicked(const QModelIndex &)
void removeItem(const QModelIndex &)
ConstraintModel * constraintModel() const
void itemEntered(const QModelIndex &)
void clicked(const QModelIndex &index)
void setSummaryHandlingModel(QAbstractProxyModel *)
void qrealClicked(const QModelIndex &index)
void updateRow(const QModelIndex &idx)
void drawBackground(QPainter *painter, const QRectF &rect) override
static QModelIndex dataIndex(const QModelIndex &idx)
Returns the index pointing to the last column in the same row as idx.
void setRowController(AbstractRowController *rc)
void pressed(const QModelIndex &index)
void drawForeground(QPainter *painter, const QRectF &rect) override
void setModel(QAbstractItemModel *)
void setGrid(AbstractGrid *grid)
GraphicsItem * findItem(const QModelIndex &) const
void setItemDelegate(ItemDelegate *)
static QModelIndex mainIndex(const QModelIndex &idx)
void deleteSubtree(const QModelIndex &)
GraphicsItem * createItem(ItemType type) const
Creates a new item of type type.
GraphicsItem * dragSource() const
void helpEvent(QGraphicsSceneHelpEvent *helpEvent) override
void itemPressed(const QModelIndex &)
void print(QPrinter *printer, bool drawRowLabels=true, bool drawColumnLabels=true)
Print the Gantt chart using printer.
void setSelectionModel(QItemSelectionModel *selectionmodel)
ConstraintGraphicsItem * findConstraintItem(const Constraint &) const
QModelIndex rootIndex() const
void clearConstraintItems()
AbstractGrid * grid() const
~GraphicsScene() override
void setRootIndex(const QModelIndex &idx)
QItemSelectionModel * selectionModel() const
void insertItem(const QPersistentModelIndex &, GraphicsItem *)
void setDragSource(GraphicsItem *item)
QAbstractItemModel * model() const
QAbstractProxyModel * summaryHandlingModel() const
The GraphicsView class provides a model/view implementation of a gantt chart.
void setModel(QAbstractItemModel *)
Sets the model to be displayed in this view to model.
void setRowController(AbstractRowController *)
Sets the AbstractRowController used by this view.
Class used to render gantt items in a KDGantt::GraphicsView.
A class representing a start point and a length.
Proxy model that supports summary gantt items.
KDAB_SCOPED_UNITTEST_SIMPLE(KDGantt, GraphicsView, "test")
ItemType
The values of this enum are used to represent the different types of gantt items that KDGantt underst...