1 /****************************************************************************
2 ** Copyright (C) 2001-2006 Klarälvdalens Datakonsult AB. All rights reserved.
4 ** This file is part of the KD Gantt library.
6 ** This file may be distributed and/or modified under the terms of the
7 ** GNU General Public License version 2 as published by the Free Software
8 ** Foundation and appearing in the file LICENSE.GPL included in the
9 ** packaging of this file.
11 ** Licensees holding valid commercial KD Gantt licenses may use this file in
12 ** accordance with the KD Gantt Commercial License Agreement provided with
15 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18 ** See http://www.kdab.net/kdgantt for
19 ** information about KD Gantt Commercial License Agreements.
21 ** Contact info@kdab.net if any conditions of this
22 ** licensing are not clear to you.
24 **********************************************************************/
25 #include "kdganttgraphicsitem.h"
26 #include "kdganttgraphicsscene.h"
27 #include "kdganttgraphicsview.h"
28 #include "kdganttitemdelegate.h"
29 #include "kdganttconstraintgraphicsitem.h"
30 #include "kdganttconstraintmodel.h"
31 #include "kdganttabstractgrid.h"
32 #include "kdganttabstractrowcontroller.h"
40 #include <QAbstractItemModel>
41 #include <QAbstractProxyModel>
42 #include <QItemSelectionModel>
43 #include <QGraphicsSceneMouseEvent>
44 #include <QGraphicsLineItem>
48 /*!\class KDGantt::GraphicsItem
52 using namespace KDGantt
;
54 typedef QGraphicsItem BASE
;
61 Updater( bool* u
) : u_ptr( u
), oldval( *u
) {
70 GraphicsItem::GraphicsItem( QGraphicsItem
* parent
, GraphicsScene
* scene
)
71 : BASE( parent
, scene
), m_isupdating( false )
76 GraphicsItem::GraphicsItem( const QModelIndex
& idx
, QGraphicsItem
* parent
,
77 GraphicsScene
* scene
)
78 : BASE( parent
, scene
), m_index( idx
), m_isupdating( false )
83 GraphicsItem::~GraphicsItem()
87 void GraphicsItem::init()
89 #if QT_VERSION >= QT_VERSION_CHECK(4,4,0)
90 setCacheMode( QGraphicsItem::DeviceCoordinateCache
);
92 setFlags( ItemIsMovable
|ItemIsSelectable
|ItemIsFocusable
);
93 setAcceptsHoverEvents( true );
94 setHandlesChildEvents( true );
99 int GraphicsItem::type() const
104 StyleOptionGanttItem
GraphicsItem::getStyleOption() const
106 StyleOptionGanttItem opt
;
107 opt
.itemRect
= rect();
108 opt
.boundingRect
= boundingRect();
109 QVariant tp
= m_index
.model()->data( m_index
, TextPositionRole
);
111 opt
.displayPosition
= static_cast<StyleOptionGanttItem::Position
>(tp
.toInt());
114 qDebug() << "Item" << m_index
.model()->data( m_index
, Qt::DisplayRole
).toString()
115 << ", ends="<<m_endConstraints
.size() << ", starts="<<m_startConstraints
.size();
117 opt
.displayPosition
= m_endConstraints
.size()<m_startConstraints
.size()?StyleOptionGanttItem::Left
:StyleOptionGanttItem::Right
;
119 qDebug() << "choosing" << opt
.displayPosition
;
122 QVariant da
= m_index
.model()->data( m_index
, Qt::TextAlignmentRole
);
123 if ( da
.isValid() ) {
124 opt
.displayAlignment
= static_cast< Qt::Alignment
>( da
.toInt() );
126 switch( opt
.displayPosition
) {
127 case StyleOptionGanttItem::Left
: opt
.displayAlignment
= Qt::AlignLeft
|Qt::AlignVCenter
; break;
128 case StyleOptionGanttItem::Right
: opt
.displayAlignment
= Qt::AlignRight
|Qt::AlignVCenter
; break;
129 case StyleOptionGanttItem::Center
: opt
.displayAlignment
= Qt::AlignCenter
; break;
132 opt
.grid
= scene()->grid();
133 opt
.text
= m_index
.model()->data( m_index
, Qt::DisplayRole
).toString();
134 if ( isEnabled() ) opt
.state
|= QStyle::State_Enabled
;
135 if ( isSelected() ) opt
.state
|= QStyle::State_Selected
;
136 if ( hasFocus() ) opt
.state
|= QStyle::State_HasFocus
;
140 GraphicsScene
* GraphicsItem::scene() const
142 return qobject_cast
<GraphicsScene
*>( QGraphicsItem::scene() );
145 void GraphicsItem::setRect( const QRectF
& r
)
148 qDebug() << "GraphicsItem::setRect("<<r
<<"), txt="<<m_index
.model()->data( m_index
, Qt::DisplayRole
).toString();
149 if ( m_index
.model()->data( m_index
, Qt::DisplayRole
).toString() == QLatin1String("Code Freeze" ) ) {
150 qDebug() << "gotcha";
154 prepareGeometryChange();
156 updateConstraintItems();
160 void GraphicsItem::setBoundingRect( const QRectF
& r
)
162 prepareGeometryChange();
167 bool GraphicsItem::isEditable() const
169 return !scene()->isReadOnly() && m_index
.model()->flags( m_index
) & Qt::ItemIsEditable
;
172 void GraphicsItem::paint( QPainter
* painter
, const QStyleOptionGraphicsItem
* option
,
176 if ( boundingRect().isValid() && scene() ) {
177 StyleOptionGanttItem opt
= getStyleOption();
178 *static_cast<QStyleOption
*>(&opt
) = *static_cast<const QStyleOption
*>( option
);
179 //opt.fontMetrics = painter->fontMetrics();
180 scene()->itemDelegate()->paintGanttItem( painter
, opt
, index() );
184 void GraphicsItem::setIndex( const QPersistentModelIndex
& idx
)
190 QString
GraphicsItem::ganttToolTip() const
192 // TODO: Make delegate handle this
193 const QAbstractItemModel
* model
= index().model();
194 if ( !model
) return QString();
197 QDebug( &dbgstr
) << m_index
;
200 QString tip
= model
->data( index(), Qt::ToolTipRole
).toString();
201 if ( !tip
.isNull() ) return tip
;
202 else return GraphicsScene::tr( "%1 -> %2: %3" )
203 .arg( model
->data( index(), StartTimeRole
).toString() )
204 .arg( model
->data( index(), EndTimeRole
).toString() )
205 .arg( model
->data( index(), Qt::DisplayRole
).toString() );
208 QRectF
GraphicsItem::boundingRect() const
210 return m_boundingrect
;
213 QPointF
GraphicsItem::startConnector() const
215 return mapToScene( m_rect
.right(), m_rect
.top()+m_rect
.height()/2. );
218 QPointF
GraphicsItem::endConnector() const
220 return mapToScene( m_rect
.left(), m_rect
.top()+m_rect
.height()/2. );
224 void GraphicsItem::constraintsChanged()
226 if ( !scene() || !scene()->itemDelegate() ) return;
227 const Span bs
= scene()->itemDelegate()->itemBoundingSpan( getStyleOption(), index() );
228 const QRectF br
= boundingRect();
229 setBoundingRect( QRectF( bs
.start(), 0., bs
.length(), br
.height() ) );
232 void GraphicsItem::addStartConstraint( ConstraintGraphicsItem
* item
)
235 m_startConstraints
<< item
;
236 item
->setStart( startConnector() );
237 constraintsChanged();
240 void GraphicsItem::addEndConstraint( ConstraintGraphicsItem
* item
)
243 m_endConstraints
<< item
;
244 item
->setEnd( endConnector() );
245 constraintsChanged();
248 void GraphicsItem::removeStartConstraint( ConstraintGraphicsItem
* item
)
251 m_startConstraints
.removeAll( item
);
252 constraintsChanged();
255 void GraphicsItem::removeEndConstraint( ConstraintGraphicsItem
* item
)
258 m_endConstraints
.removeAll( item
);
259 constraintsChanged();
262 void GraphicsItem::updateConstraintItems()
264 QPointF s
= startConnector();
265 QPointF e
= endConnector();
266 { // Workaround for multiple definition error with MSVC6
267 Q_FOREACH( ConstraintGraphicsItem
* item
, m_startConstraints
) {
270 {// Workaround for multiple definition error with MSVC6
271 Q_FOREACH( ConstraintGraphicsItem
* item
, m_endConstraints
) {
276 void GraphicsItem::updateItem( const Span
& rowGeometry
, const QPersistentModelIndex
& idx
)
278 //qDebug() << "GraphicsItem::updateItem("<<rowGeometry<<idx<<")";
279 Updater
updater( &m_isupdating
);
280 if ( !idx
.isValid() || idx
.data( ItemTypeRole
)==TypeMulti
) {
286 const Span s
= scene()->grid()->mapToChart( idx
);
287 setPos( QPointF( s
.start(), rowGeometry
.start() ) );
288 setRect( QRectF( 0., 0., s
.length(), rowGeometry
.length() ) );
290 const Span bs
= scene()->itemDelegate()->itemBoundingSpan( getStyleOption(), index() );
291 //qDebug() << "boundingSpan for" << getStyleOption().text << rect() << "is" << bs;
292 setBoundingRect( QRectF( bs
.start(), 0., bs
.length(), rowGeometry
.length() ) );
293 const int maxh
= scene()->rowController()->maximumItemHeight();
294 if ( maxh
< rowGeometry
.length() ) {
296 const Qt::Alignment align
= getStyleOption().displayAlignment
;
297 if ( align
& Qt::AlignTop
) {
299 } else if ( align
& Qt::AlignBottom
) {
300 r
.setY( rowGeometry
.length()-maxh
);
303 r
.setY( ( rowGeometry
.length()-maxh
) / 2. );
309 //scene()->setSceneRect( scene()->sceneRect().united( mapToScene( boundingRect() ).boundingRect() ) );
310 //updateConstraintItems();
313 QVariant
GraphicsItem::itemChange( GraphicsItemChange change
, const QVariant
& value
)
315 if ( !isUpdating() && change
==ItemPositionChange
&& scene() ) {
316 QPointF newPos
=value
.toPointF();
317 if ( isEditable() ) {
318 newPos
.setY( pos().y() );
323 } else if ( change
==QGraphicsItem::ItemSelectedChange
) {
324 if ( index().isValid() && !( index().model()->flags( index() ) & Qt::ItemIsSelectable
) ) {
325 // Reject selection attempt
326 return qVariantFromValue( false );
329 if ( value
.toBool() ) {
330 scene()->selectionModel()->select( index(), QItemSelectionModel::Select
);
332 scene()->selectionModel()->select( index(), QItemSelectionModel::Deselect
);
336 return QGraphicsItem::itemChange( change
, value
);
339 void GraphicsItem::focusInEvent( QFocusEvent
* event
)
342 scene()->selectionModel()->select( index(), QItemSelectionModel::SelectCurrent
);
345 void GraphicsItem::updateModel()
347 //qDebug() << "GraphicsItem::updateModel()";
348 if ( isEditable() ) {
349 QAbstractItemModel
* model
= const_cast<QAbstractItemModel
*>( index().model() );
350 ConstraintModel
* cmodel
= scene()->constraintModel();
354 //ItemType typ = static_cast<ItemType>( model->data( index(),
355 // ItemTypeRole ).toInt() );
356 const QModelIndex sourceIdx
= scene()->summaryHandlingModel()->mapToSource( index() );
357 QList
<Constraint
> constraints
;
358 for( QList
<ConstraintGraphicsItem
*>::iterator it1
= m_startConstraints
.begin() ;
359 it1
!= m_startConstraints
.end() ;
361 constraints
.push_back((*it1
)->proxyConstraint());
362 for( QList
<ConstraintGraphicsItem
*>::iterator it2
= m_endConstraints
.begin() ;
363 it2
!= m_endConstraints
.end() ;
365 constraints
.push_back((*it2
)->proxyConstraint());
366 if ( scene()->grid()->mapFromChart( Span( scenePos().x(), rect().width() ),
369 scene()->updateRow( index().parent() );
375 void GraphicsItem::hoverMoveEvent( QGraphicsSceneHoverEvent
* event
)
377 if ( !isEditable() ) return;
378 StyleOptionGanttItem opt
= getStyleOption();
379 ItemDelegate::InteractionState istate
= scene()->itemDelegate()->interactionStateFor( event
->pos(), opt
, index() );
381 case ItemDelegate::State_ExtendLeft
:
383 setCursor( Qt::SizeHorCursor
);
385 scene()->itemEntered( index() );
387 case ItemDelegate::State_ExtendRight
:
389 setCursor( Qt::SizeHorCursor
);
391 scene()->itemEntered( index() );
393 case ItemDelegate::State_Move
:
395 setCursor( Qt::SplitHCursor
);
397 scene()->itemEntered( index() );
407 void GraphicsItem::hoverLeaveEvent( QGraphicsSceneHoverEvent
* )
414 void GraphicsItem::mousePressEvent( QGraphicsSceneMouseEvent
* event
)
416 //qDebug() << "GraphicsItem::mousePressEvent("<<event<<")";
417 StyleOptionGanttItem opt
= getStyleOption();
418 m_istate
= scene()->itemDelegate()->interactionStateFor( event
->pos(), opt
, index() );
419 m_presspos
= event
->pos();
420 m_pressscenepos
= event
->scenePos();
421 scene()->itemPressed( index() );
424 case ItemDelegate::State_ExtendLeft
:
425 case ItemDelegate::State_ExtendRight
:
426 default: /* None and Move */
427 BASE::mousePressEvent( event
);
432 void GraphicsItem::mouseReleaseEvent( QGraphicsSceneMouseEvent
* event
)
434 //qDebug() << "GraphicsItem::mouseReleaseEvent("<<event << ")";
435 if ( !m_presspos
.isNull() ) {
436 scene()->itemClicked( index() );
438 delete m_dragline
; m_dragline
= 0;
439 if ( scene()->dragSource() ) {
440 // Create a new constraint
441 GraphicsItem
* other
= qgraphicsitem_cast
<GraphicsItem
*>( scene()->itemAt( event
->scenePos() ) );
442 if ( other
&& scene()->dragSource()!=other
&&
443 other
->mapToScene( other
->rect() ).boundingRect().contains( event
->scenePos() )) {
444 GraphicsView
* view
= qobject_cast
<GraphicsView
*>( event
->widget()->parentWidget() );
446 view
->addConstraint( scene()->summaryHandlingModel()->mapToSource( scene()->dragSource()->index() ),
447 scene()->summaryHandlingModel()->mapToSource( other
->index() ), event
->modifiers() );
450 scene()->setDragSource( 0 );
453 if ( isEditable() ) {
454 updateItemFromMouse(event
->scenePos());
456 // It is important to set m_presspos to null here because
457 // when the sceneRect updates because we move the item
458 // a MouseMoveEvent will be delivered, and we have to
459 // protect against that
460 m_presspos
= QPointF();
462 // without this command we sometimes get a white area at the left side of a task item
463 // after we moved that item right-ways into a grey weekend section of the scene:
468 m_presspos
= QPointF();
469 BASE::mouseReleaseEvent( event
);
472 void GraphicsItem::mouseDoubleClickEvent( QGraphicsSceneMouseEvent
* event
)
474 StyleOptionGanttItem opt
= getStyleOption();
475 ItemDelegate::InteractionState istate
= scene()->itemDelegate()->interactionStateFor( event
->pos(), opt
, index() );
476 if ( istate
!= ItemDelegate::State_None
) {
477 scene()->itemDoubleClicked( index() );
479 BASE::mouseDoubleClickEvent( event
);
482 void GraphicsItem::updateItemFromMouse( const QPointF
& scenepos
)
484 //qDebug() << "GraphicsItem::updateItemFromMouse("<<scenepos<<")";
485 const QPointF p
= scenepos
- m_presspos
;
487 QRectF br
= boundingRect();
489 case ItemDelegate::State_Move
:
490 setPos( p
.x(), pos().y() );
492 case ItemDelegate::State_ExtendLeft
: {
493 const qreal brr
= br
.right();
494 const qreal rr
= r
.right();
495 const qreal delta
= pos().x()-p
.x();
496 setPos( p
.x(), QGraphicsItem::pos().y() );
497 br
.setRight( brr
+delta
);
498 r
.setRight( rr
+delta
);
501 case ItemDelegate::State_ExtendRight
: {
502 const qreal rr
= r
.right();
503 r
.setRight( scenepos
.x()-pos().x() );
504 br
.setWidth( br
.width() + r
.right()-rr
);
510 setBoundingRect( br
);
513 void GraphicsItem::mouseMoveEvent( QGraphicsSceneMouseEvent
* event
)
515 if ( !isEditable() ) return;
516 if ( m_presspos
.isNull() ) return;
518 //qDebug() << "GraphicsItem::mouseMoveEvent("<<event<<"), m_istate="<< static_cast<ItemDelegate::InteractionState>( m_istate );
519 //QPointF pos = event->pos() - m_presspos;
521 case ItemDelegate::State_ExtendLeft
:
522 case ItemDelegate::State_ExtendRight
:
523 case ItemDelegate::State_Move
:
524 // Check for constraint drag
525 if ( qAbs( m_pressscenepos
.x()-event
->scenePos().x() ) < 10.
526 && qAbs( m_pressscenepos
.y()-event
->scenePos().y() ) > 5. ) {
527 m_istate
= ItemDelegate::State_DragConstraint
;
528 m_dragline
= new QGraphicsLineItem( this );
529 m_dragline
->setPen( QPen( Qt::DashLine
) );
530 m_dragline
->setLine(QLineF( rect().center(), event
->pos() ));
531 scene()->addItem( m_dragline
);
532 scene()->setDragSource( this );
536 scene()->selectionModel()->setCurrentIndex( index(), QItemSelectionModel::Current
);
537 updateItemFromMouse(event
->scenePos());
538 //BASE::mouseMoveEvent(event);
540 case ItemDelegate::State_DragConstraint
: {
541 QLineF line
= m_dragline
->line();
542 m_dragline
->setLine( QLineF( line
.p1(), event
->pos() ) );
543 //QGraphicsItem* item = scene()->itemAt( event->scenePos() );