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->mapToScene( other->rect() ).boundingRect().contains( event->scenePos() )) {
00442 GraphicsView* view = qobject_cast<GraphicsView*>( event->widget()->parentWidget() );
00443 if ( view ) {
00444 view->addConstraint( scene()->summaryHandlingModel()->mapToSource( scene()->dragSource()->index() ),
00445 scene()->summaryHandlingModel()->mapToSource( other->index() ), event->modifiers() );
00446 }
00447 }
00448 scene()->setDragSource( 0 );
00449
00450 } else {
00451 if ( isEditable() ) {
00452 updateItemFromMouse(event->scenePos());
00453
00454
00455
00456
00457
00458 m_presspos = QPointF();
00459 updateModel();
00460
00461
00462 scene()->update();
00463 }
00464 }
00465
00466 m_presspos = QPointF();
00467 BASE::mouseReleaseEvent( event );
00468 }
00469
00470 void GraphicsItem::mouseDoubleClickEvent( QGraphicsSceneMouseEvent* event )
00471 {
00472 const int typ = static_cast<ItemType>( index().model()->data( index(), ItemTypeRole ).toInt() );
00473 StyleOptionGanttItem opt = getStyleOption();
00474 ItemDelegate::InteractionState istate = scene()->itemDelegate()->interactionStateFor( event->pos(), opt, index() );
00475 if ( (istate != ItemDelegate::State_None) || (typ == TypeSummary)) {
00476 scene()->itemDoubleClicked( index() );
00477 }
00478 BASE::mouseDoubleClickEvent( event );
00479 }
00480
00481 void GraphicsItem::updateItemFromMouse( const QPointF& scenepos )
00482 {
00483
00484 const QPointF p = scenepos - m_presspos;
00485 QRectF r = rect();
00486 QRectF br = boundingRect();
00487 switch( m_istate ) {
00488 case ItemDelegate::State_Move:
00489 setPos( p.x(), pos().y() );
00490 break;
00491 case ItemDelegate::State_ExtendLeft: {
00492 const qreal brr = br.right();
00493 const qreal rr = r.right();
00494 const qreal delta = pos().x()-p.x();
00495 setPos( p.x(), QGraphicsItem::pos().y() );
00496 br.setRight( brr+delta );
00497 r.setRight( rr+delta );
00498 break;
00499 }
00500 case ItemDelegate::State_ExtendRight: {
00501 const qreal rr = r.right();
00502 r.setRight( scenepos.x()-pos().x() );
00503 br.setWidth( br.width() + r.right()-rr );
00504 break;
00505 }
00506 default: return;
00507 }
00508 setRect( r );
00509 setBoundingRect( br );
00510 }
00511
00512 void GraphicsItem::mouseMoveEvent( QGraphicsSceneMouseEvent* event )
00513 {
00514 if ( !isEditable() ) return;
00515 if ( m_presspos.isNull() ) return;
00516
00517
00518 QPointF pos = event->pos() - m_presspos;
00519 switch( m_istate ) {
00520 case ItemDelegate::State_ExtendLeft:
00521 case ItemDelegate::State_ExtendRight:
00522 case ItemDelegate::State_Move:
00523
00524 if ( qAbs( m_pressscenepos.x()-event->scenePos().x() ) < 10.
00525 && qAbs( m_pressscenepos.y()-event->scenePos().y() ) > 5. ) {
00526 m_istate = ItemDelegate::State_DragConstraint;
00527 m_dragline = new QGraphicsLineItem( this );
00528 m_dragline->setPen( QPen( Qt::DashLine ) );
00529 m_dragline->setLine(QLineF( rect().center(), event->pos() ));
00530 scene()->setDragSource( this );
00531 break;
00532 }
00533
00534 scene()->selectionModel()->setCurrentIndex( index(), QItemSelectionModel::Current );
00535 updateItemFromMouse(event->scenePos());
00536
00537 break;
00538 case ItemDelegate::State_DragConstraint: {
00539 QLineF line = m_dragline->line();
00540 m_dragline->setLine( QLineF( line.p1(), event->pos() ) );
00541
00542 break;
00543 }
00544 }
00545 }