00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
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
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
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 {
00280 Q_FOREACH( ConstraintGraphicsItem* item, m_startConstraints ) {
00281 QPointF s = startConnector( item->constraint().relationType() );
00282 item->setStart( s );
00283 }}
00284 {
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
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
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
00314 } else if ( align & Qt::AlignBottom ) {
00315 r.setY( rowGeometry.length()-maxh );
00316 } else {
00317
00318 r.setY( ( rowGeometry.length()-maxh ) / 2. );
00319 }
00320 r.setHeight( maxh );
00321 setRect( r );
00322 }
00323
00324
00325
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
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
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
00370
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
00421 StyleOptionGanttItem opt = getStyleOption();
00422 int istate = scene()->itemDelegate()->interactionStateFor( event->pos(), opt, index() );
00423
00424
00425
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:
00436 BASE::mousePressEvent( event );
00437 break;
00438 }
00439 } else {
00440 event->ignore();
00441 }
00442 }
00443
00444 void GraphicsItem::mouseReleaseEvent( QGraphicsSceneMouseEvent* event )
00445 {
00446
00447 if ( !m_presspos.isNull() ) {
00448 scene()->itemClicked( index() );
00449 }
00450 delete m_dragline; m_dragline = 0;
00451 if ( scene()->dragSource() ) {
00452
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
00464 } else {
00465 if ( isEditable() ) {
00466 updateItemFromMouse(event->scenePos());
00467
00468
00469
00470
00471
00472 m_presspos = QPointF();
00473 updateModel();
00474
00475
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
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
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
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
00551 break;
00552 case ItemDelegate::State_DragConstraint: {
00553 QLineF line = m_dragline->line();
00554 m_dragline->setLine( QLineF( line.p1(), event->pos() ) );
00555
00556 break;
00557 }
00558 }
00559 }