diff --git a/src/KGantt/kganttgraphicsitem.cpp b/src/KGantt/kganttgraphicsitem.cpp index 05968d2..e11472b 100644 --- a/src/KGantt/kganttgraphicsitem.cpp +++ b/src/KGantt/kganttgraphicsitem.cpp @@ -1,580 +1,573 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "kganttgraphicsitem.h" #include "kganttgraphicsscene.h" #include "kganttgraphicsview.h" #include "kganttitemdelegate.h" #include "kganttconstraintgraphicsitem.h" #include "kganttconstraintmodel.h" #include "kganttconstraint.h" #include "kganttabstractgrid.h" #include "kganttabstractrowcontroller.h" #include #include #include #include #include #include #include -#include #include #include #include /*!\class KGantt::GraphicsItem * \internal */ using namespace KGantt; typedef QGraphicsItem BASE; namespace { class Updater { bool *u_ptr; bool oldval; public: Updater( bool* u ) : u_ptr( u ), oldval( *u ) { *u=true; } ~Updater() { *u_ptr = oldval; } }; } GraphicsItem::GraphicsItem( QGraphicsItem* parent, GraphicsScene* scene ) : BASE( parent ), m_isupdating( false ) { if ( scene ) scene->addItem( this ); init(); } GraphicsItem::GraphicsItem( const QModelIndex& idx, QGraphicsItem* parent, GraphicsScene* scene ) : BASE( parent ), m_index( idx ), m_isupdating( false ) { init(); if ( scene ) scene->addItem( this ); } GraphicsItem::~GraphicsItem() { } void GraphicsItem::init() { setCacheMode( QGraphicsItem::DeviceCoordinateCache ); setFlags( ItemIsMovable|ItemIsSelectable|ItemIsFocusable ); setAcceptHoverEvents( true ); setHandlesChildEvents( true ); setZValue( 100. ); m_dragline = nullptr; } int GraphicsItem::type() const { return Type; } StyleOptionGanttItem GraphicsItem::getStyleOption() const { StyleOptionGanttItem opt; if (!m_index.isValid()) { // TODO: find out why we get invalid indexes //qDebug()<<"GraphicsItem::getStyleOption: Invalid index"; return opt; } opt.itemRect = rect(); opt.boundingRect = boundingRect(); QVariant tp = m_index.model()->data( m_index, TextPositionRole ); if (tp.isValid()) { opt.displayPosition = static_cast(tp.toInt()); } else { #if 0 qDebug() << "Item" << m_index.model()->data( m_index, Qt::DisplayRole ).toString() << ", ends="<data( m_index, Qt::TextAlignmentRole ); if ( da.isValid() ) { opt.displayAlignment = static_cast< Qt::Alignment >( da.toInt() ); } else { switch ( opt.displayPosition ) { case StyleOptionGanttItem::Left: opt.displayAlignment = Qt::AlignLeft|Qt::AlignVCenter; break; case StyleOptionGanttItem::Right: opt.displayAlignment = Qt::AlignRight|Qt::AlignVCenter; break; case StyleOptionGanttItem::Hidden: // fall through case StyleOptionGanttItem::Center: opt.displayAlignment = Qt::AlignCenter; break; } } opt.grid = scene()->grid(); opt.text = m_index.model()->data( m_index, Qt::DisplayRole ).toString(); if ( isEnabled() ) opt.state |= QStyle::State_Enabled; if ( isSelected() ) opt.state |= QStyle::State_Selected; if ( hasFocus() ) opt.state |= QStyle::State_HasFocus; return opt; } GraphicsScene* GraphicsItem::scene() const { return qobject_cast( QGraphicsItem::scene() ); } void GraphicsItem::setRect( const QRectF& r ) { #if 0 qDebug() << "GraphicsItem::setRect("<isReadOnly() && m_index.isValid() && m_index.model()->flags( m_index ) & Qt::ItemIsEditable; } void GraphicsItem::paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget ) { Q_UNUSED( widget ); if ( boundingRect().isValid() && scene() ) { StyleOptionGanttItem opt = getStyleOption(); *static_cast(&opt) = *static_cast( option ); //opt.fontMetrics = painter->fontMetrics(); scene()->itemDelegate()->paintGanttItem( painter, opt, index() ); } } void GraphicsItem::setIndex( const QPersistentModelIndex& idx ) { m_index=idx; update(); } QString GraphicsItem::ganttToolTip() const { return scene()->itemDelegate()->toolTip( index() ); } QRectF GraphicsItem::boundingRect() const { return m_boundingrect; } QPointF GraphicsItem::startConnector( int relationType ) const { switch ( relationType ) { case Constraint::StartStart: case Constraint::StartFinish: return mapToScene( m_rect.left(), m_rect.top()+m_rect.height()/2. ); default: break; } return mapToScene( m_rect.right(), m_rect.top()+m_rect.height()/2. ); } QPointF GraphicsItem::endConnector( int relationType ) const { switch ( relationType ) { case Constraint::FinishFinish: case Constraint::StartFinish: return mapToScene( m_rect.right(), m_rect.top()+m_rect.height()/2. ); default: break; } return mapToScene( m_rect.left(), m_rect.top()+m_rect.height()/2. ); } void GraphicsItem::constraintsChanged() { if ( !scene() || !scene()->itemDelegate() ) return; const Span bs = scene()->itemDelegate()->itemBoundingSpan( getStyleOption(), index() ); const QRectF br = boundingRect(); setBoundingRect( QRectF( bs.start(), 0., bs.length(), br.height() ) ); } void GraphicsItem::addStartConstraint( ConstraintGraphicsItem* item ) { assert( item ); m_startConstraints << item; item->setStart( startConnector( item->constraint().relationType() ) ); constraintsChanged(); } void GraphicsItem::addEndConstraint( ConstraintGraphicsItem* item ) { assert( item ); m_endConstraints << item; item->setEnd( endConnector( item->constraint().relationType() ) ); constraintsChanged(); } void GraphicsItem::removeStartConstraint( ConstraintGraphicsItem* item ) { assert( item ); m_startConstraints.removeAll( item ); constraintsChanged(); } void GraphicsItem::removeEndConstraint( ConstraintGraphicsItem* item ) { assert( item ); m_endConstraints.removeAll( item ); constraintsChanged(); } void GraphicsItem::updateConstraintItems() { { // Workaround for multiple definition error with MSVC6 Q_FOREACH( ConstraintGraphicsItem* item, m_startConstraints ) { QPointF s = startConnector( item->constraint().relationType() ); item->setStart( s ); }} {// Workaround for multiple definition error with MSVC6 Q_FOREACH( ConstraintGraphicsItem* item, m_endConstraints ) { QPointF e = endConnector( item->constraint().relationType() ); item->setEnd( e ); }} } void GraphicsItem::updateItem( const Span& rowGeometry, const QPersistentModelIndex& idx ) { //qDebug() << "GraphicsItem::updateItem("<grid()->mapToChart( static_cast(idx) ); setPos( QPointF( s.start(), rowGeometry.start() ) ); setRect( QRectF( 0., 0., s.length(), rowGeometry.length() ) ); setIndex( idx ); const Span bs = scene()->itemDelegate()->itemBoundingSpan( getStyleOption(), index() ); //qDebug() << "boundingSpan for" << getStyleOption().text << rect() << "is" << bs; setBoundingRect( QRectF( bs.start(), 0., bs.length(), rowGeometry.length() ) ); const int maxh = scene()->rowController()->maximumItemHeight(); if ( maxh < rowGeometry.length() ) { QRectF r = rect(); const Qt::Alignment align = getStyleOption().displayAlignment; if ( align & Qt::AlignTop ) { // Do nothing } else if ( align & Qt::AlignBottom ) { r.setY( rowGeometry.length()-maxh ); } else { // Center r.setY( ( rowGeometry.length()-maxh ) / 2. ); } r.setHeight( maxh ); setRect( r ); } //scene()->setSceneRect( scene()->sceneRect().united( mapToScene( boundingRect() ).boundingRect() ) ); //updateConstraintItems(); } QVariant GraphicsItem::itemChange( GraphicsItemChange change, const QVariant& value ) { if ( !isUpdating() && change==ItemPositionChange && scene() ) { QPointF newPos=value.toPointF(); if ( isEditable() ) { newPos.setY( pos().y() ); return newPos; } else { return pos(); } } else if ( change==QGraphicsItem::ItemSelectedChange ) { if ( index().isValid() && !( index().model()->flags( index() ) & Qt::ItemIsSelectable ) ) { // Reject selection attempt return QVariant::fromValue( false ); } - - if ( value.toBool() ) { - scene()->selectionModel()->select( index(), QItemSelectionModel::Select ); - } else { - scene()->selectionModel()->select( index(), QItemSelectionModel::Deselect ); - } } return QGraphicsItem::itemChange( change, value ); } void GraphicsItem::focusInEvent( QFocusEvent* event ) { Q_UNUSED( event ); - scene()->selectionModel()->select( index(), QItemSelectionModel::SelectCurrent ); } void GraphicsItem::updateModel() { //qDebug() << "GraphicsItem::updateModel()"; if ( isEditable() ) { QAbstractItemModel* model = const_cast( index().model() ); #if !defined(NDEBUG) ConstraintModel* cmodel = scene()->constraintModel(); #endif assert( model ); assert( cmodel ); if ( model ) { //ItemType typ = static_cast( model->data( index(), // ItemTypeRole ).toInt() ); QList constraints; for ( QList::iterator it1 = m_startConstraints.begin() ; it1 != m_startConstraints.end() ; ++it1 ) constraints.push_back((*it1)->proxyConstraint()); for ( QList::iterator it2 = m_endConstraints.begin() ; it2 != m_endConstraints.end() ; ++it2 ) constraints.push_back((*it2)->proxyConstraint()); if ( scene()->grid()->mapFromChart( Span( scenePos().x(), rect().width() ), index(), constraints ) ) { scene()->updateRow( index().parent() ); } } } } void GraphicsItem::hoverMoveEvent( QGraphicsSceneHoverEvent* event ) { if ( !isEditable() ) return; StyleOptionGanttItem opt = getStyleOption(); ItemDelegate::InteractionState istate = scene()->itemDelegate()->interactionStateFor( event->pos(), opt, index() ); switch ( istate ) { case ItemDelegate::State_ExtendLeft: #ifndef QT_NO_CURSOR setCursor( Qt::SizeHorCursor ); #endif scene()->itemEntered( index() ); break; case ItemDelegate::State_ExtendRight: #ifndef QT_NO_CURSOR setCursor( Qt::SizeHorCursor ); #endif scene()->itemEntered( index() ); break; case ItemDelegate::State_Move: #ifndef QT_NO_CURSOR setCursor( Qt::SplitHCursor ); #endif scene()->itemEntered( index() ); break; default: #ifndef QT_NO_CURSOR unsetCursor(); #endif break; }; } void GraphicsItem::hoverLeaveEvent( QGraphicsSceneHoverEvent* ) { #ifndef QT_NO_CURSOR unsetCursor(); #endif } void GraphicsItem::mousePressEvent( QGraphicsSceneMouseEvent* event ) { //qDebug() << "GraphicsItem::mousePressEvent("<itemDelegate()->interactionStateFor( event->pos(), opt, index() ); // If State_None is returned by interactionStateFor(), we ignore this event so that // it can get forwarded to another item that's below this one. Needed, for example, // to allow items to be moved that are placed below the label of another item. if ( istate != ItemDelegate::State_None ) { m_istate = istate; m_presspos = event->pos(); m_pressscenepos = event->scenePos(); - scene()->itemPressed( index() ); + + scene()->itemPressed( index(), event ); switch ( m_istate ) { case ItemDelegate::State_ExtendLeft: case ItemDelegate::State_ExtendRight: default: /* State_Move */ - BASE::mousePressEvent( event ); + if (!(flags() & ItemIsMovable)) { + event->ignore(); + } break; } } else { event->ignore(); } } void GraphicsItem::mouseReleaseEvent( QGraphicsSceneMouseEvent* event ) { //qDebug() << "GraphicsItem::mouseReleaseEvent("<itemClicked( index() ); } delete m_dragline; m_dragline = nullptr; if ( scene()->dragSource() ) { // Create a new constraint GraphicsItem* other = qgraphicsitem_cast( scene()->itemAt( event->scenePos(), QTransform() ) ); if ( other && scene()->dragSource()!=other && other->index().data(KGantt::ItemTypeRole) == KGantt::TypeEvent ) { // The code below fixes bug KDCH-696. // Modified the code to add constraint even if the user drags and drops // constraint on left part of the TypeEvent symbol(i.e diamond symbol) QRectF itemRect = other->rect().adjusted(-other->rect().height()/2.0, 0, 0, 0 ); if ( other->mapToScene( itemRect ).boundingRect().contains( event->scenePos() )) { GraphicsView* view = qobject_cast( event->widget()->parentWidget() ); if ( view ) { view->addConstraint( scene()->summaryHandlingModel()->mapToSource( scene()->dragSource()->index() ), scene()->summaryHandlingModel()->mapToSource( other->index() ), event->modifiers() ); } } } else { if ( other && scene()->dragSource()!=other && other->mapToScene( other->rect() ).boundingRect().contains( event->scenePos() )) { GraphicsView* view = qobject_cast( event->widget()->parentWidget() ); if ( view ) { view->addConstraint( scene()->summaryHandlingModel()->mapToSource( scene()->dragSource()->index() ), scene()->summaryHandlingModel()->mapToSource( other->index() ), event->modifiers() ); } } } scene()->setDragSource( nullptr ); //scene()->update(); } else { if ( isEditable() ) { updateItemFromMouse(event->scenePos()); // It is important to set m_presspos to null here because // when the sceneRect updates because we move the item // a MouseMoveEvent will be delivered, and we have to // protect against that m_presspos = QPointF(); updateModel(); // without this command we sometimes get a white area at the left side of a task item // after we moved that item right-ways into a grey weekend section of the scene: scene()->update(); } } m_presspos = QPointF(); - BASE::mouseReleaseEvent( event ); } void GraphicsItem::mouseDoubleClickEvent( QGraphicsSceneMouseEvent* event ) { const int typ = static_cast( index().model()->data( index(), ItemTypeRole ).toInt() ); StyleOptionGanttItem opt = getStyleOption(); ItemDelegate::InteractionState istate = scene()->itemDelegate()->interactionStateFor( event->pos(), opt, index() ); if ( (istate != ItemDelegate::State_None) || (typ == TypeSummary)) { scene()->itemDoubleClicked( index() ); } BASE::mouseDoubleClickEvent( event ); } void GraphicsItem::updateItemFromMouse( const QPointF& scenepos ) { //qDebug() << "GraphicsItem::updateItemFromMouse("< #include #include #include #include #include #include #include #include #include #include // defines HAVE_PRINTER if support for printing should be included #ifdef _WIN32_WCE // There is no printer support under wince even if QT_NO_PRINTER is not set #else #ifndef QT_NO_PRINTER #define HAVE_PRINTER #endif #endif /*!\class KGantt::GraphicsScene * \internal */ using namespace KGantt; GraphicsScene::Private::Private( GraphicsScene* _q ) : q( _q ), dragSource( nullptr ), itemDelegate( new ItemDelegate( _q ) ), rowController( nullptr ), grid( &default_grid ), readOnly( false ), isPrinting( false ), drawColumnLabels( true ), labelsWidth( 0.0 ), summaryHandlingModel( new SummaryHandlingProxyModel( _q ) ), selectionModel( nullptr ) { default_grid.setStartDateTime( QDateTime::currentDateTime().addDays( -1 ) ); } void GraphicsScene::Private::clearConstraintItems() { for(ConstraintGraphicsItem *citem : constraintItems) { // remove constraint from items first for(GraphicsItem *item : items) { item->removeStartConstraint(citem); item->removeEndConstraint(citem); } q->removeItem(citem); delete citem; } constraintItems.clear(); } void GraphicsScene::Private::resetConstraintItems() { clearConstraintItems(); if ( constraintModel.isNull() ) return; QList clst = constraintModel->constraints(); Q_FOREACH( const Constraint& c, clst ) { createConstraintItem( c ); } q->updateItems(); } void GraphicsScene::Private::createConstraintItem( const Constraint& c ) { GraphicsItem* sitem = q->findItem( summaryHandlingModel->mapFromSource( c.startIndex() ) ); GraphicsItem* eitem = q->findItem( summaryHandlingModel->mapFromSource( c.endIndex() ) ); if ( sitem && eitem ) { ConstraintGraphicsItem* citem = new ConstraintGraphicsItem( c ); sitem->addStartConstraint( citem ); eitem->addEndConstraint( citem ); constraintItems.append( citem ); q->addItem( citem ); } //q->insertConstraintItem( c, citem ); } // Delete the constraint item, and clean up pointers in the start- and end item void GraphicsScene::Private::deleteConstraintItem( ConstraintGraphicsItem *citem ) { //qDebug()<<"GraphicsScene::Private::deleteConstraintItem citem="<constraint(); GraphicsItem* item = items.value( summaryHandlingModel->mapFromSource( c.startIndex() ), nullptr ); if ( item ) { item->removeStartConstraint( citem ); } item = items.value( summaryHandlingModel->mapFromSource( c.endIndex() ), nullptr ); if ( item ) { item->removeEndConstraint( citem ); } constraintItems.removeAt(constraintItems.indexOf(citem)); delete citem; } void GraphicsScene::Private::deleteConstraintItem( const Constraint& c ) { deleteConstraintItem( findConstraintItem( c ) ); } ConstraintGraphicsItem* GraphicsScene::Private::findConstraintItem( const Constraint& c ) const { GraphicsItem* item = items.value( summaryHandlingModel->mapFromSource( c.startIndex() ), nullptr ); if ( item ) { const QList clst = item->startConstraints(); QList::const_iterator it = clst.begin(); for ( ; it != clst.end() ; ++it ) { if ( c.compareIndexes((*it)->constraint()) ) break; } if ( it != clst.end() ) { return *it; } } item = items.value( summaryHandlingModel->mapFromSource( c.endIndex() ), nullptr ); if ( item ) { const QList clst = item->endConstraints(); QList::const_iterator it = clst.begin(); for ( ; it != clst.end() ; ++it ) { if ( c.compareIndexes( (*it)->constraint() ) ) break; } if ( it != clst.end() ) { return *it; } } return nullptr; } // NOTE: we might get here after indexes are invalidated, so cannot do any controlled cleanup void GraphicsScene::Private::clearItems() { for(GraphicsItem *item : items) { q->removeItem(item); delete item; } items.clear(); // do last to avoid cleaning up items clearConstraintItems(); } GraphicsScene::GraphicsScene( QObject* parent ) : QGraphicsScene( parent ), _d( new Private( this ) ) { init(); } GraphicsScene::~GraphicsScene() { qDeleteAll( items() ); delete _d; } #define d d_func() void GraphicsScene::init() { setItemIndexMethod( QGraphicsScene::NoIndex ); setConstraintModel( new ConstraintModel( this ) ); connect( d->grid, SIGNAL(gridChanged()), this, SLOT(slotGridChanged()) ); } /* NOTE: The delegate should really be a property * of the view, but that doesn't really fit at * this time */ void GraphicsScene::setItemDelegate( ItemDelegate* delegate ) { if ( !d->itemDelegate.isNull() && d->itemDelegate->parent()==this ) delete d->itemDelegate; d->itemDelegate = delegate; update(); } ItemDelegate* GraphicsScene::itemDelegate() const { return d->itemDelegate; } QAbstractItemModel* GraphicsScene::model() const { assert(!d->summaryHandlingModel.isNull()); return d->summaryHandlingModel->sourceModel(); } void GraphicsScene::setModel( QAbstractItemModel* model ) { assert(!d->summaryHandlingModel.isNull()); d->summaryHandlingModel->setSourceModel(model); d->grid->setModel( d->summaryHandlingModel ); setSelectionModel( new QItemSelectionModel( model, this ) ); } QAbstractProxyModel* GraphicsScene::summaryHandlingModel() const { return d->summaryHandlingModel; } void GraphicsScene::setSummaryHandlingModel( QAbstractProxyModel* proxyModel ) { proxyModel->setSourceModel( model() ); d->summaryHandlingModel = proxyModel; } void GraphicsScene::setRootIndex( const QModelIndex& idx ) { d->grid->setRootIndex( idx ); } QModelIndex GraphicsScene::rootIndex() const { return d->grid->rootIndex(); } ConstraintModel* GraphicsScene::constraintModel() const { return d->constraintModel; } void GraphicsScene::setConstraintModel( ConstraintModel* cm ) { if ( !d->constraintModel.isNull() ) { d->constraintModel->disconnect( this ); d->clearConstraintItems(); } d->constraintModel = cm; connect( cm, SIGNAL(constraintAdded(KGantt::Constraint)), this, SLOT(slotConstraintAdded(KGantt::Constraint)) ); connect( cm, SIGNAL(constraintRemoved(KGantt::Constraint)), this, SLOT(slotConstraintRemoved(KGantt::Constraint)) ); d->resetConstraintItems(); } void GraphicsScene::setSelectionModel( QItemSelectionModel* smodel ) { if (d->selectionModel) { d->selectionModel->disconnect( this ); } d->selectionModel = smodel; if (smodel) { connect(d->selectionModel, SIGNAL(modelChanged(QAbstractItemModel*)), this, SLOT(selectionModelChanged(QAbstractItemModel*))); connect( smodel, SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)), this, SLOT(slotSelectionChanged(const QItemSelection&,const QItemSelection&)) ); } } QItemSelectionModel* GraphicsScene::selectionModel() const { return d->selectionModel; } void GraphicsScene::setRowController( AbstractRowController* rc ) { d->rowController = rc; } AbstractRowController* GraphicsScene::rowController() const { return d->rowController; } void GraphicsScene::setGrid( AbstractGrid* grid ) { QAbstractItemModel* model = nullptr; if ( grid == nullptr ) grid = &d->default_grid; if ( d->grid ) { d->grid->disconnect( this ); model = d->grid->model(); } d->grid = grid; connect( d->grid, SIGNAL(gridChanged()), this, SLOT(slotGridChanged()) ); d->grid->setModel( model ); slotGridChanged(); } AbstractGrid* GraphicsScene::grid() const { return d->grid; } void GraphicsScene::setReadOnly( bool ro ) { d->readOnly = ro; } bool GraphicsScene::isReadOnly() const { return d->readOnly; } /* Returns the index with column=0 fromt the * same row as idx and with the same parent. * This is used to traverse the tree-structure * of the model */ QModelIndex GraphicsScene::mainIndex( const QModelIndex& idx ) { #if 0 if ( idx.isValid() ) { return idx.model()->index( idx.row(), 0,idx.parent() ); } else { return QModelIndex(); } #else return idx; #endif } /*! Returns the index pointing to the last column * in the same row as idx. This can be thought of * as in "inverse" of mainIndex() */ QModelIndex GraphicsScene::dataIndex( const QModelIndex& idx ) { #if 0 if ( idx.isValid() ) { const QAbstractItemModel* model = idx.model(); return model->index( idx.row(), model->columnCount( idx.parent() )-1,idx.parent() ); } else { return QModelIndex(); } #else return idx; #endif } /*! Creates a new item of type type. */ GraphicsItem* GraphicsScene::createItem( ItemType type ) const { assert(views().count() == 1); GraphicsView *v = qobject_cast(views().first()); assert(v); return v->createItem(type); } void GraphicsScene::Private::recursiveUpdateMultiItem( const Span& span, const QModelIndex& idx ) { //qDebug() << "recursiveUpdateMultiItem("<findItem( idx ); const int itemtype = summaryHandlingModel->data( idx, ItemTypeRole ).toInt(); if (!item) { item = q->createItem( static_cast( itemtype ) ); item->setIndex( idx ); q->insertItem( idx, item); } item->updateItem( span, idx ); QModelIndex child; int cr = 0; while ( ( child = summaryHandlingModel->index( cr, 0, idx ) ).isValid() ) { recursiveUpdateMultiItem( span, child ); ++cr; } } void GraphicsScene::updateRow( const QModelIndex& rowidx ) { //qDebug() << "GraphicsScene::updateRow("<mapToSource( rowidx ); Span rg = rowController()->rowGeometry( sidx ); for ( QModelIndex treewalkidx = sidx; treewalkidx.isValid(); treewalkidx = treewalkidx.parent() ) { if ( treewalkidx.data( ItemTypeRole ).toInt() == TypeMulti && !rowController()->isRowExpanded( treewalkidx )) { rg = rowController()->rowGeometry( treewalkidx ); } } bool blocked = blockSignals( true ); for ( int col = 0; col < summaryHandlingModel()->columnCount( rowidx.parent() ); ++col ) { const QModelIndex idx = summaryHandlingModel()->index( rowidx.row(), col, rowidx.parent() ); const QModelIndex sidx = summaryHandlingModel()->mapToSource( idx ); const int itemtype = summaryHandlingModel()->data( idx, ItemTypeRole ).toInt(); const bool isExpanded = rowController()->isRowExpanded( sidx ); if ( itemtype == TypeNone ) { removeItem( idx ); continue; } if ( itemtype == TypeMulti && !isExpanded ) { d->recursiveUpdateMultiItem( rg, idx ); } else { if ( summaryHandlingModel()->data( rowidx.parent(), ItemTypeRole ).toInt() == TypeMulti && !isExpanded ) { //continue; } GraphicsItem* item = findItem( idx ); if (!item) { item = createItem( static_cast( itemtype ) ); item->setIndex( idx ); insertItem(idx, item); } const Span span = rowController()->rowGeometry( sidx ); item->updateItem( span, idx ); } } blockSignals( blocked ); } void GraphicsScene::insertItem( const QPersistentModelIndex& idx, GraphicsItem* item ) { if ( !d->constraintModel.isNull() ) { // Create items for constraints const QModelIndex sidx = summaryHandlingModel()->mapToSource( idx ); const QList clst = d->constraintModel->constraintsForIndex( sidx ); Q_FOREACH( const Constraint& c, clst ) { QModelIndex other_idx; if ( c.startIndex() == sidx ) { other_idx = c.endIndex(); GraphicsItem* other_item = d->items.value(summaryHandlingModel()->mapFromSource( other_idx ),nullptr); if ( !other_item ) continue; ConstraintGraphicsItem* citem = new ConstraintGraphicsItem( c ); item->addStartConstraint( citem ); other_item->addEndConstraint( citem ); d->constraintItems.append( citem ); addItem( citem ); } else if ( c.endIndex() == sidx ) { other_idx = c.startIndex(); GraphicsItem* other_item = d->items.value(summaryHandlingModel()->mapFromSource( other_idx ),nullptr); if ( !other_item ) continue; ConstraintGraphicsItem* citem = new ConstraintGraphicsItem( c ); other_item->addStartConstraint( citem ); item->addEndConstraint( citem ); d->constraintItems.append( citem ); addItem( citem ); } else { assert( 0 ); // Impossible } } } d->items.insert( idx, item ); addItem( item ); } void GraphicsScene::removeItem( const QModelIndex& idx ) { //qDebug() << "GraphicsScene::removeItem("<::iterator it = d->items.find( idx ); if ( it != d->items.end() ) { GraphicsItem* item = *it; assert( item ); // We have to remove the item from the list first because // there is a good chance there will be reentrant calls d->items.erase( it ); { // Remove any constraintitems attached const QSet clst = QSet::fromList( item->startConstraints() ) + QSet::fromList( item->endConstraints() ); Q_FOREACH( ConstraintGraphicsItem* citem, clst ) { d->deleteConstraintItem( citem ); } } // Get rid of the item delete item; } } GraphicsItem* GraphicsScene::findItem( const QModelIndex& idx ) const { if ( !idx.isValid() ) return nullptr; assert( idx.model() == summaryHandlingModel() ); QHash::const_iterator it = d->items.find( idx ); return ( it != d->items.end() )?*it:nullptr; } GraphicsItem* GraphicsScene::findItem( const QPersistentModelIndex& idx ) const { if ( !idx.isValid() ) return nullptr; assert( idx.model() == summaryHandlingModel() ); QHash::const_iterator it = d->items.find( idx ); return ( it != d->items.end() )?*it:nullptr; } void GraphicsScene::clearItems() { d->clearItems(); } void GraphicsScene::updateItems() { for ( QHash::iterator it = d->items.begin(); it != d->items.end(); ++it ) { GraphicsItem* const item = it.value(); const QPersistentModelIndex& idx = it.key(); item->updateItem( Span( item->pos().y(), item->rect().height() ), idx ); } invalidate( QRectF(), QGraphicsScene::BackgroundLayer ); } void GraphicsScene::deleteSubtree( const QModelIndex& _idx ) { QModelIndex idx = dataIndex( _idx ); if ( !idx.model() ) return; const QModelIndex parent( idx.parent() ); const int colcount = idx.model()->columnCount( parent ); {for ( int i = 0; i < colcount; ++i ) { removeItem( summaryHandlingModel()->index(idx.row(), i, parent ) ); }} const int rowcount = summaryHandlingModel()->rowCount( _idx ); {for ( int i = 0; i < rowcount; ++i ) { deleteSubtree( summaryHandlingModel()->index( i, summaryHandlingModel()->columnCount(_idx)-1, _idx ) ); }} } ConstraintGraphicsItem* GraphicsScene::findConstraintItem( const Constraint& c ) const { return d->findConstraintItem( c ); } void GraphicsScene::slotConstraintAdded( const KGantt::Constraint& c ) { d->createConstraintItem( c ); } void GraphicsScene::slotConstraintRemoved( const KGantt::Constraint& c ) { d->deleteConstraintItem( c ); } void GraphicsScene::slotGridChanged() { updateItems(); update(); emit gridChanged(); } void GraphicsScene::selectionModelChanged(QAbstractItemModel *model) { - qInfo()<selectionModel->disconnect(this); for (const QModelIndex &idx : deselected.indexes()) { GraphicsItem *item = findItem(idx.model() == d->summaryHandlingModel ? idx : d->summaryHandlingModel->mapFromSource(idx)); if (item) { item->setSelected(false); } } for (const QModelIndex &idx : selected.indexes()) { GraphicsItem *item = findItem(idx.model() == d->summaryHandlingModel ? idx : d->summaryHandlingModel->mapFromSource(idx)); if (item) { item->setSelected(true); } } - connect(d->selectionModel, SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)), - this, SLOT(slotSelectionChanged(const QItemSelection&,const QItemSelection&)) ); + update(); } void GraphicsScene::helpEvent( QGraphicsSceneHelpEvent *helpEvent ) { #ifndef QT_NO_TOOLTIP QGraphicsItem *item = itemAt( helpEvent->scenePos(), QTransform() ); if ( GraphicsItem* gitem = qgraphicsitem_cast( item ) ) { QToolTip::showText(helpEvent->screenPos(), gitem->ganttToolTip()); } else if ( ConstraintGraphicsItem* citem = qgraphicsitem_cast( item ) ) { QToolTip::showText(helpEvent->screenPos(), citem->ganttToolTip()); } else { QGraphicsScene::helpEvent( helpEvent ); } #endif /* QT_NO_TOOLTIP */ } void GraphicsScene::drawBackground( QPainter* painter, const QRectF& _rect ) { QRectF scn( sceneRect() ); QRectF rect( _rect ); if ( d->isPrinting && d->drawColumnLabels ) { QRectF headerRect( scn.topLeft()+QPointF( d->labelsWidth, 0 ), QSizeF( scn.width()-d->labelsWidth, d->rowController->headerHeight() )); d->grid->paintHeader( painter, headerRect, rect, 0, nullptr ); #if 0 /* We have to blank out the part of the header that is invisible during * normal rendering when we are printing. */ QRectF labelsTabRect( scn.topLeft(), QSizeF( d->labelsWidth, headerRect.height() ) ); QStyleOptionHeader opt; opt.rect = labelsTabRect.toRect(); opt.text = QLatin1String(""); opt.textAlignment = Qt::AlignCenter; style()->drawControl(QStyle::CE_Header, &opt, painter, 0); #endif scn.setTop( headerRect.bottom() ); scn.setLeft( headerRect.left() ); rect = rect.intersected( scn ); } d->grid->paintGrid( painter, scn, rect, d->rowController ); d->grid->drawBackground(painter, rect); } void GraphicsScene::drawForeground( QPainter* painter, const QRectF& rect ) { d->grid->drawForeground(painter, rect); } void GraphicsScene::itemEntered( const QModelIndex& idx ) { emit entered( idx ); } -void GraphicsScene::itemPressed( const QModelIndex& idx ) +void GraphicsScene::itemPressed( const QModelIndex& idx, QGraphicsSceneMouseEvent *event ) { + if (event->button() == Qt::LeftButton) { + QItemSelectionModel::SelectionFlags flags; + if (event->modifiers() & Qt::ControlModifier) { + flags |= QItemSelectionModel::Toggle; + } else { + flags |= QItemSelectionModel::ClearAndSelect; + } + d->selectionModel->select(idx, flags); + } emit pressed( idx ); } void GraphicsScene::itemClicked( const QModelIndex& idx ) { emit clicked( idx ); } void GraphicsScene::itemDoubleClicked( const QModelIndex& idx ) { emit qrealClicked( idx ); } void GraphicsScene::setDragSource( GraphicsItem* item ) { d->dragSource = item; } GraphicsItem* GraphicsScene::dragSource() const { return d->dragSource; } /*! Print the Gantt chart using \a printer. If \a drawRowLabels * is true (the default), each row will have it's label printed * on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. * * This version of print() will print multiple pages. */ void GraphicsScene::print( QPrinter* printer, bool drawRowLabels, bool drawColumnLabels ) { #ifndef HAVE_PRINTER Q_UNUSED( printer ); Q_UNUSED( drawRowLabels ); Q_UNUSED( drawColumnLabels ); #else QPainter painter( printer ); doPrint( &painter, printer->pageRect(), sceneRect().left(), sceneRect().right(), printer, drawRowLabels, drawColumnLabels ); #endif } /*! Print part of the Gantt chart from \a start to \a end using \a printer. * If \a drawRowLabels is true (the default), each row will have it's * label printed on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. * * This version of print() will print multiple pages. * * To print a certain range of a chart with a DateTimeGrid, use * qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const * to figure out the values for \a start and \a end. */ void GraphicsScene::print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels ) { #ifndef HAVE_PRINTER Q_UNUSED( printer ); Q_UNUSED( start ); Q_UNUSED( end ); Q_UNUSED( drawRowLabels ); Q_UNUSED( drawColumnLabels ); #else QPainter painter( printer ); doPrint( &painter, printer->pageRect(), start, end, printer, drawRowLabels, drawColumnLabels ); #endif } /*! Render the GanttView inside the rectangle \a target using the painter \a painter. * If \a drawRowLabels is true (the default), each row will have it's * label printed on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. */ void GraphicsScene::print( QPainter* painter, const QRectF& _targetRect, bool drawRowLabels, bool drawColumnLabels ) { QRectF targetRect( _targetRect ); if ( targetRect.isNull() ) { targetRect = sceneRect(); } doPrint( painter, targetRect, sceneRect().left(), sceneRect().right(), nullptr, drawRowLabels, drawColumnLabels ); } /*! Render the GanttView inside the rectangle \a target using the painter \a painter. * If \a drawRowLabels is true (the default), each row will have it's * label printed on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. * * To print a certain range of a chart with a DateTimeGrid, use * qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const * to figure out the values for \a start and \a end. */ void GraphicsScene::print( QPainter* painter, qreal start, qreal end, const QRectF& _targetRect, bool drawRowLabels, bool drawColumnLabels ) { QRectF targetRect( _targetRect ); if ( targetRect.isNull() ) { targetRect = sceneRect(); } doPrint( painter, targetRect, start, end, nullptr, drawRowLabels, drawColumnLabels ); } /*!\internal */ void GraphicsScene::doPrint( QPainter* painter, const QRectF& targetRect, qreal start, qreal end, QPrinter* printer, bool drawRowLabels, bool drawColumnLabels ) { assert( painter ); d->isPrinting = true; d->drawColumnLabels = drawColumnLabels; d->labelsWidth = 0.0; QFont sceneFont( font() ); #ifdef HAVE_PRINTER if ( printer ) { sceneFont = QFont( font(), printer ); if ( font().pointSizeF() >= 0.0 ) sceneFont.setPointSizeF( font().pointSizeF() ); else if ( font().pointSize() >= 0 ) sceneFont.setPointSize( font().pointSize() ); else sceneFont.setPixelSize( font().pixelSize() ); } #endif QGraphicsTextItem dummyTextItem( QLatin1String("X") ); dummyTextItem.adjustSize(); QFontMetrics fm(dummyTextItem.font()); sceneFont.setPixelSize( fm.height() ); const QRectF oldScnRect( sceneRect() ); QRectF scnRect( oldScnRect ); scnRect.setLeft( start ); scnRect.setRight( end ); bool b = blockSignals( true ); /* column labels */ if ( d->drawColumnLabels ) { QRectF headerRect( scnRect ); headerRect.setHeight( - d->rowController->headerHeight() ); scnRect.setTop(scnRect.top() - d->rowController->headerHeight()); } /* row labels */ QVector textLabels; if ( drawRowLabels ) { qreal textWidth = 0.; qreal charWidth = QFontMetricsF(sceneFont).boundingRect( QString::fromLatin1( "X" ) ).width(); QModelIndex sidx = summaryHandlingModel()->mapToSource( summaryHandlingModel()->index( 0, 0, rootIndex()) ); do { QModelIndex idx = summaryHandlingModel()->mapFromSource( sidx ); const Span rg=rowController()->rowGeometry( sidx ); const QString txt = idx.data( Qt::DisplayRole ).toString(); QGraphicsTextItem* item = new QGraphicsTextItem( txt ); addItem( item ); textLabels << item; item->setTextWidth( QFontMetricsF(sceneFont).boundingRect( txt ).width() + charWidth ); textWidth = qMax( item->textWidth(), textWidth ); item->setPos( 0, rg.start() ); } while ( ( sidx = rowController()->indexBelow( sidx ) ).isValid() ); Q_FOREACH( QGraphicsTextItem* item, textLabels ) { item->setPos( scnRect.left()-textWidth, item->y() ); item->show(); } scnRect.setLeft( scnRect.left()-textWidth ); d->labelsWidth = textWidth; } setSceneRect( scnRect ); painter->save(); painter->setClipRect( targetRect ); qreal yratio = targetRect.height()/scnRect.height(); /* If we're not printing multiple pages, * check if the span fits and adjust: */ if ( !printer && targetRect.width()/scnRect.width() < yratio ) { yratio = targetRect.width()/scnRect.width(); } qreal offset = scnRect.left(); int pagecount = 0; while ( offset < scnRect.right() ) { painter->setFont( sceneFont ); render( painter, targetRect, QRectF( QPointF( offset, scnRect.top()), QSizeF( targetRect.width()/yratio, scnRect.height() ) ) ); offset += targetRect.width()/yratio; ++pagecount; if ( printer && offset < scnRect.right() ) { #ifdef HAVE_PRINTER printer->newPage(); #endif } else { break; } } d->isPrinting = false; d->drawColumnLabels = true; d->labelsWidth = 0.0; qDeleteAll( textLabels ); blockSignals( b ); setSceneRect( oldScnRect ); painter->restore(); } #include "moc_kganttgraphicsscene.cpp" #ifndef KDAB_NO_UNIT_TESTS #include "unittest/test.h" #include #include #include #include "kganttgraphicsview.h" class SceneTestRowController : public KGantt::AbstractRowController { private: static const int ROW_HEIGHT; QPointer m_model; public: SceneTestRowController() { } void setModel( QAbstractItemModel* model ) { m_model = model; } /*reimp*/int headerHeight() const override { return 40; } /*reimp*/ bool isRowVisible( const QModelIndex& ) const override { return true;} /*reimp*/ bool isRowExpanded( const QModelIndex& ) const override { return false; } /*reimp*/ KGantt::Span rowGeometry( const QModelIndex& idx ) const override { return KGantt::Span( idx.row() * ROW_HEIGHT, ROW_HEIGHT ); } /*reimp*/ int maximumItemHeight() const override { return ROW_HEIGHT/2; } /*reimp*/int totalHeight() const override { return m_model->rowCount()* ROW_HEIGHT; } /*reimp*/ QModelIndex indexAt( int height ) const override { return m_model->index( height/ROW_HEIGHT, 0 ); } /*reimp*/ QModelIndex indexBelow( const QModelIndex& idx ) const override { if ( !idx.isValid() )return QModelIndex(); return idx.model()->index( idx.row()+1, idx.column(), idx.parent() ); } /*reimp*/ QModelIndex indexAbove( const QModelIndex& idx ) const override { if ( !idx.isValid() )return QModelIndex(); return idx.model()->index( idx.row()-1, idx.column(), idx.parent() ); } }; class TestLineItem : public QGraphicsLineItem { public: TestLineItem( bool *destroyedFlag ) : QGraphicsLineItem( 0, 0, 10, 10 ), // geometry doesn't matter m_destroyedFlag( destroyedFlag ) {} ~TestLineItem() { *m_destroyedFlag = true; } private: bool *m_destroyedFlag; }; const int SceneTestRowController::ROW_HEIGHT = 30; KDAB_SCOPED_UNITTEST_SIMPLE( KGantt, GraphicsView, "test" ) { QStandardItemModel model; QStandardItem* item = new QStandardItem(); item->setData( KGantt::TypeTask, KGantt::ItemTypeRole ); item->setData( QString::fromLatin1( "Decide on new product" ) ); item->setData( QDateTime( QDate( 2007, 3, 1 ) ), KGantt::StartTimeRole ); item->setData( QDateTime( QDate( 2007, 3, 3 ) ), KGantt::EndTimeRole ); QStandardItem* item2 = new QStandardItem(); item2->setData( KGantt::TypeTask, KGantt::ItemTypeRole ); item2->setData( QString::fromLatin1( "Educate personnel" ) ); item2->setData( QDateTime( QDate( 2007, 3, 3 ) ), KGantt::StartTimeRole ); item2->setData( QDateTime( QDate( 2007, 3, 6 ) ), KGantt::EndTimeRole ); model.appendRow( item ); model.appendRow( item2 ); SceneTestRowController rowController; rowController.setModel( &model ); KGantt::GraphicsView graphicsView; graphicsView.setRowController( &rowController ); graphicsView.setModel( &model ); // Now the interesting stuff - the items above are just for a "realistic environment" bool foreignItemDestroyed = false; TestLineItem *foreignItem = new TestLineItem( &foreignItemDestroyed ); graphicsView.scene()->addItem( foreignItem ); assertFalse( foreignItemDestroyed ); graphicsView.updateScene(); assertFalse( foreignItemDestroyed ); } #endif /* KDAB_NO_UNIT_TESTS */ diff --git a/src/KGantt/kganttgraphicsscene.h b/src/KGantt/kganttgraphicsscene.h index 858afd0..decf068 100644 --- a/src/KGantt/kganttgraphicsscene.h +++ b/src/KGantt/kganttgraphicsscene.h @@ -1,143 +1,144 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef KGANTTGRAPHICSSCENE_H #define KGANTTGRAPHICSSCENE_H #include #include #include #include #include "kganttglobal.h" QT_BEGIN_NAMESPACE class QAbstractProxyModel; class QItemSelectionModel; class QItemSelection; class QPrinter; class QAbstractItemModel; +class QGraphicsSceneMouseEvent; QT_END_NAMESPACE namespace KGantt { class AbstractGrid; class AbstractRowController; class GraphicsItem; class Constraint; class ConstraintModel; class ConstraintGraphicsItem; class ItemDelegate; class KGANTT_EXPORT GraphicsScene : public QGraphicsScene { Q_OBJECT KGANTT_DECLARE_PRIVATE_BASE_POLYMORPHIC( GraphicsScene ) public: explicit GraphicsScene( QObject* parent = nullptr ); virtual ~GraphicsScene(); //qreal dateTimeToSceneX( const QDateTime& dt ) const; //QDateTime sceneXtoDateTime( qreal x ) const; static QModelIndex mainIndex( const QModelIndex& idx ); static QModelIndex dataIndex( const QModelIndex& idx ); QAbstractItemModel* model() const; QAbstractProxyModel* summaryHandlingModel() const; QModelIndex rootIndex() const; ConstraintModel* constraintModel() const; QItemSelectionModel* selectionModel() const; void insertItem( const QPersistentModelIndex&, GraphicsItem* ); void removeItem( const QModelIndex& ); using QGraphicsScene::removeItem; GraphicsItem* findItem( const QModelIndex& ) const; GraphicsItem* findItem( const QPersistentModelIndex& ) const; void updateItems(); void clearItems(); void deleteSubtree( const QModelIndex& ); ConstraintGraphicsItem* findConstraintItem( const Constraint& ) const; QList findConstraintItems( const QModelIndex& idx ) const; void setItemDelegate( ItemDelegate* ); ItemDelegate* itemDelegate() const; void setRowController( AbstractRowController* rc ); AbstractRowController* rowController() const; void setGrid( AbstractGrid* grid ); AbstractGrid* grid() const; bool isReadOnly() const; void updateRow( const QModelIndex& idx ); GraphicsItem* createItem( ItemType type ) const; /* used by GraphicsItem */ void itemEntered( const QModelIndex& ); - void itemPressed( const QModelIndex& ); + void itemPressed( const QModelIndex& idx, QGraphicsSceneMouseEvent *event ); void itemClicked( const QModelIndex& ); void itemDoubleClicked( const QModelIndex& ); void setDragSource( GraphicsItem* item ); GraphicsItem* dragSource() const; /* Printing */ void print( QPrinter* printer, bool drawRowLabels = true, bool drawColumnLabels = true ); void print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels = true, bool drawColumnLabels = true ); void print( QPainter* painter, const QRectF& target = QRectF(), bool drawRowLabels=true, bool drawColumnLabels = true ); void print( QPainter* painter, qreal start, qreal end, const QRectF& target = QRectF(), bool drawRowLabels=true, bool drawColumnLabels = true ); Q_SIGNALS: void gridChanged(); void clicked( const QModelIndex & index ); void qrealClicked( const QModelIndex & index ); void entered( const QModelIndex & index ); void pressed( const QModelIndex & index ); protected: /*reimp*/ void helpEvent( QGraphicsSceneHelpEvent *helpEvent ) override; /*reimp*/ void drawBackground( QPainter* painter, const QRectF& rect ) override; /*reimp*/ void drawForeground( QPainter* painter, const QRectF& rect ) override; public Q_SLOTS: void setModel( QAbstractItemModel* ); void setSummaryHandlingModel( QAbstractProxyModel* ); void setConstraintModel( ConstraintModel* ); void setRootIndex( const QModelIndex& idx ); void setSelectionModel( QItemSelectionModel* selectionmodel ); void setReadOnly( bool ); private Q_SLOTS: /* slots for ConstraintModel */ void slotConstraintAdded( const KGantt::Constraint& ); void slotConstraintRemoved( const KGantt::Constraint& ); void slotGridChanged(); void slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected); void selectionModelChanged(QAbstractItemModel *); private: void doPrint( QPainter* painter, const QRectF& targetRect, qreal start, qreal end, QPrinter* printer, bool drawRowLabels, bool drawColumnLabels ); }; } #endif /* KGANTTGRAPHICSSCENE_H */ diff --git a/src/KGantt/kganttgraphicsview.cpp b/src/KGantt/kganttgraphicsview.cpp index 645c6cf..80e6716 100644 --- a/src/KGantt/kganttgraphicsview.cpp +++ b/src/KGantt/kganttgraphicsview.cpp @@ -1,811 +1,813 @@ /* * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved. * * This file is part of the KGantt library. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "kganttgraphicsview.h" #include "kganttgraphicsview_p.h" #include "kganttabstractrowcontroller.h" #include "kganttgraphicsitem.h" #include "kganttconstraintmodel.h" #include "kganttdatetimetimelinedialog.h" #include #include #include #include #include #include #include +#include +#include #include #if defined KDAB_EVAL #include "../evaldialog/evaldialog.h" #endif /*!\class KGantt::HeaderWidget * \internal */ using namespace KGantt; HeaderWidget::HeaderWidget( GraphicsView* parent ) : QWidget( parent ), m_offset( 0. ) { assert( parent ); // Parent must be set } HeaderWidget::~HeaderWidget() { } void HeaderWidget::scrollTo( int v ) { m_offset = v; // QWidget::scroll() wont work properly for me on Mac //scroll( static_cast( old-v ), 0 ); update(); } void HeaderWidget::paintEvent( QPaintEvent* ev ) { QPainter p( this ); view()->grid()->paintHeader( &p, rect(), ev->rect(), m_offset, this ); } bool HeaderWidget::event( QEvent* event ) { if ( event->type() == QEvent::ToolTip ) { DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() ); if ( grid ) { QHelpEvent *e = static_cast( event ); QDateTime dt = grid->mapFromChart( view()->mapToScene( e->x(), 0 ).x() ).toDateTime(); setToolTip( dt.toString() ); } } return QWidget::event( event ); } void HeaderWidget::contextMenuEvent( QContextMenuEvent* event ) { QMenu contextMenu; DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() ); QAction* actionScaleAuto = nullptr; QAction* actionScaleMonth = nullptr; QAction* actionScaleWeek = nullptr; QAction* actionScaleDay = nullptr; QAction* actionScaleHour = nullptr; QAction* actionZoomIn = nullptr; QAction* actionZoomOut = nullptr; QAction* actionTimeline = nullptr; if ( grid != nullptr ) { QMenu* menuScale = new QMenu( tr( "Scale", "@title:menu" ), &contextMenu ); QActionGroup* scaleGroup = new QActionGroup( &contextMenu ); scaleGroup->setExclusive( true ); actionScaleAuto = new QAction( tr( "Auto", "@item:inmenu Automatic scale" ), menuScale ); actionScaleAuto->setCheckable( true ); actionScaleAuto->setChecked( grid->scale() == DateTimeGrid::ScaleAuto ); actionScaleMonth = new QAction( tr( "Month", "@item:inmenu" ), menuScale ); actionScaleMonth->setCheckable( true ); actionScaleMonth->setChecked( grid->scale() == DateTimeGrid::ScaleMonth ); actionScaleWeek = new QAction( tr( "Week", "@item:inmenu" ), menuScale ); actionScaleWeek->setCheckable( true ); actionScaleWeek->setChecked( grid->scale() == DateTimeGrid::ScaleWeek ); actionScaleDay = new QAction( tr( "Day", "@item:inmenu" ), menuScale ); actionScaleDay->setCheckable( true ); actionScaleDay->setChecked( grid->scale() == DateTimeGrid::ScaleDay ); actionScaleHour = new QAction( tr( "Hour", "@item:inmenu" ), menuScale ); actionScaleHour->setCheckable( true ); actionScaleHour->setChecked( grid->scale() == DateTimeGrid::ScaleHour ); scaleGroup->addAction( actionScaleAuto ); menuScale->addAction( actionScaleAuto ); scaleGroup->addAction( actionScaleMonth ); menuScale->addAction( actionScaleMonth ); scaleGroup->addAction( actionScaleWeek ); menuScale->addAction( actionScaleWeek ); scaleGroup->addAction( actionScaleDay ); menuScale->addAction( actionScaleDay ); scaleGroup->addAction( actionScaleHour ); menuScale->addAction( actionScaleHour ); contextMenu.addMenu( menuScale ); contextMenu.addSeparator(); actionZoomIn = new QAction( tr( "Zoom In", "@action:inmenu" ), &contextMenu ); contextMenu.addAction( actionZoomIn ); actionZoomOut = new QAction( tr( "Zoom Out", "@action:inmenu" ), &contextMenu ); contextMenu.addAction( actionZoomOut ); contextMenu.addSeparator(); actionTimeline = new QAction( tr( "Timeline...", "@action:inmenu" ), &contextMenu ); contextMenu.addAction( actionTimeline ); } if ( contextMenu.isEmpty() ) { event->ignore(); return; } const QAction* const action = contextMenu.exec( event->globalPos() ); if ( action == nullptr ) {} else if ( action == actionScaleAuto ) { assert( grid != nullptr ); grid->setScale( DateTimeGrid::ScaleAuto ); } else if ( action == actionScaleMonth ) { assert( grid != nullptr ); grid->setScale( DateTimeGrid::ScaleMonth ); } else if ( action == actionScaleWeek ) { assert( grid != nullptr ); grid->setScale( DateTimeGrid::ScaleWeek ); } else if ( action == actionScaleDay ) { assert( grid != nullptr ); grid->setScale( DateTimeGrid::ScaleDay ); } else if ( action == actionScaleHour ) { assert( grid != nullptr ); grid->setScale( DateTimeGrid::ScaleHour ); } else if ( action == actionZoomIn ) { assert( grid != nullptr ); grid->setDayWidth( grid->dayWidth() * 1.25 ); } else if ( action == actionZoomOut ) { assert( grid != nullptr ); // daywidth *MUST NOT* go below 1.0, it is used as an integer later on grid->setDayWidth( qMax( 1.0, grid->dayWidth() * 0.8 ) ); } else if ( action == actionTimeline ) { assert( grid != nullptr ); DateTimeTimeLineDialog dlg(grid->timeLine()); dlg.exec(); } event->accept(); } GraphicsView::Private::Private( GraphicsView* _q ) : q( _q ), rowcontroller(nullptr), headerwidget( _q ) { } void GraphicsView::Private::updateHeaderGeometry() { q->setViewportMargins(0,rowcontroller->headerHeight(),0,0); headerwidget.setGeometry( q->viewport()->x(), q->viewport()->y() - rowcontroller->headerHeight(), q->viewport()->width(), rowcontroller->headerHeight() ); } void GraphicsView::Private::slotGridChanged() { updateHeaderGeometry(); headerwidget.update(); q->updateSceneRect(); q->update(); } void GraphicsView::Private::slotHorizontalScrollValueChanged( int val ) { const QRectF viewRect = q->transform().mapRect( q->sceneRect() ); headerwidget.scrollTo( val-q->horizontalScrollBar()->minimum()+static_cast( viewRect.left() ) ); } void GraphicsView::Private::slotColumnsInserted( const QModelIndex& parent, int start, int end ) { Q_UNUSED( start ); Q_UNUSED( end ); QModelIndex idx = scene.model()->index( 0, 0, scene.summaryHandlingModel()->mapToSource( parent ) ); do { scene.updateRow( scene.summaryHandlingModel()->mapFromSource( idx ) ); } while ( ( idx = rowcontroller->indexBelow( idx ) ) != QModelIndex() && rowcontroller->isRowVisible( idx ) ); //} while ( ( idx = d->treeview.indexBelow( idx ) ) != QModelIndex() && d->treeview.visualRect(idx).isValid() ); q->updateSceneRect(); } void GraphicsView::Private::slotColumnsRemoved( const QModelIndex& parent, int start, int end ) { // TODO Q_UNUSED( start ); Q_UNUSED( end ); Q_UNUSED( parent ); q->updateScene(); } void GraphicsView::Private::slotDataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ) { //qDebug() << "GraphicsView::slotDataChanged("<index( row, 0, parent ) ); } } void GraphicsView::Private::slotLayoutChanged() { //qDebug() << "slotLayoutChanged()"; q->updateScene(); } void GraphicsView::Private::slotModelReset() { //qDebug() << "slotModelReset()"; q->updateScene(); } void GraphicsView::Private::slotRowsInserted( const QModelIndex& parent, int start, int end ) { Q_UNUSED( parent ); Q_UNUSED( start ); Q_UNUSED( end ); q->updateScene(); // TODO: This might be optimised } void GraphicsView::Private::removeConstraintsRecursive( QAbstractProxyModel *summaryModel, const QModelIndex& index ) { if ( summaryModel->hasChildren( index ) ) { //qDebug() << "removing constraints from children of"<rowCount( index ); ++row ) { const QModelIndex child = summaryModel->index( row, index.column(), index ); removeConstraintsRecursive( summaryModel, child ); } } //qDebug() << "removing constraints from"<sourceModel() QList clst = scene.constraintModel()->constraintsForIndex( summaryModel->mapToSource( index ) ); Q_FOREACH( Constraint c, clst ) { scene.constraintModel()->removeConstraint( c ); } } void GraphicsView::Private::slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end ) { //qDebug() << "GraphicsView::Private::slotRowsAboutToBeRemoved("<columnCount( parent ); ++col ) { const QModelIndex idx = summaryModel->index( row, col, parent ); removeConstraintsRecursive( summaryModel, idx ); scene.removeItem( idx ); } } } void GraphicsView::Private::slotRowsRemoved( const QModelIndex& parent, int start, int end ) { //qDebug() << "GraphicsView::Private::slotRowsRemoved("<updateScene(); } void GraphicsView::Private::slotItemClicked( const QModelIndex& idx ) { QModelIndex sidx = idx;//scene.summaryHandlingModel()->mapToSource( idx ); emit q->clicked( sidx ); if (q->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, q)) emit q->activated( sidx ); } void GraphicsView::Private::slotItemDoubleClicked( const QModelIndex& idx ) { QModelIndex sidx = idx;//scene.summaryHandlingModel()->mapToSource( idx ); emit q->qrealClicked( sidx ); if (!q->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, q)) emit q->activated( sidx ); } void GraphicsView::Private::slotHeaderContextMenuRequested( const QPoint& pt ) { emit q->headerContextMenuRequested( headerwidget.mapToGlobal( pt ) ); } /*!\class KGantt::GraphicsView kganttgraphicsview.h KGanttGraphicsView * \ingroup KGantt * \brief The GraphicsView class provides a model/view implementation of a gantt chart. * * */ /*! \fn void GraphicsView::activated( const QModelIndex & index ) */ /*! \fn void GraphicsView::clicked( const QModelIndex & index ); */ /*! \fn void GraphicsView::qrealClicked( const QModelIndex & index ); */ /*! \fn void GraphicsView::entered( const QModelIndex & index ); */ /*! \fn void GraphicsView::pressed( const QModelIndex & index ); */ /*! \fn void GraphicsView::headerContextMenuRequested( const QPoint& pt ) * This signal is emitted when the header has contextMenuPolicy Qt::CustomContextMenu * and the widget wants to show a context menu for the header. Unlike in * QWidget::customContextMenuRequested() signal, \a pt is here in global coordinates. */ /*! Constructor. Creates a new KGantt::GraphicsView with parent * \a parent. */ GraphicsView::GraphicsView( QWidget* parent ) : QGraphicsView( parent ), _d( new Private( this ) ) { #if defined KDAB_EVAL EvalDialog::checkEvalLicense( "KD Gantt" ); #endif connect( horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotHorizontalScrollValueChanged(int)) ); connect( &_d->scene, SIGNAL(gridChanged()), this, SLOT(slotGridChanged()) ); connect( &_d->scene, SIGNAL(entered(QModelIndex)), this, SIGNAL(entered(QModelIndex)) ); connect( &_d->scene, SIGNAL(pressed(QModelIndex)), this, SIGNAL(pressed(QModelIndex)) ); connect( &_d->scene, SIGNAL(clicked(QModelIndex)), this, SLOT(slotItemClicked(QModelIndex)) ); connect( &_d->scene, SIGNAL(qrealClicked(QModelIndex)), this, SLOT(slotItemDoubleClicked(QModelIndex)) ); connect( &_d->scene, SIGNAL(sceneRectChanged(QRectF)), this, SLOT(updateSceneRect()) ); connect( &_d->headerwidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotHeaderContextMenuRequested(QPoint)) ); setScene( &_d->scene ); // HACK! setSummaryHandlingModel( _d->scene.summaryHandlingModel() ); // So that AbstractGrid::drawBackground() and AbstractGrid::drawForeground() // works properly setViewportUpdateMode(QGraphicsView::FullViewportUpdate); //setCacheMode( CacheBackground ); } /*! Destroys this view. */ GraphicsView::~GraphicsView() { delete _d; } #define d d_func() /*! Sets the model to be displayed in this view to * \a model. The view does not take ownership of the model. * * To make a model work well with GraphicsView it must * have a certain layout. Whether the model is flat or has a * treestrucure is not important, as long as an * AbstractRowController is provided that can navigate the * model. * * GraphicsView operates per row in the model. The data is always * taken from the _last_ item in the row. The ItemRoles used are * Qt::DisplayRole and the roles defined in KGantt::ItemDataRole. * * Note: This model is not returned by \a model() * * \see GraphicsView::model */ void GraphicsView::setModel( QAbstractItemModel* model ) { if ( d->scene.model() ) { disconnect( d->scene.model() ); } d->scene.setModel( model ); if (model) { connect( model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(updateSceneRect()) ); } updateScene(); } /*! \returns the current model displayed by this view * * Note: The returned model is not the model set with \a setModel() * * \see GraphicsView::setModel */ QAbstractItemModel* GraphicsView::model() const { return d->scene.model(); } void GraphicsView::setSummaryHandlingModel( QAbstractProxyModel* proxyModel ) { disconnect( d->scene.summaryHandlingModel() ); d->scene.setSummaryHandlingModel( proxyModel ); /* Connections. We have to rely on the treeview * to receive the signals before we do(!) */ connect( proxyModel, SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(slotColumnsInserted(QModelIndex,int,int)) ); connect( proxyModel, SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(slotColumnsRemoved(QModelIndex,int,int)) ); connect( proxyModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(slotDataChanged(QModelIndex,QModelIndex)) ); connect( proxyModel, SIGNAL(layoutChanged()), this, SLOT(slotLayoutChanged()) ); connect( proxyModel, SIGNAL(modelReset()), this, SLOT(slotModelReset()) ); connect( proxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(slotRowsInserted(QModelIndex,int,int)) ); connect( proxyModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)) ); connect( proxyModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(slotRowsRemoved(QModelIndex,int,int)) ); updateScene(); } /*! Sets the constraintmodel displayed by this view. * \see KGantt::ConstraintModel. */ void GraphicsView::setConstraintModel( ConstraintModel* cmodel ) { d->scene.setConstraintModel( cmodel ); } /*! \returns the KGantt::ConstraintModel displayed by this view. */ ConstraintModel* GraphicsView::constraintModel() const { return d->scene.constraintModel(); } /*! \returns the KGantt::SummaryHandlingProxyModel used by this view. */ QAbstractProxyModel* GraphicsView::summaryHandlingModel() const { return d->scene.summaryHandlingModel(); } /*! Sets the root index of the model displayed by this view. * Similar to QAbstractItemView::setRootIndex, default is QModelIndex(). */ void GraphicsView::setRootIndex( const QModelIndex& idx ) { d->scene.setRootIndex( idx ); } /*! \returns the rootindex for this view. */ QModelIndex GraphicsView::rootIndex() const { return d->scene.rootIndex(); } /*! Sets the QItemSelectionModel used by this view to manage * selections. Similar to QAbstractItemView::setSelectionModel */ void GraphicsView::setSelectionModel( QItemSelectionModel* model ) { d->scene.setSelectionModel( model ); } /*! \returns the QItemSelectionModel used by this view */ QItemSelectionModel* GraphicsView::selectionModel() const { return d->scene.selectionModel(); } /*! Sets the KGantt::ItemDelegate used for rendering items on this * view. \see ItemDelegate and QAbstractItemDelegate. */ void GraphicsView::setItemDelegate( ItemDelegate* delegate ) { d->scene.setItemDelegate( delegate ); } /*! \returns the ItemDelegate used by this view to render items */ ItemDelegate* GraphicsView::itemDelegate() const { return d->scene.itemDelegate(); } /*! Sets the AbstractRowController used by this view. The * AbstractRowController deals with the height and position * of each row and with which parts of the model are * displayed. \see AbstractRowController */ void GraphicsView::setRowController( AbstractRowController* rowcontroller ) { d->rowcontroller = rowcontroller; d->scene.setRowController( rowcontroller ); updateScene(); } /*! \returns the AbstractRowController * for this view. \see setRowController */ AbstractRowController* GraphicsView::rowController() const { return d->rowcontroller; } /*! Sets the AbstractGrid for this view. The grid is an * object that controls how QModelIndexes are mapped * to and from the view and how the background and header * is rendered. \see AbstractGrid and DateTimeGrid. */ void GraphicsView::setGrid( AbstractGrid* grid ) { d->scene.setGrid( grid ); d->slotGridChanged(); } /*! \returns the AbstractGrid used by this view. */ AbstractGrid* GraphicsView::grid() const { return d->scene.grid(); } /*! Sets the view to read-only mode if \a to is true. The default is * read/write if the model permits it. */ void GraphicsView::setReadOnly( bool ro ) { d->scene.setReadOnly( ro ); } /*!\returns true iff the view is in read-only mode */ bool GraphicsView::isReadOnly() const { return d->scene.isReadOnly(); } /*! Sets the context menu policy for the header. The default value * Qt::DefaultContextMenu results in a standard context menu on the header * that allows the user to set the scale and zoom. * * Setting this to Qt::CustomContextMenu will cause the signal * headerContextMenuRequested(const QPoint& pt) to be emitted instead. * * \see QWidget::setContextMenuPolicy( Qt::ContextMenuPolicy ) */ void GraphicsView::setHeaderContextMenuPolicy( Qt::ContextMenuPolicy p ) { d->headerwidget.setContextMenuPolicy( p ); } /*! \returns the context menu policy for the header */ Qt::ContextMenuPolicy GraphicsView::headerContextMenuPolicy() const { return d->headerwidget.contextMenuPolicy(); } /*! Adds a constraint from \a from to \a to. \a modifiers are the * keyboard modifiers pressed by the user when the action is invoked. * * Override this to control how contraints are added. The default * implementation adds a soft constraint unless the Shift key is pressed, * in that case it adds a hard constraint. If a constraint is already * present, it is removed and nothing is added. */ void GraphicsView::addConstraint( const QModelIndex& from, const QModelIndex& to, Qt::KeyboardModifiers modifiers ) { if ( isReadOnly() ) return; ConstraintModel* cmodel = constraintModel(); assert( cmodel ); Constraint c( from, to, ( modifiers&Qt::ShiftModifier )?Constraint::TypeHard:Constraint::TypeSoft ); if ( cmodel->hasConstraint( c ) ) cmodel->removeConstraint( c ); else cmodel->addConstraint( c ); } void GraphicsView::resizeEvent( QResizeEvent* ev ) { d->updateHeaderGeometry(); QRectF r = scene()->itemsBoundingRect(); // To scroll more to the left than the actual item start, bug #4516 r.setLeft( qMin( 0.0, r.left() ) ); // TODO: take scrollbars into account (if not always on) // The scene should be at least the size of the viewport QSizeF size = viewport()->size(); //TODO: why -2 below? size should be ex. frames etc? if ( size.width() > r.width() ) { r.setWidth( size.width() - 2 ); } if ( size.height() > r.height() ) { r.setHeight( size.height() - 2 ); } const int totalh = rowController()->totalHeight(); if ( r.height() < totalh ) { r.setHeight( totalh ); } scene()->setSceneRect( r ); QGraphicsView::resizeEvent( ev ); } /*!\returns The QModelIndex for the item located at * position \a pos in the view or an invalid index * if no item was present at that position. * * This is useful for for example contextmenus. */ QModelIndex GraphicsView::indexAt( const QPoint& pos ) const { QGraphicsItem* item = itemAt( pos ); if ( GraphicsItem* gitem = qgraphicsitem_cast( item ) ) { return d->scene.summaryHandlingModel()->mapToSource( gitem->index() ); } else { return QModelIndex(); } } /*! \internal */ void GraphicsView::clearItems() { d->scene.clearItems(); } /*! \internal */ void GraphicsView::updateRow( const QModelIndex& idx ) { d->scene.updateRow( d->scene.summaryHandlingModel()->mapFromSource( idx ) ); } /*! \internal * Adjusts the bounding rectangle of the scene. */ void GraphicsView::updateSceneRect() { /* What to do with this? We need to shrink the view to * make collapsing items work */ qreal range = horizontalScrollBar()->maximum()-horizontalScrollBar()->minimum(); const qreal hscroll = horizontalScrollBar()->value()/( range>0?range:1 ); QRectF r = d->scene.itemsBoundingRect(); // To scroll more to the left than the actual item start, bug #4516 r.setTop( 0. ); r.setLeft( qMin( 0.0, r.left() ) ); r.setSize( r.size().expandedTo( viewport()->size() ) ); const int totalh = rowController()->totalHeight(); if ( r.height() < totalh ) r.setHeight( totalh ); d->scene.setSceneRect( r ); /* set scrollbar to keep the same time in view */ range = horizontalScrollBar()->maximum()-horizontalScrollBar()->minimum(); if ( range>0 ) horizontalScrollBar()->setValue( qRound( hscroll*range ) ); /* We have to update here to adjust for any rows with no * information because they are painted with a different * background brush */ d->scene.invalidate( QRectF(), QGraphicsScene::BackgroundLayer ); } /*! \internal * Resets the state of the view. */ void GraphicsView::updateScene() { clearItems(); if ( !model()) return; if ( !rowController()) return; QModelIndex idx = model()->index( 0, 0, rootIndex() ); do { updateRow( idx ); } while ( ( idx = rowController()->indexBelow( idx ) ) != QModelIndex() && rowController()->isRowVisible(idx) ); //constraintModel()->cleanup(); //qDebug() << constraintModel(); updateSceneRect(); if ( scene() ) scene()->invalidate( QRectF(), QGraphicsScene::BackgroundLayer ); } /*! Creates a new GraphicsItem * Re-iplement to create your own flavour of GraphicsItem */ GraphicsItem* GraphicsView::createItem( ItemType type ) const { Q_UNUSED(type) return new GraphicsItem; } /*! \internal */ void GraphicsView::deleteSubtree( const QModelIndex& idx ) { d->scene.deleteSubtree( d->scene.summaryHandlingModel()->mapFromSource( idx ) ); } /*! Print the Gantt chart using \a printer. If \a drawRowLabels * is true (the default), each row will have it's label printed * on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. * * This version of print() will print multiple pages. */ void GraphicsView::print( QPrinter* printer, bool drawRowLabels, bool drawColumnLabels ) { d->scene.print( printer, drawRowLabels, drawColumnLabels ); } /*! Print part of the Gantt chart from \a start to \a end using \a printer. * If \a drawRowLabels is true (the default), each row will have it's * label printed on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. * * This version of print() will print multiple pages. * * To print a certain range of a chart with a DateTimeGrid, use * qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const * to figure out the values for \a start and \a end. */ void GraphicsView::print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels ) { d->scene.print( printer, start, end, drawRowLabels, drawColumnLabels ); } /*! Render the GanttView inside the rectangle \a target using the painter \a painter. * If \a drawRowLabels is true (the default), each row will have it's * label printed on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. */ void GraphicsView::print( QPainter* painter, const QRectF& targetRect, bool drawRowLabels, bool drawColumnLabels ) { d->scene.print(painter, targetRect, drawRowLabels, drawColumnLabels); } /*! Render the GanttView inside the rectangle \a target using the painter \a painter. * If \a drawRowLabels is true (the default), each row will have it's * label printed on the left side. If \a drawColumnLabels is true (the * default), each column will have it's label printed at the * top side. * * To print a certain range of a chart with a DateTimeGrid, use * qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const * to figure out the values for \a start and \a end. */ void GraphicsView::print( QPainter* painter, qreal start, qreal end, const QRectF& targetRect, bool drawRowLabels, bool drawColumnLabels ) { d->scene.print(painter, start, end, targetRect, drawRowLabels, drawColumnLabels); } #include "moc_kganttgraphicsview.cpp"