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 setFlags( ItemIsMovable|ItemIsSelectable|ItemIsFocusable );
00089 setAcceptsHoverEvents( true );
00090 setHandlesChildEvents( true );
00091 setZValue( 100. );
00092 m_dragline = 0;
00093 }
00094
00095 int GraphicsItem::type() const
00096 {
00097 return Type;
00098 }
00099
00100 StyleOptionGanttItem GraphicsItem::getStyleOption() const
00101 {
00102 StyleOptionGanttItem opt;
00103 opt.itemRect = rect();
00104 opt.boundingRect = boundingRect();
00105 opt.displayPosition = StyleOptionGanttItem::Right;
00106 opt.displayAlignment = static_cast< Qt::Alignment >( m_index.model()->data( m_index, Qt::TextAlignmentRole ).toInt() );
00107 opt.grid = scene()->grid();
00108 opt.text = m_index.model()->data( m_index, Qt::DisplayRole ).toString();
00109 if ( isEnabled() ) opt.state |= QStyle::State_Enabled;
00110 if ( isSelected() ) opt.state |= QStyle::State_Selected;
00111 if ( hasFocus() ) opt.state |= QStyle::State_HasFocus;
00112 return opt;
00113 }
00114
00115 GraphicsScene* GraphicsItem::scene() const
00116 {
00117 return qobject_cast<GraphicsScene*>( QGraphicsItem::scene() );
00118 }
00119
00120 void GraphicsItem::setRect( const QRectF& r )
00121 {
00122 prepareGeometryChange();
00123 m_rect = r;
00124 updateConstraintItems();
00125 update();
00126 }
00127
00128 void GraphicsItem::setBoundingRect( const QRectF& r )
00129 {
00130 prepareGeometryChange();
00131 m_boundingrect = r;
00132 update();
00133 }
00134
00135 bool GraphicsItem::isEditable() const
00136 {
00137 return !scene()->isReadOnly() && m_index.model()->flags( m_index ) & Qt::ItemIsEditable;
00138 }
00139
00140 void GraphicsItem::paint( QPainter* painter, const QStyleOptionGraphicsItem* option,
00141 QWidget* widget )
00142 {
00143 Q_UNUSED( widget );
00144 if ( boundingRect().isValid() && scene() ) {
00145 StyleOptionGanttItem opt = getStyleOption();
00146 *static_cast<QStyleOption*>(&opt) = *static_cast<const QStyleOption*>( option );
00147 scene()->itemDelegate()->paintGanttItem( painter, opt, index() );
00148 }
00149 }
00150
00151 void GraphicsItem::setIndex( const QPersistentModelIndex& idx )
00152 {
00153 m_index=idx;
00154 update();
00155 }
00156
00157 QString GraphicsItem::ganttToolTip() const
00158 {
00159
00160 const QAbstractItemModel* model = index().model();
00161 if ( !model ) return QString();
00162 QString tip = model->data( index(), Qt::ToolTipRole ).toString();
00163 if ( !tip.isNull() ) return tip;
00164 else return GraphicsScene::tr( "%1 -> %2: %3" )
00165 .arg( model->data( index(), StartTimeRole ).toString() )
00166 .arg( model->data( index(), EndTimeRole ).toString() )
00167 .arg( model->data( index(), Qt::DisplayRole ).toString() );
00168 }
00169
00170 QRectF GraphicsItem::boundingRect() const
00171 {
00172 return m_boundingrect;
00173 }
00174
00175 QPointF GraphicsItem::startConnector( int relationType ) const
00176 {
00177 switch ( relationType ) {
00178 case Constraint::StartStart:
00179 case Constraint::StartFinish:
00180 return mapToScene( m_rect.left(), m_rect.top()+m_rect.height()/2. );
00181 default:
00182 break;
00183 }
00184 return mapToScene( m_rect.right(), m_rect.top()+m_rect.height()/2. );
00185 }
00186
00187 QPointF GraphicsItem::endConnector( int relationType ) const
00188 {
00189 switch ( relationType ) {
00190 case Constraint::FinishFinish:
00191 case Constraint::StartFinish:
00192 return mapToScene( m_rect.right(), m_rect.top()+m_rect.height()/2. );
00193 default:
00194 break;
00195 }
00196 return mapToScene( m_rect.left(), m_rect.top()+m_rect.height()/2. );
00197 }
00198
00199
00200 void GraphicsItem::addStartConstraint( ConstraintGraphicsItem* item )
00201 {
00202 assert( item );
00203 m_startConstraints << item;
00204 item->setStart( startConnector( item->constraint().relationType() ) );
00205 }
00206
00207 void GraphicsItem::addEndConstraint( ConstraintGraphicsItem* item )
00208 {
00209 assert( item );
00210 m_endConstraints << item;
00211 item->setEnd( endConnector( item->constraint().relationType() ) );
00212 }
00213
00214 void GraphicsItem::removeStartConstraint( ConstraintGraphicsItem* item )
00215 {
00216 assert( item );
00217 m_startConstraints.removeAll( item );
00218 }
00219
00220 void GraphicsItem::removeEndConstraint( ConstraintGraphicsItem* item )
00221 {
00222 assert( item );
00223 m_endConstraints.removeAll( item );
00224 }
00225
00226 void GraphicsItem::updateConstraintItems()
00227 {
00228 {
00229 Q_FOREACH( ConstraintGraphicsItem* item, m_startConstraints ) {
00230 QPointF s = startConnector( item->constraint().relationType() );
00231 item->setStart( s );
00232 }}
00233 {
00234 Q_FOREACH( ConstraintGraphicsItem* item, m_endConstraints ) {
00235 QPointF e = endConnector( item->constraint().relationType() );
00236 item->setEnd( e );
00237 }}
00238 }
00239
00240 void GraphicsItem::updateItem( const Span& rowGeometry, const QPersistentModelIndex& idx )
00241 {
00242 Updater updater( &m_isupdating );
00243
00244 if ( !idx.isValid() || idx.data( ItemTypeRole )==TypeMulti ) {
00245 setRect( QRectF() );
00246 hide();
00247 return;
00248 }
00249
00250 Span s = scene()->grid()->mapToChart( idx );
00251 setPos( QPointF( s.start(), rowGeometry.start() ) );
00252 setRect( QRectF( 0., 0., s.length(), rowGeometry.length() ) );
00253 setIndex( idx );
00254 Span bs = scene()->itemDelegate()->itemBoundingSpan( getStyleOption(), index() );
00255 setBoundingRect( QRectF( bs.start(), 0., bs.length(), rowGeometry.length() ) );
00256 int maxh = scene()->rowController()->maximumItemHeight();
00257 if ( maxh < rowGeometry.length() ) {
00258 QRectF r = rect();
00259 Qt::Alignment align = getStyleOption().displayAlignment;
00260 if ( align & Qt::AlignTop ) {
00261
00262 } else if ( align & Qt::AlignBottom ) {
00263 r.setY( rowGeometry.length()-maxh );
00264 } else {
00265
00266 r.setY( ( rowGeometry.length()-maxh ) / 2. );
00267 }
00268 r.setHeight( maxh );
00269 setRect( r );
00270 }
00271
00272 scene()->setSceneRect( scene()->sceneRect().united( mapToScene( boundingRect() ).boundingRect() ) );
00273 updateConstraintItems();
00274 }
00275
00276 QVariant GraphicsItem::itemChange( GraphicsItemChange change, const QVariant& value )
00277 {
00278 if ( !isUpdating() && change==ItemPositionChange && scene() ) {
00279 QPointF newPos=value.toPointF();
00280 if ( isEditable() ) {
00281 newPos.setY( pos().y() );
00282 return newPos;
00283 } else {
00284 return pos();
00285 }
00286 } else if ( change==QGraphicsItem::ItemSelectedChange ) {
00287 if ( value.toBool() ) {
00288 scene()->selectionModel()->select( index(), QItemSelectionModel::Select );
00289 } else {
00290 scene()->selectionModel()->select( index(), QItemSelectionModel::Deselect );
00291 }
00292 }
00293
00294 return QGraphicsItem::itemChange( change, value );
00295 }
00296
00297 void GraphicsItem::focusInEvent( QFocusEvent* event )
00298 {
00299 Q_UNUSED( event );
00300 scene()->selectionModel()->select( index(), QItemSelectionModel::SelectCurrent );
00301 }
00302
00303 void GraphicsItem::updateModel()
00304 {
00305
00306 if ( isEditable() ) {
00307 QAbstractItemModel* model = const_cast<QAbstractItemModel*>( index().model() );
00308 ConstraintModel* cmodel = scene()->constraintModel();
00309 assert( model );
00310 assert( cmodel );
00311 if ( model ) {
00312
00313
00314 const QModelIndex sourceIdx = scene()->summaryHandlingModel()->mapToSource( index() );
00315 QList<Constraint> constraints;
00316 for( QList<ConstraintGraphicsItem*>::iterator it1 = m_startConstraints.begin() ; it1 != m_startConstraints.end() ; ++it1 )
00317 constraints.push_back((*it1)->proxyConstraint());
00318 for( QList<ConstraintGraphicsItem*>::iterator it2 = m_endConstraints.begin() ; it2 != m_endConstraints.end() ; ++it2 )
00319 constraints.push_back((*it2)->proxyConstraint());
00320 if ( scene()->grid()->mapFromChart( Span( pos().x(), rect().width() ),
00321 index(),
00322 constraints ) ) {
00323 scene()->updateRow( index().parent() );
00324 }
00325 }
00326 }
00327 }
00328
00329 void GraphicsItem::hoverMoveEvent( QGraphicsSceneHoverEvent* event )
00330 {
00331 if ( !isEditable() ) return;
00332 StyleOptionGanttItem opt = getStyleOption();
00333 ItemDelegate::InteractionState istate = scene()->itemDelegate()->interactionStateFor( event->pos(), opt, index() );
00334 switch( istate ) {
00335 case ItemDelegate::State_ExtendLeft:
00336 setCursor( Qt::SizeHorCursor );
00337 scene()->itemEntered( index() );
00338 break;
00339 case ItemDelegate::State_ExtendRight:
00340 setCursor( Qt::SizeHorCursor );
00341 scene()->itemEntered( index() );
00342 break;
00343 case ItemDelegate::State_Move:
00344 setCursor( Qt::SplitHCursor );
00345 scene()->itemEntered( index() );
00346 break;
00347 default:
00348 unsetCursor();
00349 };
00350 }
00351
00352 void GraphicsItem::hoverLeaveEvent( QGraphicsSceneHoverEvent* )
00353 {
00354 unsetCursor();
00355 }
00356
00357 void GraphicsItem::mousePressEvent( QGraphicsSceneMouseEvent* event )
00358 {
00359 StyleOptionGanttItem opt = getStyleOption();
00360 m_istate = scene()->itemDelegate()->interactionStateFor( event->pos(), opt, index() );
00361 m_presspos = event->pos();
00362 m_pressscenepos = event->scenePos();
00363 scene()->itemPressed( index() );
00364
00365 switch( m_istate ) {
00366 case ItemDelegate::State_ExtendLeft:
00367 case ItemDelegate::State_ExtendRight:
00368 default:
00369 BASE::mousePressEvent( event );
00370 break;
00371 }
00372 }
00373
00374 void GraphicsItem::mouseReleaseEvent( QGraphicsSceneMouseEvent* event )
00375 {
00376 if ( !m_presspos.isNull() ) {
00377 scene()->itemClicked( index() );
00378 }
00379 delete m_dragline; m_dragline = 0;
00380 if ( scene()->dragSource() ) {
00381
00382 GraphicsItem* other = qgraphicsitem_cast<GraphicsItem*>( scene()->itemAt( event->scenePos() ) );
00383 if ( other && scene()->dragSource()!=other &&
00384 other->mapToScene( other->rect() ).boundingRect().contains( event->scenePos() )) {
00385 GraphicsView* view = qobject_cast<GraphicsView*>( event->widget()->parentWidget() );
00386 if ( view ) {
00387 view->addConstraint( scene()->summaryHandlingModel()->mapToSource( scene()->dragSource()->index() ),
00388 scene()->summaryHandlingModel()->mapToSource( other->index() ), event->modifiers() );
00389 }
00390 }
00391 scene()->setDragSource( 0 );
00392 } else {
00393 updateItemFromMouse(event->scenePos());
00394 updateModel();
00395 }
00396
00397 m_presspos = QPointF();
00398 BASE::mouseReleaseEvent( event );
00399 }
00400
00401 void GraphicsItem::mouseDoubleClickEvent( QGraphicsSceneMouseEvent* event )
00402 {
00403 StyleOptionGanttItem opt = getStyleOption();
00404 ItemDelegate::InteractionState istate = scene()->itemDelegate()->interactionStateFor( event->pos(), opt, index() );
00405 if ( istate != ItemDelegate::State_None ) {
00406 scene()->itemDoubleClicked( index() );
00407 }
00408 BASE::mouseDoubleClickEvent( event );
00409 }
00410
00411 void GraphicsItem::updateItemFromMouse( const QPointF& scenepos )
00412 {
00413 const QPointF p = scenepos - m_presspos;
00414 QRectF r = rect();
00415 QRectF br = boundingRect();
00416 switch( m_istate ) {
00417 case ItemDelegate::State_Move:
00418 setPos( p.x(), pos().y() );
00419 break;
00420 case ItemDelegate::State_ExtendLeft: {
00421 const qreal brr = br.right();
00422 const qreal rr = r.right();
00423 const qreal delta = pos().x()-p.x();
00424 setPos( p.x(), QGraphicsItem::pos().y() );
00425 br.setRight( brr+delta );
00426 r.setRight( rr+delta );
00427 break;
00428 }
00429 case ItemDelegate::State_ExtendRight: {
00430 const qreal rr = r.right();
00431 r.setRight( scenepos.x()-pos().x() );
00432 br.setWidth( br.width() + r.right()-rr );
00433 break;
00434 }
00435 default: return;
00436 }
00437 setRect( r );
00438 setBoundingRect( br );
00439 }
00440
00441 void GraphicsItem::mouseMoveEvent( QGraphicsSceneMouseEvent* event )
00442 {
00443 if ( !isEditable() ) return;
00444
00445 QPointF pos = event->pos() - m_presspos;
00446 switch( m_istate ) {
00447 case ItemDelegate::State_ExtendLeft:
00448 case ItemDelegate::State_ExtendRight:
00449 case ItemDelegate::State_Move:
00450
00451 if ( qAbs( m_pressscenepos.x()-event->scenePos().x() ) < 10.
00452 && qAbs( m_pressscenepos.y()-event->scenePos().y() ) > 5. ) {
00453 m_istate = ItemDelegate::State_DragConstraint;
00454 m_dragline = new QGraphicsLineItem( this );
00455 m_dragline->setPen( QPen( Qt::DashLine ) );
00456 m_dragline->setLine(QLineF( rect().center(), event->pos() ));
00457 scene()->addItem( m_dragline );
00458 scene()->setDragSource( this );
00459 break;
00460 }
00461
00462 scene()->selectionModel()->setCurrentIndex( index(), QItemSelectionModel::Current );
00463 updateItemFromMouse(event->scenePos());
00464
00465 break;
00466 case ItemDelegate::State_DragConstraint: {
00467 QLineF line = m_dragline->line();
00468 m_dragline->setLine( QLineF( line.p1(), event->pos() ) );
00469
00470 break;
00471 }
00472 }
00473 }