KD Chart 2  [rev.2.5.1]
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Pages
kdganttgraphicsitem.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Copyright (C) 2001-2013 Klaralvdalens Datakonsult AB. All rights reserved.
3 **
4 ** This file is part of the KD Chart library.
5 **
6 ** Licensees holding valid commercial KD Chart licenses may use this file in
7 ** accordance with the KD Chart Commercial License Agreement provided with
8 ** the Software.
9 **
10 **
11 ** This file may be distributed and/or modified under the terms of the
12 ** GNU General Public License version 2 and version 3 as published by the
13 ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included.
14 **
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.
17 **
18 ** Contact info@kdab.com if any conditions of this licensing are not
19 ** clear to you.
20 **
21 **********************************************************************/
22 
23 #include "kdganttgraphicsitem.h"
24 #include "kdganttgraphicsscene.h"
25 #include "kdganttgraphicsview.h"
26 #include "kdganttitemdelegate.h"
28 #include "kdganttconstraintmodel.h"
29 #include "kdganttconstraint.h"
30 #include "kdganttabstractgrid.h"
32 
33 #include <cassert>
34 #include <cmath>
35 #include <algorithm>
36 #include <iterator>
37 
38 #include <QPainter>
39 #include <QAbstractItemModel>
40 #include <QAbstractProxyModel>
41 #include <QItemSelectionModel>
42 #include <QGraphicsSceneMouseEvent>
43 #include <QGraphicsLineItem>
44 
45 #include <QDebug>
46 
51 using namespace KDGantt;
52 
54 
55 namespace {
56  class Updater {
57  bool *u_ptr;
58  bool oldval;
59  public:
60  Updater( bool* u ) : u_ptr( u ), oldval( *u ) {
61  *u=true;
62  }
63  ~Updater() {
64  *u_ptr = oldval;
65  }
66  };
67 }
68 #if QT_VERSION < 0x050000
70  : BASE( parent, scene ), m_isupdating( false )
71 {
72  init();
73 }
74 
75 GraphicsItem::GraphicsItem( const QModelIndex& idx, QGraphicsItem* parent,
76  GraphicsScene* scene )
77  : BASE( parent, scene ), m_index( idx ), m_isupdating( false )
78 {
79  init();
80 }
81 #else
83  : BASE( parent ), m_isupdating( false )
84 {
85  if ( scene )
86  scene->addItem( this );
87  init();
88 }
89 
90 GraphicsItem::GraphicsItem( const QModelIndex& idx, QGraphicsItem* parent,
91  GraphicsScene* scene )
92  : BASE( parent ), m_index( idx ), m_isupdating( false )
93 {
94  init();
95  if ( scene )
96  scene->addItem( this );
97 }
98 #endif
99 
100 
102 {
103 }
104 
105 void GraphicsItem::init()
106 {
107 #if QT_VERSION >= QT_VERSION_CHECK(4,4,0)
108  setCacheMode( QGraphicsItem::DeviceCoordinateCache );
109 #endif
110  setFlags( ItemIsMovable|ItemIsSelectable|ItemIsFocusable );
111 #if QT_VERSION < 0x050000
112  setAcceptsHoverEvents( true );
113 #else
114  setAcceptHoverEvents( true );
115 #endif
116  setHandlesChildEvents( true );
117  setZValue( 100. );
118  m_dragline = 0;
119 }
120 
122 {
123  return Type;
124 }
125 
126 StyleOptionGanttItem GraphicsItem::getStyleOption() const
127 {
129  opt.itemRect = rect();
130  opt.boundingRect = boundingRect();
131  QVariant tp = m_index.model()->data( m_index, TextPositionRole );
132  if (tp.isValid()) {
133  opt.displayPosition = static_cast<StyleOptionGanttItem::Position>(tp.toInt());
134  } else {
135 #if 0
136  qDebug() << "Item" << m_index.model()->data( m_index, Qt::DisplayRole ).toString()
137  << ", ends="<<m_endConstraints.size() << ", starts="<<m_startConstraints.size();
138 #endif
139  opt.displayPosition = m_endConstraints.size()<m_startConstraints.size()?StyleOptionGanttItem::Left:StyleOptionGanttItem::Right;
140 #if 0
141  qDebug() << "choosing" << opt.displayPosition;
142 #endif
143  }
144  QVariant da = m_index.model()->data( m_index, Qt::TextAlignmentRole );
145  if ( da.isValid() ) {
146  opt.displayAlignment = static_cast< Qt::Alignment >( da.toInt() );
147  } else {
148  switch ( opt.displayPosition ) {
149  case StyleOptionGanttItem::Left: opt.displayAlignment = Qt::AlignLeft|Qt::AlignVCenter; break;
150  case StyleOptionGanttItem::Right: opt.displayAlignment = Qt::AlignRight|Qt::AlignVCenter; break;
151  case StyleOptionGanttItem::Hidden: // fall through
152  case StyleOptionGanttItem::Center: opt.displayAlignment = Qt::AlignCenter; break;
153  }
154  }
155  opt.grid = scene()->grid();
156  opt.text = m_index.model()->data( m_index, Qt::DisplayRole ).toString();
157  if ( isEnabled() ) opt.state |= QStyle::State_Enabled;
158  if ( isSelected() ) opt.state |= QStyle::State_Selected;
159  if ( hasFocus() ) opt.state |= QStyle::State_HasFocus;
160  return opt;
161 }
162 
164 {
165  return qobject_cast<GraphicsScene*>( QGraphicsItem::scene() );
166 }
167 
168 void GraphicsItem::setRect( const QRectF& r )
169 {
170 #if 0
171  qDebug() << "GraphicsItem::setRect("<<r<<"), txt="<<m_index.model()->data( m_index, Qt::DisplayRole ).toString();
172  if ( m_index.model()->data( m_index, Qt::DisplayRole ).toString() == QLatin1String("Code Freeze" ) ) {
173  qDebug() << "gotcha";
174  }
175 #endif
176 
177  prepareGeometryChange();
178  m_rect = r;
179  updateConstraintItems();
180  update();
181 }
182 
183 void GraphicsItem::setBoundingRect( const QRectF& r )
184 {
185  prepareGeometryChange();
186  m_boundingrect = r;
187  update();
188 }
189 
191 {
192  return !scene()->isReadOnly() && m_index.model()->flags( m_index ) & Qt::ItemIsEditable;
193 }
194 
195 void GraphicsItem::paint( QPainter* painter, const QStyleOptionGraphicsItem* option,
196  QWidget* widget )
197 {
198  Q_UNUSED( widget );
199  if ( boundingRect().isValid() && scene() ) {
200  StyleOptionGanttItem opt = getStyleOption();
201  *static_cast<QStyleOption*>(&opt) = *static_cast<const QStyleOption*>( option );
202  //opt.fontMetrics = painter->fontMetrics();
203  scene()->itemDelegate()->paintGanttItem( painter, opt, index() );
204  }
205 }
206 
207 void GraphicsItem::setIndex( const QPersistentModelIndex& idx )
208 {
209  m_index=idx;
210  update();
211 }
212 
214 {
215  return scene()->itemDelegate()->toolTip( index() );
216 }
217 
219 {
220  return m_boundingrect;
221 }
222 
223 QPointF GraphicsItem::startConnector( int relationType ) const
224 {
225  switch ( relationType ) {
228  return mapToScene( m_rect.left(), m_rect.top()+m_rect.height()/2. );
229  default:
230  break;
231  }
232  return mapToScene( m_rect.right(), m_rect.top()+m_rect.height()/2. );
233 }
234 
235 QPointF GraphicsItem::endConnector( int relationType ) const
236 {
237  switch ( relationType ) {
240  return mapToScene( m_rect.right(), m_rect.top()+m_rect.height()/2. );
241  default:
242  break;
243  }
244  return mapToScene( m_rect.left(), m_rect.top()+m_rect.height()/2. );
245 }
246 
247 
248 void GraphicsItem::constraintsChanged()
249 {
250  if ( !scene() || !scene()->itemDelegate() ) return;
251  const Span bs = scene()->itemDelegate()->itemBoundingSpan( getStyleOption(), index() );
252  const QRectF br = boundingRect();
253  setBoundingRect( QRectF( bs.start(), 0., bs.length(), br.height() ) );
254 }
255 
257 {
258  assert( item );
259  m_startConstraints << item;
260  item->setStart( startConnector( item->constraint().relationType() ) );
261  constraintsChanged();
262 }
263 
265 {
266  assert( item );
267  m_endConstraints << item;
268  item->setEnd( endConnector( item->constraint().relationType() ) );
269  constraintsChanged();
270 }
271 
273 {
274  assert( item );
275  m_startConstraints.removeAll( item );
276  constraintsChanged();
277 }
278 
280 {
281  assert( item );
282  m_endConstraints.removeAll( item );
283  constraintsChanged();
284 }
285 
286 void GraphicsItem::updateConstraintItems()
287 {
288  { // Workaround for multiple definition error with MSVC6
289  Q_FOREACH( ConstraintGraphicsItem* item, m_startConstraints ) {
290  QPointF s = startConnector( item->constraint().relationType() );
291  item->setStart( s );
292  }}
293  {// Workaround for multiple definition error with MSVC6
294  Q_FOREACH( ConstraintGraphicsItem* item, m_endConstraints ) {
295  QPointF e = endConnector( item->constraint().relationType() );
296  item->setEnd( e );
297  }}
298 }
299 
300 void GraphicsItem::updateItem( const Span& rowGeometry, const QPersistentModelIndex& idx )
301 {
302  //qDebug() << "GraphicsItem::updateItem("<<rowGeometry<<idx<<")";
303  Updater updater( &m_isupdating );
304  if ( !idx.isValid() || idx.data( ItemTypeRole )==TypeMulti ) {
305  setRect( QRectF() );
306  hide();
307  return;
308  }
309 
310  const Span s = scene()->grid()->mapToChart( idx );
311  setPos( QPointF( s.start(), rowGeometry.start() ) );
312  setRect( QRectF( 0., 0., s.length(), rowGeometry.length() ) );
313  setIndex( idx );
314  const Span bs = scene()->itemDelegate()->itemBoundingSpan( getStyleOption(), index() );
315  //qDebug() << "boundingSpan for" << getStyleOption().text << rect() << "is" << bs;
316  setBoundingRect( QRectF( bs.start(), 0., bs.length(), rowGeometry.length() ) );
317  const int maxh = scene()->rowController()->maximumItemHeight();
318  if ( maxh < rowGeometry.length() ) {
319  QRectF r = rect();
320  const Qt::Alignment align = getStyleOption().displayAlignment;
321  if ( align & Qt::AlignTop ) {
322  // Do nothing
323  } else if ( align & Qt::AlignBottom ) {
324  r.setY( rowGeometry.length()-maxh );
325  } else {
326  // Center
327  r.setY( ( rowGeometry.length()-maxh ) / 2. );
328  }
329  r.setHeight( maxh );
330  setRect( r );
331  }
332 
333  //scene()->setSceneRect( scene()->sceneRect().united( mapToScene( boundingRect() ).boundingRect() ) );
334  //updateConstraintItems();
335 }
336 
337 QVariant GraphicsItem::itemChange( GraphicsItemChange change, const QVariant& value )
338 {
339  if ( !isUpdating() && change==ItemPositionChange && scene() ) {
340  QPointF newPos=value.toPointF();
341  if ( isEditable() ) {
342  newPos.setY( pos().y() );
343  return newPos;
344  } else {
345  return pos();
346  }
347  } else if ( change==QGraphicsItem::ItemSelectedChange ) {
348  if ( index().isValid() && !( index().model()->flags( index() ) & Qt::ItemIsSelectable ) ) {
349  // Reject selection attempt
350  return qVariantFromValue( false );
351  }
352 
353  if ( value.toBool() ) {
354  scene()->selectionModel()->select( index(), QItemSelectionModel::Select );
355  } else {
356  scene()->selectionModel()->select( index(), QItemSelectionModel::Deselect );
357  }
358  }
359 
360  return QGraphicsItem::itemChange( change, value );
361 }
362 
363 void GraphicsItem::focusInEvent( QFocusEvent* event )
364 {
365  Q_UNUSED( event );
366  scene()->selectionModel()->select( index(), QItemSelectionModel::SelectCurrent );
367 }
368 
369 void GraphicsItem::updateModel()
370 {
371  //qDebug() << "GraphicsItem::updateModel()";
372  if ( isEditable() ) {
373  QAbstractItemModel* model = const_cast<QAbstractItemModel*>( index().model() );
374  ConstraintModel* cmodel = scene()->constraintModel();
375  assert( model );
376  assert( cmodel );
377  if ( model ) {
378  //ItemType typ = static_cast<ItemType>( model->data( index(),
379  // ItemTypeRole ).toInt() );
380  QList<Constraint> constraints;
381  for ( QList<ConstraintGraphicsItem*>::iterator it1 = m_startConstraints.begin() ;
382  it1 != m_startConstraints.end() ;
383  ++it1 )
384  constraints.push_back((*it1)->proxyConstraint());
385  for ( QList<ConstraintGraphicsItem*>::iterator it2 = m_endConstraints.begin() ;
386  it2 != m_endConstraints.end() ;
387  ++it2 )
388  constraints.push_back((*it2)->proxyConstraint());
389  if ( scene()->grid()->mapFromChart( Span( scenePos().x(), rect().width() ),
390  index(),
391  constraints ) ) {
392  scene()->updateRow( index().parent() );
393  }
394  }
395  }
396 }
397 
398 void GraphicsItem::hoverMoveEvent( QGraphicsSceneHoverEvent* event )
399 {
400  if ( !isEditable() ) return;
401  StyleOptionGanttItem opt = getStyleOption();
402  ItemDelegate::InteractionState istate = scene()->itemDelegate()->interactionStateFor( event->pos(), opt, index() );
403  switch ( istate ) {
405 #ifndef QT_NO_CURSOR
406  setCursor( Qt::SizeHorCursor );
407 #endif
408  scene()->itemEntered( index() );
409  break;
411 #ifndef QT_NO_CURSOR
412  setCursor( Qt::SizeHorCursor );
413 #endif
414  scene()->itemEntered( index() );
415  break;
417 #ifndef QT_NO_CURSOR
418  setCursor( Qt::SplitHCursor );
419 #endif
420  scene()->itemEntered( index() );
421  break;
422  default:
423 #ifndef QT_NO_CURSOR
424  unsetCursor();
425 #endif
426  break;
427  };
428 }
429 
430 void GraphicsItem::hoverLeaveEvent( QGraphicsSceneHoverEvent* )
431 {
432 #ifndef QT_NO_CURSOR
433  unsetCursor();
434 #endif
435 }
436 
437 void GraphicsItem::mousePressEvent( QGraphicsSceneMouseEvent* event )
438 {
439  //qDebug() << "GraphicsItem::mousePressEvent("<<event<<")";
440  StyleOptionGanttItem opt = getStyleOption();
441  int istate = scene()->itemDelegate()->interactionStateFor( event->pos(), opt, index() );
442  // If State_None is returned by interactionStateFor(), we ignore this event so that
443  // it can get forwarded to another item that's below this one. Needed, for example,
444  // to allow items to be moved that are placed below the label of another item.
445  if ( istate != ItemDelegate::State_None ) {
446  m_istate = istate;
447  m_presspos = event->pos();
448  m_pressscenepos = event->scenePos();
449  scene()->itemPressed( index() );
450 
451  switch ( m_istate ) {
454  default: /* State_Move */
455  BASE::mousePressEvent( event );
456  break;
457  }
458  } else {
459  event->ignore();
460  }
461 }
462 
463 void GraphicsItem::mouseReleaseEvent( QGraphicsSceneMouseEvent* event )
464 {
465  //qDebug() << "GraphicsItem::mouseReleaseEvent("<<event << ")";
466  if ( !m_presspos.isNull() ) {
467  scene()->itemClicked( index() );
468  }
469  delete m_dragline; m_dragline = 0;
470  if ( scene()->dragSource() ) {
471  // Create a new constraint
472  GraphicsItem* other = qgraphicsitem_cast<GraphicsItem*>( scene()->itemAt( event->scenePos(), QTransform() ) );
473  if ( other && scene()->dragSource()!=other &&
475  {
476  // The code below fixes bug KDCH-696.
477  // Modified the code to add constraint even if the user drags and drops
478  // constraint on left part of the TypeEvent symbol(i.e diamond symbol)
479  QRectF itemRect = other->rect().adjusted(-other->rect().height()/2.0, 0, 0, 0 );
480  if ( other->mapToScene( itemRect ).boundingRect().contains( event->scenePos() ))
481  {
482  GraphicsView* view = qobject_cast<GraphicsView*>( event->widget()->parentWidget() );
483  if ( view ) {
484  view->addConstraint( scene()->summaryHandlingModel()->mapToSource( scene()->dragSource()->index() ),
485  scene()->summaryHandlingModel()->mapToSource( other->index() ), event->modifiers() );
486  }
487  }
488  }
489  else
490  {
491  if ( other && scene()->dragSource()!=other &&
492  other->mapToScene( other->rect() ).boundingRect().contains( event->scenePos() )) {
493  GraphicsView* view = qobject_cast<GraphicsView*>( event->widget()->parentWidget() );
494  if ( view ) {
495  view->addConstraint( scene()->summaryHandlingModel()->mapToSource( scene()->dragSource()->index() ),
496  scene()->summaryHandlingModel()->mapToSource( other->index() ), event->modifiers() );
497  }
498  }
499  }
500 
501  scene()->setDragSource( 0 );
502  //scene()->update();
503  } else {
504  if ( isEditable() ) {
505  updateItemFromMouse(event->scenePos());
506 
507  // It is important to set m_presspos to null here because
508  // when the sceneRect updates because we move the item
509  // a MouseMoveEvent will be delivered, and we have to
510  // protect against that
511  m_presspos = QPointF();
512  updateModel();
513  // without this command we sometimes get a white area at the left side of a task item
514  // after we moved that item right-ways into a grey weekend section of the scene:
515  scene()->update();
516  }
517  }
518 
519  m_presspos = QPointF();
520  BASE::mouseReleaseEvent( event );
521 }
522 
523 void GraphicsItem::mouseDoubleClickEvent( QGraphicsSceneMouseEvent* event )
524 {
525  const int typ = static_cast<ItemType>( index().model()->data( index(), ItemTypeRole ).toInt() );
526  StyleOptionGanttItem opt = getStyleOption();
527  ItemDelegate::InteractionState istate = scene()->itemDelegate()->interactionStateFor( event->pos(), opt, index() );
528  if ( (istate != ItemDelegate::State_None) || (typ == TypeSummary)) {
529  scene()->itemDoubleClicked( index() );
530  }
531  BASE::mouseDoubleClickEvent( event );
532 }
533 
534 void GraphicsItem::updateItemFromMouse( const QPointF& scenepos )
535 {
536  //qDebug() << "GraphicsItem::updateItemFromMouse("<<scenepos<<")";
537  const QPointF p = scenepos - m_presspos;
538  QRectF r = rect();
539  QRectF br = boundingRect();
540  switch ( m_istate ) {
542  setPos( p.x(), pos().y() );
543  break;
545  const qreal brr = br.right();
546  const qreal rr = r.right();
547  const qreal delta = pos().x()-p.x();
548  setPos( p.x(), QGraphicsItem::pos().y() );
549  br.setRight( brr+delta );
550  r.setRight( rr+delta );
551  break;
552  }
554  const qreal rr = r.right();
555  r.setRight( scenepos.x()-pos().x() );
556  br.setWidth( br.width() + r.right()-rr );
557  break;
558  }
559  default: return;
560  }
561  setRect( r );
562  setBoundingRect( br );
563 }
564 
565 void GraphicsItem::mouseMoveEvent( QGraphicsSceneMouseEvent* event )
566 {
567  if ( !isEditable() ) return;
568  if ( m_presspos.isNull() ) return;
569 
570  //qDebug() << "GraphicsItem::mouseMoveEvent("<<event<<"), m_istate="<< static_cast<ItemDelegate::InteractionState>( m_istate );
571  switch ( m_istate ) {
575  // Check for constraint drag
576  if ( qAbs( m_pressscenepos.x()-event->scenePos().x() ) < 10.
577  && qAbs( m_pressscenepos.y()-event->scenePos().y() ) > 5. ) {
579  m_dragline = new QGraphicsLineItem( this );
580  m_dragline->setPen( QPen( Qt::DashLine ) );
581  m_dragline->setLine(QLineF( rect().center(), event->pos() ));
582  scene()->setDragSource( this );
583  break;
584  }
585 
586  scene()->selectionModel()->setCurrentIndex( index(), QItemSelectionModel::Current );
587  updateItemFromMouse(event->scenePos());
588  //BASE::mouseMoveEvent(event);
589  break;
591  QLineF line = m_dragline->line();
592  m_dragline->setLine( QLineF( line.p1(), event->pos() ) );
593  //QGraphicsItem* item = scene()->itemAt( event->scenePos() );
594  break;
595  }
596  }
597 }

Klarälvdalens Datakonsult AB (KDAB)
Qt-related services and products
http://www.kdab.com/
http://www.kdab.com/products/kd-chart/