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