kdganttgraphicsitem.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002  ** Copyright (C) 2001-2006 Klarälvdalens Datakonsult AB.  All rights reserved.
00003  **
00004  ** This file is part of the KD Gantt library.
00005  **
00006  ** This file may be distributed and/or modified under the terms of the
00007  ** GNU General Public License version 2 as published by the Free Software
00008  ** Foundation and appearing in the file LICENSE.GPL included in the
00009  ** packaging of this file.
00010  **
00011  ** Licensees holding valid commercial KD Gantt licenses may use this file in
00012  ** accordance with the KD Gantt Commercial License Agreement provided with
00013  ** the Software.
00014  **
00015  ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00016  ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00017  **
00018  ** See http://www.kdab.net/kdgantt for
00019  **   information about KD Gantt Commercial License Agreements.
00020  **
00021  ** Contact info@kdab.net if any conditions of this
00022  ** licensing are not clear to you.
00023  **
00024  **********************************************************************/
00025 #include "kdganttgraphicsitem.h"
00026 #include "kdganttgraphicsscene.h"
00027 #include "kdganttgraphicsview.h"
00028 #include "kdganttitemdelegate.h"
00029 #include "kdganttconstraintgraphicsitem.h"
00030 #include "kdganttconstraintmodel.h"
00031 #include "kdganttconstraint.h"
00032 #include "kdganttabstractgrid.h"
00033 #include "kdganttabstractrowcontroller.h"
00034 
00035 #include <cassert>
00036 #include <cmath>
00037 #include <algorithm>
00038 #include <iterator>
00039 
00040 #include <QPainter>
00041 #include <QAbstractItemModel>
00042 #include <QAbstractProxyModel>
00043 #include <QItemSelectionModel>
00044 #include <QGraphicsSceneMouseEvent>
00045 #include <QGraphicsLineItem>
00046 
00047 #include <QDebug>
00048 
00053 using namespace KDGantt;
00054 
00055 typedef QGraphicsItem BASE;
00056 
00057 namespace {
00058     class Updater {
00059         bool *u_ptr;
00060         bool oldval;
00061     public:
00062         Updater( bool* u ) : u_ptr( u ), oldval( *u ) {
00063             *u=true;
00064         }
00065         ~Updater() {
00066             *u_ptr = oldval;
00067         }
00068     };
00069 }
00070 
00071 GraphicsItem::GraphicsItem( QGraphicsItem* parent, GraphicsScene* scene )
00072     : BASE( parent, scene ),  m_isupdating( false )
00073 {
00074   init();
00075 }
00076 
00077 GraphicsItem::GraphicsItem( const QModelIndex& idx, QGraphicsItem* parent,
00078                                             GraphicsScene* scene )
00079     : BASE( parent, scene ),  m_index( idx ), m_isupdating( false )
00080 {
00081   init();
00082 }
00083 
00084 GraphicsItem::~GraphicsItem()
00085 {
00086 }
00087 
00088 void GraphicsItem::init()
00089 {
00090 #if QT_VERSION >= QT_VERSION_CHECK(4,4,0)
00091     setCacheMode( QGraphicsItem::DeviceCoordinateCache );
00092 #endif
00093     setFlags( ItemIsMovable|ItemIsSelectable|ItemIsFocusable );
00094     setAcceptsHoverEvents( true );
00095     setHandlesChildEvents( true );
00096     setZValue( 100. );
00097     m_dragline = 0;
00098 }
00099 
00100 int GraphicsItem::type() const
00101 {
00102     return Type;
00103 }
00104 
00105 StyleOptionGanttItem GraphicsItem::getStyleOption() const
00106 {
00107     StyleOptionGanttItem opt;
00108     opt.itemRect = rect();
00109     opt.boundingRect = boundingRect();
00110     QVariant tp = m_index.model()->data( m_index, TextPositionRole );
00111     if(tp.isValid()) {
00112         opt.displayPosition = static_cast<StyleOptionGanttItem::Position>(tp.toInt());
00113     } else {
00114 #if 0
00115         qDebug() << "Item" << m_index.model()->data( m_index, Qt::DisplayRole ).toString()
00116                  << ", ends="<<m_endConstraints.size() << ", starts="<<m_startConstraints.size();
00117 #endif
00118         opt.displayPosition = m_endConstraints.size()<m_startConstraints.size()?StyleOptionGanttItem::Left:StyleOptionGanttItem::Right;
00119 #if 0
00120         qDebug() << "choosing" << opt.displayPosition;
00121 #endif
00122     }
00123     QVariant da = m_index.model()->data( m_index, Qt::TextAlignmentRole );
00124     if ( da.isValid() ) {
00125         opt.displayAlignment = static_cast< Qt::Alignment >( da.toInt() );
00126     } else {
00127         switch( opt.displayPosition ) {
00128         case StyleOptionGanttItem::Left: opt.displayAlignment = Qt::AlignLeft|Qt::AlignVCenter; break;
00129         case StyleOptionGanttItem::Right: opt.displayAlignment = Qt::AlignRight|Qt::AlignVCenter; break;
00130         case StyleOptionGanttItem::Center: opt.displayAlignment = Qt::AlignCenter; break;
00131         }
00132     }
00133     opt.grid = scene()->grid();
00134     opt.text = m_index.model()->data( m_index, Qt::DisplayRole ).toString();
00135     if ( isEnabled() ) opt.state  |= QStyle::State_Enabled;
00136     if ( isSelected() ) opt.state |= QStyle::State_Selected;
00137     if ( hasFocus() ) opt.state   |= QStyle::State_HasFocus;
00138     return opt;
00139 }
00140 
00141 GraphicsScene* GraphicsItem::scene() const
00142 {
00143     return qobject_cast<GraphicsScene*>( QGraphicsItem::scene() );
00144 }
00145 
00146 void GraphicsItem::setRect( const QRectF& r )
00147 {
00148 #if 0
00149     qDebug() << "GraphicsItem::setRect("<<r<<"), txt="<<m_index.model()->data( m_index, Qt::DisplayRole ).toString();
00150     if ( m_index.model()->data( m_index, Qt::DisplayRole ).toString() == QLatin1String("Code Freeze" ) ) {
00151         qDebug() << "gotcha";
00152     }
00153 #endif
00154 
00155     prepareGeometryChange();
00156     m_rect = r;
00157     updateConstraintItems();
00158     update();
00159 }
00160 
00161 void GraphicsItem::setBoundingRect( const QRectF& r )
00162 {
00163     prepareGeometryChange();
00164     m_boundingrect = r;
00165     update();
00166 }
00167 
00168 bool GraphicsItem::isEditable() const
00169 {
00170     return !scene()->isReadOnly() && m_index.model()->flags( m_index ) & Qt::ItemIsEditable;
00171 }
00172 
00173 void GraphicsItem::paint( QPainter* painter, const QStyleOptionGraphicsItem* option,
00174                                   QWidget* widget )
00175 {
00176     Q_UNUSED( widget );
00177     if ( boundingRect().isValid() && scene() ) {
00178         StyleOptionGanttItem opt = getStyleOption();
00179         *static_cast<QStyleOption*>(&opt) = *static_cast<const QStyleOption*>( option );
00180         //opt.fontMetrics = painter->fontMetrics();
00181         scene()->itemDelegate()->paintGanttItem( painter, opt, index() );
00182     }
00183 }
00184 
00185 void GraphicsItem::setIndex( const QPersistentModelIndex& idx )
00186 {
00187     m_index=idx;
00188     update();
00189 }
00190 
00191 QString GraphicsItem::ganttToolTip() const
00192 {
00193     // TODO: Make delegate handle this
00194     const QAbstractItemModel* model = index().model();
00195     if ( !model ) return QString();
00196 #if 0
00197     QString dbgstr;
00198     QDebug( &dbgstr ) << m_index;
00199     return dbgstr;
00200 #endif
00201     QString tip = model->data( index(), Qt::ToolTipRole ).toString();
00202     if ( !tip.isNull() ) return tip;
00203     else return GraphicsScene::tr( "%1 -> %2: %3" )
00204         .arg( model->data( index(), StartTimeRole ).toString() )
00205         .arg( model->data( index(), EndTimeRole ).toString() )
00206         .arg( model->data( index(), Qt::DisplayRole ).toString() );
00207 }
00208 
00209 QRectF GraphicsItem::boundingRect() const
00210 {
00211     return m_boundingrect;
00212 }
00213 
00214 QPointF GraphicsItem::startConnector( int relationType ) const
00215 {
00216     switch ( relationType ) {
00217         case Constraint::StartStart:
00218         case Constraint::StartFinish:
00219             return mapToScene( m_rect.left(), m_rect.top()+m_rect.height()/2. );
00220         default:
00221             break;
00222     }
00223     return mapToScene( m_rect.right(), m_rect.top()+m_rect.height()/2. );
00224 }
00225 
00226 QPointF GraphicsItem::endConnector( int relationType ) const
00227 {
00228     switch ( relationType ) {
00229         case Constraint::FinishFinish:
00230         case Constraint::StartFinish:
00231             return mapToScene( m_rect.right(), m_rect.top()+m_rect.height()/2. );
00232         default:
00233             break;
00234     }
00235     return mapToScene( m_rect.left(), m_rect.top()+m_rect.height()/2. );
00236 }
00237 
00238 
00239 void GraphicsItem::constraintsChanged()
00240 {
00241     if ( !scene() || !scene()->itemDelegate() ) return;
00242     const Span bs = scene()->itemDelegate()->itemBoundingSpan( getStyleOption(), index() );
00243     const QRectF br = boundingRect();
00244     setBoundingRect( QRectF( bs.start(), 0., bs.length(), br.height() ) );
00245 }
00246 
00247 void GraphicsItem::addStartConstraint( ConstraintGraphicsItem* item )
00248 {
00249     assert( item );
00250     m_startConstraints << item;
00251     item->setStart( startConnector( item->constraint().relationType() ) );
00252     constraintsChanged();
00253 }
00254 
00255 void GraphicsItem::addEndConstraint( ConstraintGraphicsItem* item )
00256 {
00257     assert( item );
00258     m_endConstraints << item;
00259     item->setEnd( endConnector( item->constraint().relationType() ) );
00260     constraintsChanged();
00261 }
00262 
00263 void GraphicsItem::removeStartConstraint( ConstraintGraphicsItem* item )
00264 {
00265     assert( item );
00266     m_startConstraints.removeAll( item );
00267     constraintsChanged();
00268 }
00269 
00270 void GraphicsItem::removeEndConstraint( ConstraintGraphicsItem* item )
00271 {
00272     assert( item );
00273     m_endConstraints.removeAll( item );
00274     constraintsChanged();
00275 }
00276 
00277 void GraphicsItem::updateConstraintItems()
00278 {
00279     { // Workaround for multiple definition error with MSVC6
00280     Q_FOREACH( ConstraintGraphicsItem* item, m_startConstraints ) {
00281         QPointF s = startConnector( item->constraint().relationType() );
00282         item->setStart( s );
00283     }}
00284     {// Workaround for multiple definition error with MSVC6
00285     Q_FOREACH( ConstraintGraphicsItem* item, m_endConstraints ) {
00286         QPointF e = endConnector( item->constraint().relationType() );
00287         item->setEnd( e );
00288     }}
00289 }
00290 
00291 void GraphicsItem::updateItem( const Span& rowGeometry, const QPersistentModelIndex& idx )
00292 {
00293     //qDebug() << "GraphicsItem::updateItem("<<rowGeometry<<idx<<")";
00294     Updater updater( &m_isupdating );
00295     if ( !idx.isValid() || idx.data( ItemTypeRole )==TypeMulti ) {
00296         setRect( QRectF() );
00297         hide();
00298         return;
00299     }
00300 
00301     const Span s = scene()->grid()->mapToChart( idx );
00302     setPos( QPointF( s.start(), rowGeometry.start() ) );
00303     setRect( QRectF( 0., 0., s.length(), rowGeometry.length() ) );
00304     setIndex( idx );
00305     const Span bs = scene()->itemDelegate()->itemBoundingSpan( getStyleOption(), index() );
00306     //qDebug() << "boundingSpan for" << getStyleOption().text << rect() << "is" << bs;
00307     setBoundingRect( QRectF( bs.start(), 0., bs.length(), rowGeometry.length() ) );
00308     const int maxh = scene()->rowController()->maximumItemHeight();
00309     if ( maxh < rowGeometry.length() ) {
00310         QRectF r = rect();
00311         const Qt::Alignment align = getStyleOption().displayAlignment;
00312         if ( align & Qt::AlignTop ) {
00313             // Do nothing
00314         } else if ( align & Qt::AlignBottom ) {
00315             r.setY( rowGeometry.length()-maxh );
00316         } else {
00317             // Center
00318             r.setY( ( rowGeometry.length()-maxh ) / 2. );
00319         }
00320         r.setHeight( maxh );
00321         setRect( r );
00322     }
00323 
00324     //scene()->setSceneRect( scene()->sceneRect().united( mapToScene( boundingRect() ).boundingRect() ) );
00325     //updateConstraintItems();
00326 }
00327 
00328 QVariant GraphicsItem::itemChange( GraphicsItemChange change, const QVariant& value )
00329 {
00330     if ( !isUpdating() && change==ItemPositionChange && scene() ) {
00331         QPointF newPos=value.toPointF();
00332         if ( isEditable() ) {
00333             newPos.setY( pos().y() );
00334             return newPos;
00335         } else {
00336             return pos();
00337         }
00338     } else if ( change==QGraphicsItem::ItemSelectedChange ) {
00339         if ( index().isValid() && !( index().model()->flags( index() ) & Qt::ItemIsSelectable ) ) {
00340             // Reject selection attempt
00341             return qVariantFromValue( false );
00342         }
00343 
00344         if ( value.toBool() ) {
00345             scene()->selectionModel()->select( index(), QItemSelectionModel::Select );
00346         } else {
00347             scene()->selectionModel()->select( index(), QItemSelectionModel::Deselect );
00348         }
00349     }
00350 
00351     return QGraphicsItem::itemChange( change, value );
00352 }
00353 
00354 void GraphicsItem::focusInEvent( QFocusEvent* event )
00355 {
00356     Q_UNUSED( event );
00357     scene()->selectionModel()->select( index(), QItemSelectionModel::SelectCurrent );
00358 }
00359 
00360 void GraphicsItem::updateModel()
00361 {
00362     //qDebug() << "GraphicsItem::updateModel()";
00363     if ( isEditable() ) {
00364         QAbstractItemModel* model = const_cast<QAbstractItemModel*>( index().model() );
00365         ConstraintModel* cmodel = scene()->constraintModel();
00366         assert( model );
00367         assert( cmodel );
00368         if ( model ) {
00369             //ItemType typ = static_cast<ItemType>( model->data( index(),
00370             //                                                   ItemTypeRole ).toInt() );
00371             const QModelIndex sourceIdx = scene()->summaryHandlingModel()->mapToSource( index() );
00372             QList<Constraint> constraints;
00373             for( QList<ConstraintGraphicsItem*>::iterator it1 = m_startConstraints.begin() ;
00374                  it1 != m_startConstraints.end() ;
00375                  ++it1 )
00376                 constraints.push_back((*it1)->proxyConstraint());
00377             for( QList<ConstraintGraphicsItem*>::iterator it2 = m_endConstraints.begin() ;
00378                  it2 != m_endConstraints.end() ;
00379                  ++it2 )
00380                 constraints.push_back((*it2)->proxyConstraint());
00381             if ( scene()->grid()->mapFromChart( Span( scenePos().x(), rect().width() ),
00382                                                 index(),
00383                                                 constraints ) ) {
00384                 scene()->updateRow( index().parent() );
00385             }
00386         }
00387     }
00388 }
00389 
00390 void GraphicsItem::hoverMoveEvent( QGraphicsSceneHoverEvent* event )
00391 {
00392     if (  !isEditable() ) return;
00393     StyleOptionGanttItem opt = getStyleOption();
00394     ItemDelegate::InteractionState istate = scene()->itemDelegate()->interactionStateFor( event->pos(), opt, index() );
00395     switch( istate ) {
00396     case ItemDelegate::State_ExtendLeft:
00397         setCursor( Qt::SizeHorCursor );
00398         scene()->itemEntered( index() );
00399         break;
00400     case ItemDelegate::State_ExtendRight:
00401         setCursor( Qt::SizeHorCursor );
00402         scene()->itemEntered( index() );
00403         break;
00404     case ItemDelegate::State_Move:
00405         setCursor( Qt::SplitHCursor );
00406         scene()->itemEntered( index() );
00407         break;
00408     default:
00409         unsetCursor();
00410     };
00411 }
00412 
00413 void GraphicsItem::hoverLeaveEvent( QGraphicsSceneHoverEvent* )
00414 {
00415     unsetCursor();
00416 }
00417 
00418 void GraphicsItem::mousePressEvent( QGraphicsSceneMouseEvent* event )
00419 {
00420     //qDebug() << "GraphicsItem::mousePressEvent("<<event<<")";
00421     StyleOptionGanttItem opt = getStyleOption();
00422     int istate = scene()->itemDelegate()->interactionStateFor( event->pos(), opt, index() );
00423     // If State_None is returned by interactionStateFor(), we ignore this event so that
00424     // it can get forwarded to another item that's below this one. Needed, for example,
00425     // to allow items to be moved that are placed below the label of another item.
00426     if ( istate != ItemDelegate::State_None ) {
00427         m_istate = istate;
00428         m_presspos = event->pos();
00429         m_pressscenepos = event->scenePos();
00430         scene()->itemPressed( index() );
00431 
00432         switch( m_istate ) {
00433         case ItemDelegate::State_ExtendLeft:
00434         case ItemDelegate::State_ExtendRight:
00435         default: /* State_Move */
00436             BASE::mousePressEvent( event );
00437             break;
00438         }
00439     } else {
00440         event->ignore();
00441     }
00442 }
00443 
00444 void GraphicsItem::mouseReleaseEvent( QGraphicsSceneMouseEvent* event )
00445 {
00446     //qDebug() << "GraphicsItem::mouseReleaseEvent("<<event << ")";
00447     if ( !m_presspos.isNull() ) {
00448         scene()->itemClicked( index() );
00449     }
00450     delete m_dragline; m_dragline = 0;
00451     if ( scene()->dragSource() ) {
00452         // Create a new constraint
00453         GraphicsItem* other = qgraphicsitem_cast<GraphicsItem*>( scene()->itemAt( event->scenePos() ) );
00454         if ( other && scene()->dragSource()!=other &&
00455              other->mapToScene( other->rect() ).boundingRect().contains( event->scenePos() )) {
00456             GraphicsView* view = qobject_cast<GraphicsView*>( event->widget()->parentWidget() );
00457             if ( view ) {
00458                 view->addConstraint( scene()->summaryHandlingModel()->mapToSource( scene()->dragSource()->index() ),
00459                                      scene()->summaryHandlingModel()->mapToSource( other->index() ), event->modifiers() );
00460             }
00461         }
00462         scene()->setDragSource( 0 );
00463         //scene()->update();
00464     } else {
00465         if ( isEditable() ) {
00466             updateItemFromMouse(event->scenePos());
00467 
00468             // It is important to set m_presspos to null here because
00469             // when the sceneRect updates because we move the item
00470             // a MouseMoveEvent will be delivered, and we have to
00471             // protect against that
00472             m_presspos = QPointF();
00473             updateModel();
00474             // without this command we sometimes get a white area at the left side of a task item
00475             // after we moved that item right-ways into a grey weekend section of the scene:
00476             scene()->update();
00477         }
00478     }
00479 
00480     m_presspos = QPointF();
00481     BASE::mouseReleaseEvent( event );
00482 }
00483 
00484 void GraphicsItem::mouseDoubleClickEvent( QGraphicsSceneMouseEvent* event )
00485 {
00486     StyleOptionGanttItem opt = getStyleOption();
00487     ItemDelegate::InteractionState istate = scene()->itemDelegate()->interactionStateFor( event->pos(), opt, index() );
00488     if ( istate != ItemDelegate::State_None ) {
00489         scene()->itemDoubleClicked( index() );
00490     }
00491     BASE::mouseDoubleClickEvent( event );
00492 }
00493 
00494 void GraphicsItem::updateItemFromMouse( const QPointF& scenepos )
00495 {
00496     //qDebug() << "GraphicsItem::updateItemFromMouse("<<scenepos<<")";
00497     const QPointF p = scenepos - m_presspos;
00498     QRectF r = rect();
00499     QRectF br = boundingRect();
00500     switch( m_istate ) {
00501     case ItemDelegate::State_Move:
00502         setPos( p.x(), pos().y() );
00503         break;
00504     case ItemDelegate::State_ExtendLeft: {
00505         const qreal brr = br.right();
00506         const qreal rr = r.right();
00507         const qreal delta = pos().x()-p.x();
00508         setPos( p.x(), QGraphicsItem::pos().y() );
00509         br.setRight( brr+delta );
00510         r.setRight( rr+delta );
00511         break;
00512     }
00513     case ItemDelegate::State_ExtendRight: {
00514         const qreal rr = r.right();
00515         r.setRight( scenepos.x()-pos().x() );
00516         br.setWidth( br.width() + r.right()-rr );
00517         break;
00518     }
00519     default: return;
00520     }
00521     setRect( r );
00522     setBoundingRect( br );
00523 }
00524 
00525 void GraphicsItem::mouseMoveEvent( QGraphicsSceneMouseEvent* event )
00526 {
00527     if (  !isEditable() ) return;
00528     if ( m_presspos.isNull() ) return;
00529 
00530     //qDebug() << "GraphicsItem::mouseMoveEvent("<<event<<"), m_istate="<< static_cast<ItemDelegate::InteractionState>( m_istate );
00531     QPointF pos = event->pos() - m_presspos;
00532     switch( m_istate ) {
00533     case ItemDelegate::State_ExtendLeft:
00534     case ItemDelegate::State_ExtendRight:
00535     case ItemDelegate::State_Move:
00536         // Check for constraint drag
00537       if ( qAbs( m_pressscenepos.x()-event->scenePos().x() ) < 10.
00538            && qAbs( m_pressscenepos.y()-event->scenePos().y() ) > 5. ) {
00539             m_istate = ItemDelegate::State_DragConstraint;
00540             m_dragline = new QGraphicsLineItem( this );
00541             m_dragline->setPen( QPen( Qt::DashLine ) );
00542             m_dragline->setLine(QLineF( rect().center(), event->pos() ));
00543             scene()->addItem( m_dragline );
00544             scene()->setDragSource( this );
00545             break;
00546         }
00547 
00548         scene()->selectionModel()->setCurrentIndex( index(), QItemSelectionModel::Current );
00549         updateItemFromMouse(event->scenePos());
00550         //BASE::mouseMoveEvent(event);
00551         break;
00552     case ItemDelegate::State_DragConstraint: {
00553         QLineF line = m_dragline->line();
00554         m_dragline->setLine( QLineF( line.p1(), event->pos() ) );
00555         //QGraphicsItem* item = scene()->itemAt( event->scenePos() );
00556         break;
00557     }
00558     }
00559 }

Generated on Thu Mar 4 23:19:13 2010 for KD Chart 2 by  doxygen 1.5.4