diff --git a/src/KGantt/kganttgraphicsitem.cpp b/src/KGantt/kganttgraphicsitem.cpp
index e11472b..3ced619 100644
--- a/src/KGantt/kganttgraphicsitem.cpp
+++ b/src/KGantt/kganttgraphicsitem.cpp
@@ -1,573 +1,573 @@
/*
* Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
*
* This file is part of the KGantt library.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "kganttgraphicsitem.h"
#include "kganttgraphicsscene.h"
#include "kganttgraphicsview.h"
#include "kganttitemdelegate.h"
#include "kganttconstraintgraphicsitem.h"
#include "kganttconstraintmodel.h"
#include "kganttconstraint.h"
#include "kganttabstractgrid.h"
#include "kganttabstractrowcontroller.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*!\class KGantt::GraphicsItem
* \internal
*/
using namespace KGantt;
typedef QGraphicsItem BASE;
namespace {
class Updater {
bool *u_ptr;
bool oldval;
public:
Updater( bool* u ) : u_ptr( u ), oldval( *u ) {
*u=true;
}
~Updater() {
*u_ptr = oldval;
}
};
}
GraphicsItem::GraphicsItem( QGraphicsItem* parent, GraphicsScene* scene )
: BASE( parent ), m_isupdating( false )
{
if ( scene )
scene->addItem( this );
init();
}
GraphicsItem::GraphicsItem( const QModelIndex& idx, QGraphicsItem* parent,
GraphicsScene* scene )
: BASE( parent ), m_index( idx ), m_isupdating( false )
{
init();
if ( scene )
scene->addItem( this );
}
GraphicsItem::~GraphicsItem()
{
}
void GraphicsItem::init()
{
setCacheMode( QGraphicsItem::DeviceCoordinateCache );
setFlags( ItemIsMovable|ItemIsSelectable|ItemIsFocusable );
setAcceptHoverEvents( true );
setHandlesChildEvents( true );
setZValue( 100. );
m_dragline = nullptr;
}
int GraphicsItem::type() const
{
return Type;
}
StyleOptionGanttItem GraphicsItem::getStyleOption() const
{
StyleOptionGanttItem opt;
if (!m_index.isValid()) {
// TODO: find out why we get invalid indexes
//qDebug()<<"GraphicsItem::getStyleOption: Invalid index";
return opt;
}
opt.itemRect = rect();
opt.boundingRect = boundingRect();
QVariant tp = m_index.model()->data( m_index, TextPositionRole );
if (tp.isValid()) {
opt.displayPosition = static_cast(tp.toInt());
} else {
#if 0
qDebug() << "Item" << m_index.model()->data( m_index, Qt::DisplayRole ).toString()
<< ", ends="<data( m_index, Qt::TextAlignmentRole );
if ( da.isValid() ) {
opt.displayAlignment = static_cast< Qt::Alignment >( da.toInt() );
} else {
switch ( opt.displayPosition ) {
case StyleOptionGanttItem::Left: opt.displayAlignment = Qt::AlignLeft|Qt::AlignVCenter; break;
case StyleOptionGanttItem::Right: opt.displayAlignment = Qt::AlignRight|Qt::AlignVCenter; break;
case StyleOptionGanttItem::Hidden: // fall through
case StyleOptionGanttItem::Center: opt.displayAlignment = Qt::AlignCenter; break;
}
}
- opt.grid = scene()->grid();
+ opt.grid = const_cast(scene()->getGrid());
opt.text = m_index.model()->data( m_index, Qt::DisplayRole ).toString();
if ( isEnabled() ) opt.state |= QStyle::State_Enabled;
if ( isSelected() ) opt.state |= QStyle::State_Selected;
if ( hasFocus() ) opt.state |= QStyle::State_HasFocus;
return opt;
}
GraphicsScene* GraphicsItem::scene() const
{
return qobject_cast( QGraphicsItem::scene() );
}
void GraphicsItem::setRect( const QRectF& r )
{
#if 0
qDebug() << "GraphicsItem::setRect("<isReadOnly() && m_index.isValid() && m_index.model()->flags( m_index ) & Qt::ItemIsEditable;
}
void GraphicsItem::paint( QPainter* painter, const QStyleOptionGraphicsItem* option,
QWidget* widget )
{
Q_UNUSED( widget );
if ( boundingRect().isValid() && scene() ) {
StyleOptionGanttItem opt = getStyleOption();
*static_cast(&opt) = *static_cast( option );
//opt.fontMetrics = painter->fontMetrics();
scene()->itemDelegate()->paintGanttItem( painter, opt, index() );
}
}
void GraphicsItem::setIndex( const QPersistentModelIndex& idx )
{
m_index=idx;
update();
}
QString GraphicsItem::ganttToolTip() const
{
return scene()->itemDelegate()->toolTip( index() );
}
QRectF GraphicsItem::boundingRect() const
{
return m_boundingrect;
}
QPointF GraphicsItem::startConnector( int relationType ) const
{
switch ( relationType ) {
case Constraint::StartStart:
case Constraint::StartFinish:
return mapToScene( m_rect.left(), m_rect.top()+m_rect.height()/2. );
default:
break;
}
return mapToScene( m_rect.right(), m_rect.top()+m_rect.height()/2. );
}
QPointF GraphicsItem::endConnector( int relationType ) const
{
switch ( relationType ) {
case Constraint::FinishFinish:
case Constraint::StartFinish:
return mapToScene( m_rect.right(), m_rect.top()+m_rect.height()/2. );
default:
break;
}
return mapToScene( m_rect.left(), m_rect.top()+m_rect.height()/2. );
}
void GraphicsItem::constraintsChanged()
{
if ( !scene() || !scene()->itemDelegate() ) return;
const Span bs = scene()->itemDelegate()->itemBoundingSpan( getStyleOption(), index() );
const QRectF br = boundingRect();
setBoundingRect( QRectF( bs.start(), 0., bs.length(), br.height() ) );
}
void GraphicsItem::addStartConstraint( ConstraintGraphicsItem* item )
{
assert( item );
m_startConstraints << item;
item->setStart( startConnector( item->constraint().relationType() ) );
constraintsChanged();
}
void GraphicsItem::addEndConstraint( ConstraintGraphicsItem* item )
{
assert( item );
m_endConstraints << item;
item->setEnd( endConnector( item->constraint().relationType() ) );
constraintsChanged();
}
void GraphicsItem::removeStartConstraint( ConstraintGraphicsItem* item )
{
assert( item );
m_startConstraints.removeAll( item );
constraintsChanged();
}
void GraphicsItem::removeEndConstraint( ConstraintGraphicsItem* item )
{
assert( item );
m_endConstraints.removeAll( item );
constraintsChanged();
}
void GraphicsItem::updateConstraintItems()
{
{ // Workaround for multiple definition error with MSVC6
Q_FOREACH( ConstraintGraphicsItem* item, m_startConstraints ) {
QPointF s = startConnector( item->constraint().relationType() );
item->setStart( s );
}}
{// Workaround for multiple definition error with MSVC6
Q_FOREACH( ConstraintGraphicsItem* item, m_endConstraints ) {
QPointF e = endConnector( item->constraint().relationType() );
item->setEnd( e );
}}
}
void GraphicsItem::updateItem( const Span& rowGeometry, const QPersistentModelIndex& idx )
{
//qDebug() << "GraphicsItem::updateItem("<grid()->mapToChart( static_cast(idx) );
+ const Span s = scene()->getGrid()->mapToChart( static_cast(idx) );
setPos( QPointF( s.start(), rowGeometry.start() ) );
setRect( QRectF( 0., 0., s.length(), rowGeometry.length() ) );
setIndex( idx );
const Span bs = scene()->itemDelegate()->itemBoundingSpan( getStyleOption(), index() );
//qDebug() << "boundingSpan for" << getStyleOption().text << rect() << "is" << bs;
setBoundingRect( QRectF( bs.start(), 0., bs.length(), rowGeometry.length() ) );
const int maxh = scene()->rowController()->maximumItemHeight();
if ( maxh < rowGeometry.length() ) {
QRectF r = rect();
const Qt::Alignment align = getStyleOption().displayAlignment;
if ( align & Qt::AlignTop ) {
// Do nothing
} else if ( align & Qt::AlignBottom ) {
r.setY( rowGeometry.length()-maxh );
} else {
// Center
r.setY( ( rowGeometry.length()-maxh ) / 2. );
}
r.setHeight( maxh );
setRect( r );
}
//scene()->setSceneRect( scene()->sceneRect().united( mapToScene( boundingRect() ).boundingRect() ) );
//updateConstraintItems();
}
QVariant GraphicsItem::itemChange( GraphicsItemChange change, const QVariant& value )
{
if ( !isUpdating() && change==ItemPositionChange && scene() ) {
QPointF newPos=value.toPointF();
if ( isEditable() ) {
newPos.setY( pos().y() );
return newPos;
} else {
return pos();
}
} else if ( change==QGraphicsItem::ItemSelectedChange ) {
if ( index().isValid() && !( index().model()->flags( index() ) & Qt::ItemIsSelectable ) ) {
// Reject selection attempt
return QVariant::fromValue( false );
}
}
return QGraphicsItem::itemChange( change, value );
}
void GraphicsItem::focusInEvent( QFocusEvent* event )
{
Q_UNUSED( event );
}
void GraphicsItem::updateModel()
{
//qDebug() << "GraphicsItem::updateModel()";
if ( isEditable() ) {
QAbstractItemModel* model = const_cast( index().model() );
#if !defined(NDEBUG)
ConstraintModel* cmodel = scene()->constraintModel();
#endif
assert( model );
assert( cmodel );
if ( model ) {
//ItemType typ = static_cast( model->data( index(),
// ItemTypeRole ).toInt() );
QList constraints;
for ( QList::iterator it1 = m_startConstraints.begin() ;
it1 != m_startConstraints.end() ;
++it1 )
constraints.push_back((*it1)->proxyConstraint());
for ( QList::iterator it2 = m_endConstraints.begin() ;
it2 != m_endConstraints.end() ;
++it2 )
constraints.push_back((*it2)->proxyConstraint());
- if ( scene()->grid()->mapFromChart( Span( scenePos().x(), rect().width() ),
+ if ( scene()->getGrid()->mapFromChart( Span( scenePos().x(), rect().width() ),
index(),
constraints ) ) {
scene()->updateRow( index().parent() );
}
}
}
}
void GraphicsItem::hoverMoveEvent( QGraphicsSceneHoverEvent* event )
{
if ( !isEditable() ) return;
StyleOptionGanttItem opt = getStyleOption();
ItemDelegate::InteractionState istate = scene()->itemDelegate()->interactionStateFor( event->pos(), opt, index() );
switch ( istate ) {
case ItemDelegate::State_ExtendLeft:
#ifndef QT_NO_CURSOR
setCursor( Qt::SizeHorCursor );
#endif
scene()->itemEntered( index() );
break;
case ItemDelegate::State_ExtendRight:
#ifndef QT_NO_CURSOR
setCursor( Qt::SizeHorCursor );
#endif
scene()->itemEntered( index() );
break;
case ItemDelegate::State_Move:
#ifndef QT_NO_CURSOR
setCursor( Qt::SplitHCursor );
#endif
scene()->itemEntered( index() );
break;
default:
#ifndef QT_NO_CURSOR
unsetCursor();
#endif
break;
};
}
void GraphicsItem::hoverLeaveEvent( QGraphicsSceneHoverEvent* )
{
#ifndef QT_NO_CURSOR
unsetCursor();
#endif
}
void GraphicsItem::mousePressEvent( QGraphicsSceneMouseEvent* event )
{
//qDebug() << "GraphicsItem::mousePressEvent("<itemDelegate()->interactionStateFor( event->pos(), opt, index() );
// If State_None is returned by interactionStateFor(), we ignore this event so that
// it can get forwarded to another item that's below this one. Needed, for example,
// to allow items to be moved that are placed below the label of another item.
if ( istate != ItemDelegate::State_None ) {
m_istate = istate;
m_presspos = event->pos();
m_pressscenepos = event->scenePos();
scene()->itemPressed( index(), event );
switch ( m_istate ) {
case ItemDelegate::State_ExtendLeft:
case ItemDelegate::State_ExtendRight:
default: /* State_Move */
if (!(flags() & ItemIsMovable)) {
event->ignore();
}
break;
}
} else {
event->ignore();
}
}
void GraphicsItem::mouseReleaseEvent( QGraphicsSceneMouseEvent* event )
{
//qDebug() << "GraphicsItem::mouseReleaseEvent("<itemClicked( index() );
}
delete m_dragline; m_dragline = nullptr;
if ( scene()->dragSource() ) {
// Create a new constraint
GraphicsItem* other = qgraphicsitem_cast( scene()->itemAt( event->scenePos(), QTransform() ) );
if ( other && scene()->dragSource()!=other &&
other->index().data(KGantt::ItemTypeRole) == KGantt::TypeEvent )
{
// The code below fixes bug KDCH-696.
// Modified the code to add constraint even if the user drags and drops
// constraint on left part of the TypeEvent symbol(i.e diamond symbol)
QRectF itemRect = other->rect().adjusted(-other->rect().height()/2.0, 0, 0, 0 );
if ( other->mapToScene( itemRect ).boundingRect().contains( event->scenePos() ))
{
GraphicsView* view = qobject_cast( event->widget()->parentWidget() );
if ( view ) {
view->addConstraint( scene()->summaryHandlingModel()->mapToSource( scene()->dragSource()->index() ),
scene()->summaryHandlingModel()->mapToSource( other->index() ), event->modifiers() );
}
}
}
else
{
if ( other && scene()->dragSource()!=other &&
other->mapToScene( other->rect() ).boundingRect().contains( event->scenePos() )) {
GraphicsView* view = qobject_cast( event->widget()->parentWidget() );
if ( view ) {
view->addConstraint( scene()->summaryHandlingModel()->mapToSource( scene()->dragSource()->index() ),
scene()->summaryHandlingModel()->mapToSource( other->index() ), event->modifiers() );
}
}
}
scene()->setDragSource( nullptr );
//scene()->update();
} else {
if ( isEditable() ) {
updateItemFromMouse(event->scenePos());
// It is important to set m_presspos to null here because
// when the sceneRect updates because we move the item
// a MouseMoveEvent will be delivered, and we have to
// protect against that
m_presspos = QPointF();
updateModel();
// without this command we sometimes get a white area at the left side of a task item
// after we moved that item right-ways into a grey weekend section of the scene:
scene()->update();
}
}
m_presspos = QPointF();
}
void GraphicsItem::mouseDoubleClickEvent( QGraphicsSceneMouseEvent* event )
{
const int typ = static_cast( index().model()->data( index(), ItemTypeRole ).toInt() );
StyleOptionGanttItem opt = getStyleOption();
ItemDelegate::InteractionState istate = scene()->itemDelegate()->interactionStateFor( event->pos(), opt, index() );
if ( (istate != ItemDelegate::State_None) || (typ == TypeSummary)) {
scene()->itemDoubleClicked( index() );
}
BASE::mouseDoubleClickEvent( event );
}
void GraphicsItem::updateItemFromMouse( const QPointF& scenepos )
{
//qDebug() << "GraphicsItem::updateItemFromMouse("<
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// defines HAVE_PRINTER if support for printing should be included
#ifdef _WIN32_WCE
// There is no printer support under wince even if QT_NO_PRINTER is not set
#else
#ifndef QT_NO_PRINTER
#define HAVE_PRINTER
#endif
#endif
/*!\class KGantt::GraphicsScene
* \internal
*/
using namespace KGantt;
GraphicsScene::Private::Private( GraphicsScene* _q )
: q( _q ),
dragSource( nullptr ),
itemDelegate( new ItemDelegate( _q ) ),
rowController( nullptr ),
grid( &default_grid ),
readOnly( false ),
isPrinting( false ),
drawColumnLabels( true ),
labelsWidth( 0.0 ),
summaryHandlingModel( new SummaryHandlingProxyModel( _q ) ),
selectionModel( nullptr )
{
default_grid.setStartDateTime( QDateTime::currentDateTime().addDays( -1 ) );
}
void GraphicsScene::Private::clearConstraintItems()
{
for(ConstraintGraphicsItem *citem : constraintItems) {
// remove constraint from items first
for(GraphicsItem *item : items) {
item->removeStartConstraint(citem);
item->removeEndConstraint(citem);
}
q->removeItem(citem);
delete citem;
}
constraintItems.clear();
}
void GraphicsScene::Private::resetConstraintItems()
{
clearConstraintItems();
if ( constraintModel.isNull() ) return;
QList clst = constraintModel->constraints();
Q_FOREACH( const Constraint& c, clst ) {
createConstraintItem( c );
}
q->updateItems();
}
void GraphicsScene::Private::createConstraintItem( const Constraint& c )
{
GraphicsItem* sitem = q->findItem( summaryHandlingModel->mapFromSource( c.startIndex() ) );
GraphicsItem* eitem = q->findItem( summaryHandlingModel->mapFromSource( c.endIndex() ) );
if ( sitem && eitem ) {
ConstraintGraphicsItem* citem = new ConstraintGraphicsItem( c );
sitem->addStartConstraint( citem );
eitem->addEndConstraint( citem );
constraintItems.append( citem );
q->addItem( citem );
}
//q->insertConstraintItem( c, citem );
}
// Delete the constraint item, and clean up pointers in the start- and end item
void GraphicsScene::Private::deleteConstraintItem( ConstraintGraphicsItem *citem )
{
//qDebug()<<"GraphicsScene::Private::deleteConstraintItem citem="<constraint();
GraphicsItem* item = items.value( summaryHandlingModel->mapFromSource( c.startIndex() ), nullptr );
if ( item ) {
item->removeStartConstraint( citem );
}
item = items.value( summaryHandlingModel->mapFromSource( c.endIndex() ), nullptr );
if ( item ) {
item->removeEndConstraint( citem );
}
constraintItems.removeAt(constraintItems.indexOf(citem));
delete citem;
}
void GraphicsScene::Private::deleteConstraintItem( const Constraint& c )
{
deleteConstraintItem( findConstraintItem( c ) );
}
ConstraintGraphicsItem* GraphicsScene::Private::findConstraintItem( const Constraint& c ) const
{
GraphicsItem* item = items.value( summaryHandlingModel->mapFromSource( c.startIndex() ), nullptr );
if ( item ) {
const QList clst = item->startConstraints();
QList::const_iterator it = clst.begin();
for ( ; it != clst.end() ; ++it ) {
if ( c.compareIndexes((*it)->constraint()) )
break;
}
if ( it != clst.end() ) {
return *it;
}
}
item = items.value( summaryHandlingModel->mapFromSource( c.endIndex() ), nullptr );
if ( item ) {
const QList clst = item->endConstraints();
QList::const_iterator it = clst.begin();
for ( ; it != clst.end() ; ++it ) {
if ( c.compareIndexes( (*it)->constraint() ) )
break;
}
if ( it != clst.end() ) {
return *it;
}
}
return nullptr;
}
// NOTE: we might get here after indexes are invalidated, so cannot do any controlled cleanup
void GraphicsScene::Private::clearItems()
{
for(GraphicsItem *item : items) {
q->removeItem(item);
delete item;
}
items.clear();
// do last to avoid cleaning up items
clearConstraintItems();
}
+AbstractGrid *GraphicsScene::Private::getGrid()
+{
+ return grid.isNull() ? &default_grid : grid;
+}
+
+const AbstractGrid *GraphicsScene::Private::getGrid() const
+{
+ return grid.isNull() ? &default_grid : grid;
+}
+
GraphicsScene::GraphicsScene( QObject* parent )
: QGraphicsScene( parent ), _d( new Private( this ) )
{
init();
}
GraphicsScene::~GraphicsScene()
{
qDeleteAll( items() );
delete _d;
}
#define d d_func()
void GraphicsScene::init()
{
setItemIndexMethod( QGraphicsScene::NoIndex );
setConstraintModel( new ConstraintModel( this ) );
- connect( d->grid, SIGNAL(gridChanged()), this, SLOT(slotGridChanged()) );
+ connect( d->getGrid(), SIGNAL(gridChanged()), this, SLOT(slotGridChanged()) );
}
/* NOTE: The delegate should really be a property
* of the view, but that doesn't really fit at
* this time
*/
void GraphicsScene::setItemDelegate( ItemDelegate* delegate )
{
if ( !d->itemDelegate.isNull() && d->itemDelegate->parent()==this ) delete d->itemDelegate;
d->itemDelegate = delegate;
update();
}
ItemDelegate* GraphicsScene::itemDelegate() const
{
return d->itemDelegate;
}
QAbstractItemModel* GraphicsScene::model() const
{
assert(!d->summaryHandlingModel.isNull());
return d->summaryHandlingModel->sourceModel();
}
void GraphicsScene::setModel( QAbstractItemModel* model )
{
assert(!d->summaryHandlingModel.isNull());
d->summaryHandlingModel->setSourceModel(model);
- d->grid->setModel( d->summaryHandlingModel );
+ d->getGrid()->setModel( d->summaryHandlingModel );
setSelectionModel( new QItemSelectionModel( model, this ) );
}
QAbstractProxyModel* GraphicsScene::summaryHandlingModel() const
{
return d->summaryHandlingModel;
}
void GraphicsScene::setSummaryHandlingModel( QAbstractProxyModel* proxyModel )
{
proxyModel->setSourceModel( model() );
d->summaryHandlingModel = proxyModel;
}
void GraphicsScene::setRootIndex( const QModelIndex& idx )
{
- d->grid->setRootIndex( idx );
+ d->getGrid()->setRootIndex( idx );
}
QModelIndex GraphicsScene::rootIndex() const
{
- return d->grid->rootIndex();
+ return d->getGrid()->rootIndex();
}
ConstraintModel* GraphicsScene::constraintModel() const
{
return d->constraintModel;
}
void GraphicsScene::setConstraintModel( ConstraintModel* cm )
{
if ( !d->constraintModel.isNull() ) {
d->constraintModel->disconnect( this );
d->clearConstraintItems();
}
d->constraintModel = cm;
connect( cm, SIGNAL(constraintAdded(KGantt::Constraint)),
this, SLOT(slotConstraintAdded(KGantt::Constraint)) );
connect( cm, SIGNAL(constraintRemoved(KGantt::Constraint)),
this, SLOT(slotConstraintRemoved(KGantt::Constraint)) );
d->resetConstraintItems();
}
void GraphicsScene::setSelectionModel( QItemSelectionModel* smodel )
{
if (d->selectionModel) {
d->selectionModel->disconnect( this );
}
d->selectionModel = smodel;
if (smodel) {
connect(d->selectionModel, SIGNAL(modelChanged(QAbstractItemModel*)),
this, SLOT(selectionModelChanged(QAbstractItemModel*)));
connect( smodel, SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),
this, SLOT(slotSelectionChanged(const QItemSelection&,const QItemSelection&)) );
}
}
QItemSelectionModel* GraphicsScene::selectionModel() const
{
return d->selectionModel;
}
void GraphicsScene::setRowController( AbstractRowController* rc )
{
d->rowController = rc;
}
AbstractRowController* GraphicsScene::rowController() const
{
return d->rowController;
}
void GraphicsScene::setGrid( AbstractGrid* grid )
{
QAbstractItemModel* model = nullptr;
- if ( grid == nullptr ) grid = &d->default_grid;
- if ( d->grid ) {
- d->grid->disconnect( this );
- model = d->grid->model();
+ if ( d->getGrid() ) {
+ d->getGrid()->disconnect( this );
+ model = d->getGrid()->model();
}
d->grid = grid;
- connect( d->grid, SIGNAL(gridChanged()), this, SLOT(slotGridChanged()) );
- d->grid->setModel( model );
+ connect( d->getGrid(), SIGNAL(gridChanged()), this, SLOT(slotGridChanged()) );
+ d->getGrid()->setModel( model );
slotGridChanged();
}
+// Returns the explicitly set grid
AbstractGrid* GraphicsScene::grid() const
{
return d->grid;
}
+// May also return the default_grid if a grid has not been set
+const AbstractGrid *GraphicsScene::getGrid() const
+{
+ return d->getGrid();
+}
+
void GraphicsScene::setReadOnly( bool ro )
{
d->readOnly = ro;
}
bool GraphicsScene::isReadOnly() const
{
return d->readOnly;
}
/* Returns the index with column=0 fromt the
* same row as idx and with the same parent.
* This is used to traverse the tree-structure
* of the model
*/
QModelIndex GraphicsScene::mainIndex( const QModelIndex& idx )
{
#if 0
if ( idx.isValid() ) {
return idx.model()->index( idx.row(), 0,idx.parent() );
} else {
return QModelIndex();
}
#else
return idx;
#endif
}
/*! Returns the index pointing to the last column
* in the same row as idx. This can be thought of
* as in "inverse" of mainIndex()
*/
QModelIndex GraphicsScene::dataIndex( const QModelIndex& idx )
{
#if 0
if ( idx.isValid() ) {
const QAbstractItemModel* model = idx.model();
return model->index( idx.row(), model->columnCount( idx.parent() )-1,idx.parent() );
} else {
return QModelIndex();
}
#else
return idx;
#endif
}
/*! Creates a new item of type type.
*/
GraphicsItem* GraphicsScene::createItem( ItemType type ) const
{
assert(views().count() == 1);
GraphicsView *v = qobject_cast(views().first());
assert(v);
return v->createItem(type);
}
void GraphicsScene::Private::recursiveUpdateMultiItem( const Span& span, const QModelIndex& idx )
{
//qDebug() << "recursiveUpdateMultiItem("<findItem( idx );
const int itemtype = summaryHandlingModel->data( idx, ItemTypeRole ).toInt();
if (!item) {
item = q->createItem( static_cast( itemtype ) );
item->setIndex( idx );
q->insertItem( idx, item);
}
item->updateItem( span, idx );
QModelIndex child;
int cr = 0;
while ( ( child = summaryHandlingModel->index( cr, 0, idx ) ).isValid() ) {
recursiveUpdateMultiItem( span, child );
++cr;
}
}
void GraphicsScene::updateRow( const QModelIndex& rowidx )
{
//qDebug() << "GraphicsScene::updateRow("<mapToSource( rowidx );
Span rg = rowController()->rowGeometry( sidx );
for ( QModelIndex treewalkidx = sidx; treewalkidx.isValid(); treewalkidx = treewalkidx.parent() ) {
if ( treewalkidx.data( ItemTypeRole ).toInt() == TypeMulti
&& !rowController()->isRowExpanded( treewalkidx )) {
rg = rowController()->rowGeometry( treewalkidx );
}
}
bool blocked = blockSignals( true );
for ( int col = 0; col < summaryHandlingModel()->columnCount( rowidx.parent() ); ++col ) {
const QModelIndex idx = summaryHandlingModel()->index( rowidx.row(), col, rowidx.parent() );
const QModelIndex sidx = summaryHandlingModel()->mapToSource( idx );
const int itemtype = summaryHandlingModel()->data( idx, ItemTypeRole ).toInt();
const bool isExpanded = rowController()->isRowExpanded( sidx );
if ( itemtype == TypeNone ) {
removeItem( idx );
continue;
}
if ( itemtype == TypeMulti && !isExpanded ) {
d->recursiveUpdateMultiItem( rg, idx );
} else {
if ( summaryHandlingModel()->data( rowidx.parent(), ItemTypeRole ).toInt() == TypeMulti && !isExpanded ) {
//continue;
}
GraphicsItem* item = findItem( idx );
if (!item) {
item = createItem( static_cast( itemtype ) );
item->setIndex( idx );
insertItem(idx, item);
}
const Span span = rowController()->rowGeometry( sidx );
item->updateItem( span, idx );
}
}
blockSignals( blocked );
}
void GraphicsScene::insertItem( const QPersistentModelIndex& idx, GraphicsItem* item )
{
if ( !d->constraintModel.isNull() ) {
// Create items for constraints
const QModelIndex sidx = summaryHandlingModel()->mapToSource( idx );
const QList clst = d->constraintModel->constraintsForIndex( sidx );
Q_FOREACH( const Constraint& c, clst ) {
QModelIndex other_idx;
if ( c.startIndex() == sidx ) {
other_idx = c.endIndex();
GraphicsItem* other_item = d->items.value(summaryHandlingModel()->mapFromSource( other_idx ),nullptr);
if ( !other_item ) continue;
ConstraintGraphicsItem* citem = new ConstraintGraphicsItem( c );
item->addStartConstraint( citem );
other_item->addEndConstraint( citem );
d->constraintItems.append( citem );
addItem( citem );
} else if ( c.endIndex() == sidx ) {
other_idx = c.startIndex();
GraphicsItem* other_item = d->items.value(summaryHandlingModel()->mapFromSource( other_idx ),nullptr);
if ( !other_item ) continue;
ConstraintGraphicsItem* citem = new ConstraintGraphicsItem( c );
other_item->addStartConstraint( citem );
item->addEndConstraint( citem );
d->constraintItems.append( citem );
addItem( citem );
} else {
assert( 0 ); // Impossible
}
}
}
d->items.insert( idx, item );
addItem( item );
}
void GraphicsScene::removeItem( const QModelIndex& idx )
{
//qDebug() << "GraphicsScene::removeItem("<::iterator it = d->items.find( idx );
if ( it != d->items.end() ) {
GraphicsItem* item = *it;
assert( item );
// We have to remove the item from the list first because
// there is a good chance there will be reentrant calls
d->items.erase( it );
{
// Remove any constraintitems attached
const QSet clst = QSet::fromList( item->startConstraints() ) +
QSet::fromList( item->endConstraints() );
Q_FOREACH( ConstraintGraphicsItem* citem, clst ) {
d->deleteConstraintItem( citem );
}
}
// Get rid of the item
delete item;
}
}
GraphicsItem* GraphicsScene::findItem( const QModelIndex& idx ) const
{
if ( !idx.isValid() ) return nullptr;
assert( idx.model() == summaryHandlingModel() );
QHash::const_iterator it = d->items.find( idx );
return ( it != d->items.end() )?*it:nullptr;
}
GraphicsItem* GraphicsScene::findItem( const QPersistentModelIndex& idx ) const
{
if ( !idx.isValid() ) return nullptr;
assert( idx.model() == summaryHandlingModel() );
QHash::const_iterator it = d->items.find( idx );
return ( it != d->items.end() )?*it:nullptr;
}
void GraphicsScene::clearItems()
{
d->clearItems();
}
void GraphicsScene::updateItems()
{
for ( QHash::iterator it = d->items.begin();
it != d->items.end(); ++it ) {
GraphicsItem* const item = it.value();
const QPersistentModelIndex& idx = it.key();
item->updateItem( Span( item->pos().y(), item->rect().height() ), idx );
}
invalidate( QRectF(), QGraphicsScene::BackgroundLayer );
}
void GraphicsScene::deleteSubtree( const QModelIndex& _idx )
{
QModelIndex idx = dataIndex( _idx );
if ( !idx.model() ) return;
const QModelIndex parent( idx.parent() );
const int colcount = idx.model()->columnCount( parent );
{for ( int i = 0; i < colcount; ++i ) {
removeItem( summaryHandlingModel()->index(idx.row(), i, parent ) );
}}
const int rowcount = summaryHandlingModel()->rowCount( _idx );
{for ( int i = 0; i < rowcount; ++i ) {
deleteSubtree( summaryHandlingModel()->index( i, summaryHandlingModel()->columnCount(_idx)-1, _idx ) );
}}
}
ConstraintGraphicsItem* GraphicsScene::findConstraintItem( const Constraint& c ) const
{
return d->findConstraintItem( c );
}
void GraphicsScene::slotConstraintAdded( const KGantt::Constraint& c )
{
d->createConstraintItem( c );
}
void GraphicsScene::slotConstraintRemoved( const KGantt::Constraint& c )
{
d->deleteConstraintItem( c );
}
void GraphicsScene::slotGridChanged()
{
updateItems();
update();
emit gridChanged();
}
void GraphicsScene::selectionModelChanged(QAbstractItemModel *model)
{
Q_UNUSED(model)
}
void GraphicsScene::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
{
for (const QModelIndex &idx : deselected.indexes()) {
GraphicsItem *item = findItem(idx.model() == d->summaryHandlingModel ? idx : d->summaryHandlingModel->mapFromSource(idx));
if (item) {
item->setSelected(false);
}
}
for (const QModelIndex &idx : selected.indexes()) {
GraphicsItem *item = findItem(idx.model() == d->summaryHandlingModel ? idx : d->summaryHandlingModel->mapFromSource(idx));
if (item) {
item->setSelected(true);
}
}
update();
}
void GraphicsScene::helpEvent( QGraphicsSceneHelpEvent *helpEvent )
{
#ifndef QT_NO_TOOLTIP
QGraphicsItem *item = itemAt( helpEvent->scenePos(), QTransform() );
if ( GraphicsItem* gitem = qgraphicsitem_cast( item ) ) {
QToolTip::showText(helpEvent->screenPos(), gitem->ganttToolTip());
} else if ( ConstraintGraphicsItem* citem = qgraphicsitem_cast( item ) ) {
QToolTip::showText(helpEvent->screenPos(), citem->ganttToolTip());
} else {
QGraphicsScene::helpEvent( helpEvent );
}
#endif /* QT_NO_TOOLTIP */
}
void GraphicsScene::drawBackground( QPainter* painter, const QRectF& _rect )
{
QRectF scn( sceneRect() );
QRectF rect( _rect );
if ( d->isPrinting && d->drawColumnLabels ) {
QRectF headerRect( scn.topLeft()+QPointF( d->labelsWidth, 0 ),
QSizeF( scn.width()-d->labelsWidth, d->rowController->headerHeight() ));
- d->grid->paintHeader( painter, headerRect, rect, 0, nullptr );
+ d->getGrid()->paintHeader( painter, headerRect, rect, 0, nullptr );
#if 0
/* We have to blank out the part of the header that is invisible during
* normal rendering when we are printing.
*/
QRectF labelsTabRect( scn.topLeft(), QSizeF( d->labelsWidth, headerRect.height() ) );
QStyleOptionHeader opt;
opt.rect = labelsTabRect.toRect();
opt.text = QLatin1String("");
opt.textAlignment = Qt::AlignCenter;
style()->drawControl(QStyle::CE_Header, &opt, painter, 0);
#endif
scn.setTop( headerRect.bottom() );
scn.setLeft( headerRect.left() );
rect = rect.intersected( scn );
}
- d->grid->paintGrid( painter, scn, rect, d->rowController );
+ d->getGrid()->paintGrid( painter, scn, rect, d->rowController );
- d->grid->drawBackground(painter, rect);
+ d->getGrid()->drawBackground(painter, rect);
}
void GraphicsScene::drawForeground( QPainter* painter, const QRectF& rect )
{
- d->grid->drawForeground(painter, rect);
+ d->getGrid()->drawForeground(painter, rect);
}
void GraphicsScene::itemEntered( const QModelIndex& idx )
{
emit entered( idx );
}
void GraphicsScene::itemPressed( const QModelIndex& idx, QGraphicsSceneMouseEvent *event )
{
if (event->button() == Qt::LeftButton) {
QItemSelectionModel::SelectionFlags flags;
if (event->modifiers() & Qt::ControlModifier) {
flags |= QItemSelectionModel::Toggle;
} else {
flags |= QItemSelectionModel::ClearAndSelect;
}
d->selectionModel->select(idx, flags);
}
emit pressed( idx );
}
void GraphicsScene::itemClicked( const QModelIndex& idx )
{
emit clicked( idx );
}
void GraphicsScene::itemDoubleClicked( const QModelIndex& idx )
{
emit qrealClicked( idx );
}
void GraphicsScene::setDragSource( GraphicsItem* item )
{
d->dragSource = item;
}
GraphicsItem* GraphicsScene::dragSource() const
{
return d->dragSource;
}
/*! Print the Gantt chart using \a printer. If \a drawRowLabels
* is true (the default), each row will have it's label printed
* on the left side. If \a drawColumnLabels is true (the
* default), each column will have it's label printed at the
* top side.
*
* This version of print() will print multiple pages.
*/
void GraphicsScene::print( QPrinter* printer, bool drawRowLabels, bool drawColumnLabels )
{
#ifndef HAVE_PRINTER
Q_UNUSED( printer );
Q_UNUSED( drawRowLabels );
Q_UNUSED( drawColumnLabels );
#else
QPainter painter( printer );
doPrint( &painter, printer->pageRect(), sceneRect().left(), sceneRect().right(), printer, drawRowLabels, drawColumnLabels );
#endif
}
/*! Print part of the Gantt chart from \a start to \a end using \a printer.
* If \a drawRowLabels is true (the default), each row will have it's
* label printed on the left side. If \a drawColumnLabels is true (the
* default), each column will have it's label printed at the
* top side.
*
* This version of print() will print multiple pages.
*
* To print a certain range of a chart with a DateTimeGrid, use
* qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const
* to figure out the values for \a start and \a end.
*/
void GraphicsScene::print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels )
{
#ifndef HAVE_PRINTER
Q_UNUSED( printer );
Q_UNUSED( start );
Q_UNUSED( end );
Q_UNUSED( drawRowLabels );
Q_UNUSED( drawColumnLabels );
#else
QPainter painter( printer );
doPrint( &painter, printer->pageRect(), start, end, printer, drawRowLabels, drawColumnLabels );
#endif
}
/*! Render the GanttView inside the rectangle \a target using the painter \a painter.
* If \a drawRowLabels is true (the default), each row will have it's
* label printed on the left side. If \a drawColumnLabels is true (the
* default), each column will have it's label printed at the
* top side.
*/
void GraphicsScene::print( QPainter* painter, const QRectF& _targetRect, bool drawRowLabels, bool drawColumnLabels )
{
QRectF targetRect( _targetRect );
if ( targetRect.isNull() ) {
targetRect = sceneRect();
}
doPrint( painter, targetRect, sceneRect().left(), sceneRect().right(), nullptr, drawRowLabels, drawColumnLabels );
}
/*! Render the GanttView inside the rectangle \a target using the painter \a painter.
* If \a drawRowLabels is true (the default), each row will have it's
* label printed on the left side. If \a drawColumnLabels is true (the
* default), each column will have it's label printed at the
* top side.
*
* To print a certain range of a chart with a DateTimeGrid, use
* qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const
* to figure out the values for \a start and \a end.
*/
void GraphicsScene::print( QPainter* painter, qreal start, qreal end,
const QRectF& _targetRect, bool drawRowLabels, bool drawColumnLabels )
{
QRectF targetRect( _targetRect );
if ( targetRect.isNull() ) {
targetRect = sceneRect();
}
doPrint( painter, targetRect, start, end, nullptr, drawRowLabels, drawColumnLabels );
}
/*!\internal
*/
void GraphicsScene::doPrint( QPainter* painter, const QRectF& targetRect,
qreal start, qreal end,
QPrinter* printer, bool drawRowLabels, bool drawColumnLabels )
{
assert( painter );
d->isPrinting = true;
d->drawColumnLabels = drawColumnLabels;
d->labelsWidth = 0.0;
QFont sceneFont( font() );
#ifdef HAVE_PRINTER
if ( printer ) {
sceneFont = QFont( font(), printer );
if ( font().pointSizeF() >= 0.0 )
sceneFont.setPointSizeF( font().pointSizeF() );
else if ( font().pointSize() >= 0 )
sceneFont.setPointSize( font().pointSize() );
else
sceneFont.setPixelSize( font().pixelSize() );
}
#endif
QGraphicsTextItem dummyTextItem( QLatin1String("X") );
dummyTextItem.adjustSize();
QFontMetrics fm(dummyTextItem.font());
sceneFont.setPixelSize( fm.height() );
const QRectF oldScnRect( sceneRect() );
QRectF scnRect( oldScnRect );
scnRect.setLeft( start );
scnRect.setRight( end );
bool b = blockSignals( true );
/* column labels */
if ( d->drawColumnLabels ) {
QRectF headerRect( scnRect );
headerRect.setHeight( - d->rowController->headerHeight() );
scnRect.setTop(scnRect.top() - d->rowController->headerHeight());
}
/* row labels */
QVector textLabels;
if ( drawRowLabels ) {
qreal textWidth = 0.;
qreal charWidth = QFontMetricsF(sceneFont).boundingRect( QString::fromLatin1( "X" ) ).width();
QModelIndex sidx = summaryHandlingModel()->mapToSource( summaryHandlingModel()->index( 0, 0, rootIndex()) );
do {
QModelIndex idx = summaryHandlingModel()->mapFromSource( sidx );
const Span rg=rowController()->rowGeometry( sidx );
const QString txt = idx.data( Qt::DisplayRole ).toString();
QGraphicsTextItem* item = new QGraphicsTextItem( txt );
addItem( item );
textLabels << item;
item->setTextWidth( QFontMetricsF(sceneFont).boundingRect( txt ).width() + charWidth );
textWidth = qMax( item->textWidth(), textWidth );
item->setPos( 0, rg.start() );
} while ( ( sidx = rowController()->indexBelow( sidx ) ).isValid() );
Q_FOREACH( QGraphicsTextItem* item, textLabels ) {
item->setPos( scnRect.left()-textWidth, item->y() );
item->show();
}
scnRect.setLeft( scnRect.left()-textWidth );
d->labelsWidth = textWidth;
}
setSceneRect( scnRect );
painter->save();
painter->setClipRect( targetRect );
qreal yratio = targetRect.height()/scnRect.height();
/* If we're not printing multiple pages,
* check if the span fits and adjust:
*/
if ( !printer && targetRect.width()/scnRect.width() < yratio ) {
yratio = targetRect.width()/scnRect.width();
}
qreal offset = scnRect.left();
int pagecount = 0;
while ( offset < scnRect.right() ) {
painter->setFont( sceneFont );
render( painter, targetRect, QRectF( QPointF( offset, scnRect.top()),
QSizeF( targetRect.width()/yratio, scnRect.height() ) ) );
offset += targetRect.width()/yratio;
++pagecount;
if ( printer && offset < scnRect.right() ) {
#ifdef HAVE_PRINTER
printer->newPage();
#endif
} else {
break;
}
}
d->isPrinting = false;
d->drawColumnLabels = true;
d->labelsWidth = 0.0;
qDeleteAll( textLabels );
blockSignals( b );
setSceneRect( oldScnRect );
painter->restore();
}
#include "moc_kganttgraphicsscene.cpp"
#ifndef KDAB_NO_UNIT_TESTS
#include "unittest/test.h"
#include
#include
#include
#include "kganttgraphicsview.h"
class SceneTestRowController : public KGantt::AbstractRowController {
private:
static const int ROW_HEIGHT;
QPointer m_model;
public:
SceneTestRowController()
{
}
void setModel( QAbstractItemModel* model )
{
m_model = model;
}
/*reimp*/int headerHeight() const override { return 40; }
/*reimp*/ bool isRowVisible( const QModelIndex& ) const override { return true;}
/*reimp*/ bool isRowExpanded( const QModelIndex& ) const override { return false; }
/*reimp*/ KGantt::Span rowGeometry( const QModelIndex& idx ) const override
{
return KGantt::Span( idx.row() * ROW_HEIGHT, ROW_HEIGHT );
}
/*reimp*/ int maximumItemHeight() const override {
return ROW_HEIGHT/2;
}
/*reimp*/int totalHeight() const override {
return m_model->rowCount()* ROW_HEIGHT;
}
/*reimp*/ QModelIndex indexAt( int height ) const override {
return m_model->index( height/ROW_HEIGHT, 0 );
}
/*reimp*/ QModelIndex indexBelow( const QModelIndex& idx ) const override {
if ( !idx.isValid() )return QModelIndex();
return idx.model()->index( idx.row()+1, idx.column(), idx.parent() );
}
/*reimp*/ QModelIndex indexAbove( const QModelIndex& idx ) const override {
if ( !idx.isValid() )return QModelIndex();
return idx.model()->index( idx.row()-1, idx.column(), idx.parent() );
}
};
class TestLineItem : public QGraphicsLineItem
{
public:
TestLineItem( bool *destroyedFlag )
: QGraphicsLineItem( 0, 0, 10, 10 ), // geometry doesn't matter
m_destroyedFlag( destroyedFlag )
{}
~TestLineItem()
{ *m_destroyedFlag = true; }
private:
bool *m_destroyedFlag;
};
const int SceneTestRowController::ROW_HEIGHT = 30;
KDAB_SCOPED_UNITTEST_SIMPLE( KGantt, GraphicsView, "test" ) {
QStandardItemModel model;
QStandardItem* item = new QStandardItem();
item->setData( KGantt::TypeTask, KGantt::ItemTypeRole );
item->setData( QString::fromLatin1( "Decide on new product" ) );
item->setData( QDateTime( QDate( 2007, 3, 1 ) ), KGantt::StartTimeRole );
item->setData( QDateTime( QDate( 2007, 3, 3 ) ), KGantt::EndTimeRole );
QStandardItem* item2 = new QStandardItem();
item2->setData( KGantt::TypeTask, KGantt::ItemTypeRole );
item2->setData( QString::fromLatin1( "Educate personnel" ) );
item2->setData( QDateTime( QDate( 2007, 3, 3 ) ), KGantt::StartTimeRole );
item2->setData( QDateTime( QDate( 2007, 3, 6 ) ), KGantt::EndTimeRole );
model.appendRow( item );
model.appendRow( item2 );
SceneTestRowController rowController;
rowController.setModel( &model );
KGantt::GraphicsView graphicsView;
graphicsView.setRowController( &rowController );
graphicsView.setModel( &model );
// Now the interesting stuff - the items above are just for a "realistic environment"
bool foreignItemDestroyed = false;
TestLineItem *foreignItem = new TestLineItem( &foreignItemDestroyed );
graphicsView.scene()->addItem( foreignItem );
assertFalse( foreignItemDestroyed );
graphicsView.updateScene();
assertFalse( foreignItemDestroyed );
}
#endif /* KDAB_NO_UNIT_TESTS */
diff --git a/src/KGantt/kganttgraphicsscene.h b/src/KGantt/kganttgraphicsscene.h
index decf068..568dec1 100644
--- a/src/KGantt/kganttgraphicsscene.h
+++ b/src/KGantt/kganttgraphicsscene.h
@@ -1,144 +1,145 @@
/*
* Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
*
* This file is part of the KGantt library.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef KGANTTGRAPHICSSCENE_H
#define KGANTTGRAPHICSSCENE_H
#include
#include
#include
#include
#include "kganttglobal.h"
QT_BEGIN_NAMESPACE
class QAbstractProxyModel;
class QItemSelectionModel;
class QItemSelection;
class QPrinter;
class QAbstractItemModel;
class QGraphicsSceneMouseEvent;
QT_END_NAMESPACE
namespace KGantt {
class AbstractGrid;
class AbstractRowController;
class GraphicsItem;
class Constraint;
class ConstraintModel;
class ConstraintGraphicsItem;
class ItemDelegate;
class KGANTT_EXPORT GraphicsScene : public QGraphicsScene {
Q_OBJECT
KGANTT_DECLARE_PRIVATE_BASE_POLYMORPHIC( GraphicsScene )
public:
explicit GraphicsScene( QObject* parent = nullptr );
virtual ~GraphicsScene();
//qreal dateTimeToSceneX( const QDateTime& dt ) const;
//QDateTime sceneXtoDateTime( qreal x ) const;
static QModelIndex mainIndex( const QModelIndex& idx );
static QModelIndex dataIndex( const QModelIndex& idx );
QAbstractItemModel* model() const;
QAbstractProxyModel* summaryHandlingModel() const;
QModelIndex rootIndex() const;
ConstraintModel* constraintModel() const;
QItemSelectionModel* selectionModel() const;
void insertItem( const QPersistentModelIndex&, GraphicsItem* );
void removeItem( const QModelIndex& );
using QGraphicsScene::removeItem;
GraphicsItem* findItem( const QModelIndex& ) const;
GraphicsItem* findItem( const QPersistentModelIndex& ) const;
void updateItems();
void clearItems();
void deleteSubtree( const QModelIndex& );
ConstraintGraphicsItem* findConstraintItem( const Constraint& ) const;
QList findConstraintItems( const QModelIndex& idx ) const;
void setItemDelegate( ItemDelegate* );
ItemDelegate* itemDelegate() const;
void setRowController( AbstractRowController* rc );
AbstractRowController* rowController() const;
void setGrid( AbstractGrid* grid );
AbstractGrid* grid() const;
+ const AbstractGrid *getGrid() const;
bool isReadOnly() const;
void updateRow( const QModelIndex& idx );
GraphicsItem* createItem( ItemType type ) const;
/* used by GraphicsItem */
void itemEntered( const QModelIndex& );
void itemPressed( const QModelIndex& idx, QGraphicsSceneMouseEvent *event );
void itemClicked( const QModelIndex& );
void itemDoubleClicked( const QModelIndex& );
void setDragSource( GraphicsItem* item );
GraphicsItem* dragSource() const;
/* Printing */
void print( QPrinter* printer, bool drawRowLabels = true, bool drawColumnLabels = true );
void print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels = true, bool drawColumnLabels = true );
void print( QPainter* painter, const QRectF& target = QRectF(), bool drawRowLabels=true, bool drawColumnLabels = true );
void print( QPainter* painter, qreal start, qreal end, const QRectF& target = QRectF(), bool drawRowLabels=true, bool drawColumnLabels = true );
Q_SIGNALS:
void gridChanged();
void clicked( const QModelIndex & index );
void qrealClicked( const QModelIndex & index );
void entered( const QModelIndex & index );
void pressed( const QModelIndex & index );
protected:
/*reimp*/ void helpEvent( QGraphicsSceneHelpEvent *helpEvent ) override;
/*reimp*/ void drawBackground( QPainter* painter, const QRectF& rect ) override;
/*reimp*/ void drawForeground( QPainter* painter, const QRectF& rect ) override;
public Q_SLOTS:
void setModel( QAbstractItemModel* );
void setSummaryHandlingModel( QAbstractProxyModel* );
void setConstraintModel( ConstraintModel* );
void setRootIndex( const QModelIndex& idx );
void setSelectionModel( QItemSelectionModel* selectionmodel );
void setReadOnly( bool );
private Q_SLOTS:
/* slots for ConstraintModel */
void slotConstraintAdded( const KGantt::Constraint& );
void slotConstraintRemoved( const KGantt::Constraint& );
void slotGridChanged();
void slotSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
void selectionModelChanged(QAbstractItemModel *);
private:
void doPrint( QPainter* painter, const QRectF& targetRect,
qreal start, qreal end,
QPrinter* printer, bool drawRowLabels, bool drawColumnLabels );
};
}
#endif /* KGANTTGRAPHICSSCENE_H */
diff --git a/src/KGantt/kganttgraphicsscene_p.h b/src/KGantt/kganttgraphicsscene_p.h
index d7e168f..f84c97e 100644
--- a/src/KGantt/kganttgraphicsscene_p.h
+++ b/src/KGantt/kganttgraphicsscene_p.h
@@ -1,80 +1,84 @@
/*
* Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
*
* This file is part of the KGantt library.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef KGANTTGRAPHICSSCENE_P_H
#define KGANTTGRAPHICSSCENE_P_H
#include
#include
#include
#include
#include
#include "kganttgraphicsscene.h"
#include "kganttconstraintmodel.h"
#include "kganttdatetimegrid.h"
namespace KGantt {
+ class AbstractGrid;
+
class Q_DECL_HIDDEN GraphicsScene::Private {
public:
explicit Private(GraphicsScene*);
void clearConstraintItems();
void resetConstraintItems();
void createConstraintItem( const Constraint& c );
void deleteConstraintItem( ConstraintGraphicsItem* citem );
void deleteConstraintItem( const Constraint& c );
ConstraintGraphicsItem* findConstraintItem( const Constraint& c ) const;
void recursiveUpdateMultiItem( const Span& span, const QModelIndex& idx );
void clearItems();
+ AbstractGrid *getGrid();
+ const AbstractGrid *getGrid() const;
GraphicsScene* q;
QHash items;
QList constraintItems;
GraphicsItem* dragSource;
QPointer itemDelegate;
AbstractRowController* rowController;
DateTimeGrid default_grid;
QPointer grid;
bool readOnly;
/* printing related members */
bool isPrinting;
bool drawColumnLabels;
qreal labelsWidth;
QPointer summaryHandlingModel;
QPointer constraintModel;
QPointer selectionModel;
};
GraphicsScene::GraphicsScene( GraphicsScene::Private* d ) : _d( d )
{
init();
}
}
#endif /* KGANTTGRAPHICSSCENE_P_H */
diff --git a/src/KGantt/kganttgraphicsview.cpp b/src/KGantt/kganttgraphicsview.cpp
index 80e6716..24aa8da 100644
--- a/src/KGantt/kganttgraphicsview.cpp
+++ b/src/KGantt/kganttgraphicsview.cpp
@@ -1,813 +1,824 @@
/*
* Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
*
* This file is part of the KGantt library.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "kganttgraphicsview.h"
#include "kganttgraphicsview_p.h"
#include "kganttabstractrowcontroller.h"
#include "kganttgraphicsitem.h"
#include "kganttconstraintmodel.h"
#include "kganttdatetimetimelinedialog.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if defined KDAB_EVAL
#include "../evaldialog/evaldialog.h"
#endif
/*!\class KGantt::HeaderWidget
* \internal
*/
using namespace KGantt;
HeaderWidget::HeaderWidget( GraphicsView* parent )
: QWidget( parent ), m_offset( 0. )
{
assert( parent ); // Parent must be set
}
HeaderWidget::~HeaderWidget()
{
}
void HeaderWidget::scrollTo( int v )
{
m_offset = v;
// QWidget::scroll() wont work properly for me on Mac
//scroll( static_cast( old-v ), 0 );
update();
}
void HeaderWidget::paintEvent( QPaintEvent* ev )
{
QPainter p( this );
view()->grid()->paintHeader( &p, rect(), ev->rect(), m_offset, this );
}
bool HeaderWidget::event( QEvent* event )
{
if ( event->type() == QEvent::ToolTip ) {
DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() );
if ( grid ) {
QHelpEvent *e = static_cast( event );
QDateTime dt = grid->mapFromChart( view()->mapToScene( e->x(), 0 ).x() ).toDateTime();
setToolTip( dt.toString() );
}
}
return QWidget::event( event );
}
void HeaderWidget::contextMenuEvent( QContextMenuEvent* event )
{
QMenu contextMenu;
DateTimeGrid* const grid = qobject_cast< DateTimeGrid* >( view()->grid() );
QAction* actionScaleAuto = nullptr;
QAction* actionScaleMonth = nullptr;
QAction* actionScaleWeek = nullptr;
QAction* actionScaleDay = nullptr;
QAction* actionScaleHour = nullptr;
QAction* actionZoomIn = nullptr;
QAction* actionZoomOut = nullptr;
QAction* actionTimeline = nullptr;
if ( grid != nullptr )
{
QMenu* menuScale = new QMenu( tr( "Scale", "@title:menu" ), &contextMenu );
QActionGroup* scaleGroup = new QActionGroup( &contextMenu );
scaleGroup->setExclusive( true );
actionScaleAuto = new QAction( tr( "Auto", "@item:inmenu Automatic scale" ), menuScale );
actionScaleAuto->setCheckable( true );
actionScaleAuto->setChecked( grid->scale() == DateTimeGrid::ScaleAuto );
actionScaleMonth = new QAction( tr( "Month", "@item:inmenu" ), menuScale );
actionScaleMonth->setCheckable( true );
actionScaleMonth->setChecked( grid->scale() == DateTimeGrid::ScaleMonth );
actionScaleWeek = new QAction( tr( "Week", "@item:inmenu" ), menuScale );
actionScaleWeek->setCheckable( true );
actionScaleWeek->setChecked( grid->scale() == DateTimeGrid::ScaleWeek );
actionScaleDay = new QAction( tr( "Day", "@item:inmenu" ), menuScale );
actionScaleDay->setCheckable( true );
actionScaleDay->setChecked( grid->scale() == DateTimeGrid::ScaleDay );
actionScaleHour = new QAction( tr( "Hour", "@item:inmenu" ), menuScale );
actionScaleHour->setCheckable( true );
actionScaleHour->setChecked( grid->scale() == DateTimeGrid::ScaleHour );
scaleGroup->addAction( actionScaleAuto );
menuScale->addAction( actionScaleAuto );
scaleGroup->addAction( actionScaleMonth );
menuScale->addAction( actionScaleMonth );
scaleGroup->addAction( actionScaleWeek );
menuScale->addAction( actionScaleWeek );
scaleGroup->addAction( actionScaleDay );
menuScale->addAction( actionScaleDay );
scaleGroup->addAction( actionScaleHour );
menuScale->addAction( actionScaleHour );
contextMenu.addMenu( menuScale );
contextMenu.addSeparator();
actionZoomIn = new QAction( tr( "Zoom In", "@action:inmenu" ), &contextMenu );
contextMenu.addAction( actionZoomIn );
actionZoomOut = new QAction( tr( "Zoom Out", "@action:inmenu" ), &contextMenu );
contextMenu.addAction( actionZoomOut );
contextMenu.addSeparator();
actionTimeline = new QAction( tr( "Timeline...", "@action:inmenu" ), &contextMenu );
contextMenu.addAction( actionTimeline );
}
if ( contextMenu.isEmpty() )
{
event->ignore();
return;
}
const QAction* const action = contextMenu.exec( event->globalPos() );
if ( action == nullptr ) {}
else if ( action == actionScaleAuto )
{
assert( grid != nullptr );
grid->setScale( DateTimeGrid::ScaleAuto );
}
else if ( action == actionScaleMonth )
{
assert( grid != nullptr );
grid->setScale( DateTimeGrid::ScaleMonth );
}
else if ( action == actionScaleWeek )
{
assert( grid != nullptr );
grid->setScale( DateTimeGrid::ScaleWeek );
}
else if ( action == actionScaleDay )
{
assert( grid != nullptr );
grid->setScale( DateTimeGrid::ScaleDay );
}
else if ( action == actionScaleHour )
{
assert( grid != nullptr );
grid->setScale( DateTimeGrid::ScaleHour );
}
else if ( action == actionZoomIn )
{
assert( grid != nullptr );
grid->setDayWidth( grid->dayWidth() * 1.25 );
}
else if ( action == actionZoomOut )
{
assert( grid != nullptr );
// daywidth *MUST NOT* go below 1.0, it is used as an integer later on
grid->setDayWidth( qMax( 1.0, grid->dayWidth() * 0.8 ) );
}
else if ( action == actionTimeline )
{
assert( grid != nullptr );
DateTimeTimeLineDialog dlg(grid->timeLine());
dlg.exec();
}
event->accept();
}
GraphicsView::Private::Private( GraphicsView* _q )
: q( _q ), rowcontroller(nullptr), headerwidget( _q )
{
}
void GraphicsView::Private::updateHeaderGeometry()
{
q->setViewportMargins(0,rowcontroller->headerHeight(),0,0);
headerwidget.setGeometry( q->viewport()->x(),
q->viewport()->y() - rowcontroller->headerHeight(),
q->viewport()->width(),
rowcontroller->headerHeight() );
}
void GraphicsView::Private::slotGridChanged()
{
updateHeaderGeometry();
headerwidget.update();
q->updateSceneRect();
q->update();
}
void GraphicsView::Private::slotHorizontalScrollValueChanged( int val )
{
const QRectF viewRect = q->transform().mapRect( q->sceneRect() );
headerwidget.scrollTo( val-q->horizontalScrollBar()->minimum()+static_cast( viewRect.left() ) );
}
void GraphicsView::Private::slotColumnsInserted( const QModelIndex& parent, int start, int end )
{
Q_UNUSED( start );
Q_UNUSED( end );
QModelIndex idx = scene.model()->index( 0, 0, scene.summaryHandlingModel()->mapToSource( parent ) );
do {
scene.updateRow( scene.summaryHandlingModel()->mapFromSource( idx ) );
} while ( ( idx = rowcontroller->indexBelow( idx ) ) != QModelIndex() && rowcontroller->isRowVisible( idx ) );
//} while ( ( idx = d->treeview.indexBelow( idx ) ) != QModelIndex() && d->treeview.visualRect(idx).isValid() );
q->updateSceneRect();
}
void GraphicsView::Private::slotColumnsRemoved( const QModelIndex& parent, int start, int end )
{
// TODO
Q_UNUSED( start );
Q_UNUSED( end );
Q_UNUSED( parent );
q->updateScene();
}
void GraphicsView::Private::slotDataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight )
{
//qDebug() << "GraphicsView::slotDataChanged("<index( row, 0, parent ) );
}
}
void GraphicsView::Private::slotLayoutChanged()
{
//qDebug() << "slotLayoutChanged()";
q->updateScene();
}
void GraphicsView::Private::slotModelReset()
{
//qDebug() << "slotModelReset()";
q->updateScene();
}
void GraphicsView::Private::slotRowsInserted( const QModelIndex& parent, int start, int end )
{
Q_UNUSED( parent );
Q_UNUSED( start );
Q_UNUSED( end );
q->updateScene(); // TODO: This might be optimised
}
void GraphicsView::Private::removeConstraintsRecursive( QAbstractProxyModel *summaryModel, const QModelIndex& index )
{
if ( summaryModel->hasChildren( index ) ) {
//qDebug() << "removing constraints from children of"<rowCount( index ); ++row ) {
const QModelIndex child = summaryModel->index( row, index.column(), index );
removeConstraintsRecursive( summaryModel, child );
}
}
//qDebug() << "removing constraints from"<sourceModel()
QList clst = scene.constraintModel()->constraintsForIndex( summaryModel->mapToSource( index ) );
Q_FOREACH( Constraint c, clst ) {
scene.constraintModel()->removeConstraint( c );
}
}
void GraphicsView::Private::slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end )
{
//qDebug() << "GraphicsView::Private::slotRowsAboutToBeRemoved("<columnCount( parent ); ++col ) {
const QModelIndex idx = summaryModel->index( row, col, parent );
removeConstraintsRecursive( summaryModel, idx );
scene.removeItem( idx );
}
}
}
void GraphicsView::Private::slotRowsRemoved( const QModelIndex& parent, int start, int end )
{
//qDebug() << "GraphicsView::Private::slotRowsRemoved("<updateScene();
}
void GraphicsView::Private::slotItemClicked( const QModelIndex& idx )
{
QModelIndex sidx = idx;//scene.summaryHandlingModel()->mapToSource( idx );
emit q->clicked( sidx );
if (q->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, q))
emit q->activated( sidx );
}
void GraphicsView::Private::slotItemDoubleClicked( const QModelIndex& idx )
{
QModelIndex sidx = idx;//scene.summaryHandlingModel()->mapToSource( idx );
emit q->qrealClicked( sidx );
if (!q->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, q))
emit q->activated( sidx );
}
void GraphicsView::Private::slotHeaderContextMenuRequested( const QPoint& pt )
{
emit q->headerContextMenuRequested( headerwidget.mapToGlobal( pt ) );
}
/*!\class KGantt::GraphicsView kganttgraphicsview.h KGanttGraphicsView
* \ingroup KGantt
* \brief The GraphicsView class provides a model/view implementation of a gantt chart.
*
*
*/
/*! \fn void GraphicsView::activated( const QModelIndex & index ) */
/*! \fn void GraphicsView::clicked( const QModelIndex & index ); */
/*! \fn void GraphicsView::qrealClicked( const QModelIndex & index ); */
/*! \fn void GraphicsView::entered( const QModelIndex & index ); */
/*! \fn void GraphicsView::pressed( const QModelIndex & index ); */
/*! \fn void GraphicsView::headerContextMenuRequested( const QPoint& pt )
* This signal is emitted when the header has contextMenuPolicy Qt::CustomContextMenu
* and the widget wants to show a context menu for the header. Unlike in
* QWidget::customContextMenuRequested() signal, \a pt is here in global coordinates.
*/
/*! Constructor. Creates a new KGantt::GraphicsView with parent
* \a parent.
*/
GraphicsView::GraphicsView( QWidget* parent )
: QGraphicsView( parent ), _d( new Private( this ) )
{
#if defined KDAB_EVAL
EvalDialog::checkEvalLicense( "KD Gantt" );
#endif
connect( horizontalScrollBar(), SIGNAL(valueChanged(int)),
this, SLOT(slotHorizontalScrollValueChanged(int)) );
connect( &_d->scene, SIGNAL(gridChanged()),
this, SLOT(slotGridChanged()) );
connect( &_d->scene, SIGNAL(entered(QModelIndex)),
this, SIGNAL(entered(QModelIndex)) );
connect( &_d->scene, SIGNAL(pressed(QModelIndex)),
this, SIGNAL(pressed(QModelIndex)) );
connect( &_d->scene, SIGNAL(clicked(QModelIndex)),
this, SLOT(slotItemClicked(QModelIndex)) );
connect( &_d->scene, SIGNAL(qrealClicked(QModelIndex)),
this, SLOT(slotItemDoubleClicked(QModelIndex)) );
connect( &_d->scene, SIGNAL(sceneRectChanged(QRectF)),
this, SLOT(updateSceneRect()) );
connect( &_d->headerwidget, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(slotHeaderContextMenuRequested(QPoint)) );
setScene( &_d->scene );
// HACK!
setSummaryHandlingModel( _d->scene.summaryHandlingModel() );
// So that AbstractGrid::drawBackground() and AbstractGrid::drawForeground()
// works properly
setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
//setCacheMode( CacheBackground );
}
/*! Destroys this view. */
GraphicsView::~GraphicsView()
{
delete _d;
}
#define d d_func()
/*! Sets the model to be displayed in this view to
* \a model. The view does not take ownership of the model.
*
* To make a model work well with GraphicsView it must
* have a certain layout. Whether the model is flat or has a
* treestrucure is not important, as long as an
* AbstractRowController is provided that can navigate the
* model.
*
* GraphicsView operates per row in the model. The data is always
* taken from the _last_ item in the row. The ItemRoles used are
* Qt::DisplayRole and the roles defined in KGantt::ItemDataRole.
*
* Note: This model is not returned by \a model()
*
* \see GraphicsView::model
*/
void GraphicsView::setModel( QAbstractItemModel* model )
{
if ( d->scene.model() ) {
disconnect( d->scene.model() );
}
d->scene.setModel( model );
if (model) {
connect( model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(updateSceneRect()) );
}
updateScene();
}
/*! \returns the current model displayed by this view
*
* Note: The returned model is not the model set with \a setModel()
*
* \see GraphicsView::setModel
*/
QAbstractItemModel* GraphicsView::model() const
{
return d->scene.model();
}
void GraphicsView::setSummaryHandlingModel( QAbstractProxyModel* proxyModel )
{
disconnect( d->scene.summaryHandlingModel() );
d->scene.setSummaryHandlingModel( proxyModel );
/* Connections. We have to rely on the treeview
* to receive the signals before we do(!)
*/
connect( proxyModel, SIGNAL(columnsInserted(QModelIndex,int,int)),
this, SLOT(slotColumnsInserted(QModelIndex,int,int)) );
connect( proxyModel, SIGNAL(columnsRemoved(QModelIndex,int,int)),
this, SLOT(slotColumnsRemoved(QModelIndex,int,int)) );
connect( proxyModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(slotDataChanged(QModelIndex,QModelIndex)) );
connect( proxyModel, SIGNAL(layoutChanged()),
this, SLOT(slotLayoutChanged()) );
connect( proxyModel, SIGNAL(modelReset()),
this, SLOT(slotModelReset()) );
connect( proxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
this, SLOT(slotRowsInserted(QModelIndex,int,int)) );
connect( proxyModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
this, SLOT(slotRowsAboutToBeRemoved(QModelIndex,int,int)) );
connect( proxyModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
this, SLOT(slotRowsRemoved(QModelIndex,int,int)) );
updateScene();
}
/*! Sets the constraintmodel displayed by this view.
* \see KGantt::ConstraintModel.
*/
void GraphicsView::setConstraintModel( ConstraintModel* cmodel )
{
d->scene.setConstraintModel( cmodel );
}
/*! \returns the KGantt::ConstraintModel displayed by this view.
*/
ConstraintModel* GraphicsView::constraintModel() const
{
return d->scene.constraintModel();
}
/*! \returns the KGantt::SummaryHandlingProxyModel used by this view.
*/
QAbstractProxyModel* GraphicsView::summaryHandlingModel() const
{
return d->scene.summaryHandlingModel();
}
/*! Sets the root index of the model displayed by this view.
* Similar to QAbstractItemView::setRootIndex, default is QModelIndex().
*/
void GraphicsView::setRootIndex( const QModelIndex& idx )
{
d->scene.setRootIndex( idx );
}
/*! \returns the rootindex for this view.
*/
QModelIndex GraphicsView::rootIndex() const
{
return d->scene.rootIndex();
}
/*! Sets the QItemSelectionModel used by this view to manage
* selections. Similar to QAbstractItemView::setSelectionModel
*/
void GraphicsView::setSelectionModel( QItemSelectionModel* model )
{
d->scene.setSelectionModel( model );
}
/*! \returns the QItemSelectionModel used by this view
*/
QItemSelectionModel* GraphicsView::selectionModel() const
{
return d->scene.selectionModel();
}
/*! Sets the KGantt::ItemDelegate used for rendering items on this
* view. \see ItemDelegate and QAbstractItemDelegate.
*/
void GraphicsView::setItemDelegate( ItemDelegate* delegate )
{
d->scene.setItemDelegate( delegate );
}
/*! \returns the ItemDelegate used by this view to render items
*/
ItemDelegate* GraphicsView::itemDelegate() const
{
return d->scene.itemDelegate();
}
/*! Sets the AbstractRowController used by this view. The
* AbstractRowController deals with the height and position
* of each row and with which parts of the model are
* displayed. \see AbstractRowController
*/
void GraphicsView::setRowController( AbstractRowController* rowcontroller )
{
d->rowcontroller = rowcontroller;
d->scene.setRowController( rowcontroller );
updateScene();
}
/*! \returns the AbstractRowController
* for this view. \see setRowController
*/
AbstractRowController* GraphicsView::rowController() const
{
return d->rowcontroller;
}
/*! Sets the AbstractGrid for this view. The grid is an
* object that controls how QModelIndexes are mapped
* to and from the view and how the background and header
* is rendered. \see AbstractGrid and DateTimeGrid.
*/
void GraphicsView::setGrid( AbstractGrid* grid )
{
d->scene.setGrid( grid );
d->slotGridChanged();
}
/*! \returns the AbstractGrid used by this view.
*/
AbstractGrid* GraphicsView::grid() const
{
return d->scene.grid();
}
+/*! \returns the AbstractGrid used by this view.
+ */
+AbstractGrid* GraphicsView::takeGrid()
+{
+ AbstractGrid *grid = d->scene.grid();
+ if ( grid ) {
+ d->scene.setGrid( nullptr) ;
+ }
+ return grid;
+}
+
/*! Sets the view to read-only mode if \a to is true. The default is
* read/write if the model permits it.
*/
void GraphicsView::setReadOnly( bool ro )
{
d->scene.setReadOnly( ro );
}
/*!\returns true iff the view is in read-only mode
*/
bool GraphicsView::isReadOnly() const
{
return d->scene.isReadOnly();
}
/*! Sets the context menu policy for the header. The default value
* Qt::DefaultContextMenu results in a standard context menu on the header
* that allows the user to set the scale and zoom.
*
* Setting this to Qt::CustomContextMenu will cause the signal
* headerContextMenuRequested(const QPoint& pt) to be emitted instead.
*
* \see QWidget::setContextMenuPolicy( Qt::ContextMenuPolicy )
*/
void GraphicsView::setHeaderContextMenuPolicy( Qt::ContextMenuPolicy p )
{
d->headerwidget.setContextMenuPolicy( p );
}
/*! \returns the context menu policy for the header
*/
Qt::ContextMenuPolicy GraphicsView::headerContextMenuPolicy() const
{
return d->headerwidget.contextMenuPolicy();
}
/*! Adds a constraint from \a from to \a to. \a modifiers are the
* keyboard modifiers pressed by the user when the action is invoked.
*
* Override this to control how contraints are added. The default
* implementation adds a soft constraint unless the Shift key is pressed,
* in that case it adds a hard constraint. If a constraint is already
* present, it is removed and nothing is added.
*/
void GraphicsView::addConstraint( const QModelIndex& from,
const QModelIndex& to,
Qt::KeyboardModifiers modifiers )
{
if ( isReadOnly() ) return;
ConstraintModel* cmodel = constraintModel();
assert( cmodel );
Constraint c( from, to, ( modifiers&Qt::ShiftModifier )?Constraint::TypeHard:Constraint::TypeSoft );
if ( cmodel->hasConstraint( c ) ) cmodel->removeConstraint( c );
else cmodel->addConstraint( c );
}
void GraphicsView::resizeEvent( QResizeEvent* ev )
{
d->updateHeaderGeometry();
QRectF r = scene()->itemsBoundingRect();
// To scroll more to the left than the actual item start, bug #4516
r.setLeft( qMin( 0.0, r.left() ) );
// TODO: take scrollbars into account (if not always on)
// The scene should be at least the size of the viewport
QSizeF size = viewport()->size();
//TODO: why -2 below? size should be ex. frames etc?
if ( size.width() > r.width() ) {
r.setWidth( size.width() - 2 );
}
if ( size.height() > r.height() ) {
r.setHeight( size.height() - 2 );
}
const int totalh = rowController()->totalHeight();
if ( r.height() < totalh ) {
r.setHeight( totalh );
}
scene()->setSceneRect( r );
QGraphicsView::resizeEvent( ev );
}
/*!\returns The QModelIndex for the item located at
* position \a pos in the view or an invalid index
* if no item was present at that position.
*
* This is useful for for example contextmenus.
*/
QModelIndex GraphicsView::indexAt( const QPoint& pos ) const
{
QGraphicsItem* item = itemAt( pos );
if ( GraphicsItem* gitem = qgraphicsitem_cast( item ) ) {
return d->scene.summaryHandlingModel()->mapToSource( gitem->index() );
} else {
return QModelIndex();
}
}
/*! \internal */
void GraphicsView::clearItems()
{
d->scene.clearItems();
}
/*! \internal */
void GraphicsView::updateRow( const QModelIndex& idx )
{
d->scene.updateRow( d->scene.summaryHandlingModel()->mapFromSource( idx ) );
}
/*! \internal
* Adjusts the bounding rectangle of the scene.
*/
void GraphicsView::updateSceneRect()
{
/* What to do with this? We need to shrink the view to
* make collapsing items work
*/
qreal range = horizontalScrollBar()->maximum()-horizontalScrollBar()->minimum();
const qreal hscroll = horizontalScrollBar()->value()/( range>0?range:1 );
QRectF r = d->scene.itemsBoundingRect();
// To scroll more to the left than the actual item start, bug #4516
r.setTop( 0. );
r.setLeft( qMin( 0.0, r.left() ) );
r.setSize( r.size().expandedTo( viewport()->size() ) );
const int totalh = rowController()->totalHeight();
if ( r.height() < totalh ) r.setHeight( totalh );
d->scene.setSceneRect( r );
/* set scrollbar to keep the same time in view */
range = horizontalScrollBar()->maximum()-horizontalScrollBar()->minimum();
if ( range>0 ) horizontalScrollBar()->setValue( qRound( hscroll*range ) );
/* We have to update here to adjust for any rows with no
* information because they are painted with a different
* background brush
*/
d->scene.invalidate( QRectF(), QGraphicsScene::BackgroundLayer );
}
/*! \internal
* Resets the state of the view.
*/
void GraphicsView::updateScene()
{
clearItems();
if ( !model()) return;
if ( !rowController()) return;
QModelIndex idx = model()->index( 0, 0, rootIndex() );
do {
updateRow( idx );
} while ( ( idx = rowController()->indexBelow( idx ) ) != QModelIndex() && rowController()->isRowVisible(idx) );
//constraintModel()->cleanup();
//qDebug() << constraintModel();
updateSceneRect();
if ( scene() ) scene()->invalidate( QRectF(), QGraphicsScene::BackgroundLayer );
}
/*! Creates a new GraphicsItem
* Re-iplement to create your own flavour of GraphicsItem
*/
GraphicsItem* GraphicsView::createItem( ItemType type ) const
{
Q_UNUSED(type)
return new GraphicsItem;
}
/*! \internal */
void GraphicsView::deleteSubtree( const QModelIndex& idx )
{
d->scene.deleteSubtree( d->scene.summaryHandlingModel()->mapFromSource( idx ) );
}
/*! Print the Gantt chart using \a printer. If \a drawRowLabels
* is true (the default), each row will have it's label printed
* on the left side. If \a drawColumnLabels is true (the
* default), each column will have it's label printed at the
* top side.
*
* This version of print() will print multiple pages.
*/
void GraphicsView::print( QPrinter* printer, bool drawRowLabels, bool drawColumnLabels )
{
d->scene.print( printer, drawRowLabels, drawColumnLabels );
}
/*! Print part of the Gantt chart from \a start to \a end using \a printer.
* If \a drawRowLabels is true (the default), each row will have it's
* label printed on the left side. If \a drawColumnLabels is true (the
* default), each column will have it's label printed at the
* top side.
*
* This version of print() will print multiple pages.
*
* To print a certain range of a chart with a DateTimeGrid, use
* qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const
* to figure out the values for \a start and \a end.
*/
void GraphicsView::print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels )
{
d->scene.print( printer, start, end, drawRowLabels, drawColumnLabels );
}
/*! Render the GanttView inside the rectangle \a target using the painter \a painter.
* If \a drawRowLabels is true (the default), each row will have it's
* label printed on the left side. If \a drawColumnLabels is true (the
* default), each column will have it's label printed at the
* top side.
*/
void GraphicsView::print( QPainter* painter, const QRectF& targetRect, bool drawRowLabels, bool drawColumnLabels )
{
d->scene.print(painter, targetRect, drawRowLabels, drawColumnLabels);
}
/*! Render the GanttView inside the rectangle \a target using the painter \a painter.
* If \a drawRowLabels is true (the default), each row will have it's
* label printed on the left side. If \a drawColumnLabels is true (the
* default), each column will have it's label printed at the
* top side.
*
* To print a certain range of a chart with a DateTimeGrid, use
* qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const
* to figure out the values for \a start and \a end.
*/
void GraphicsView::print( QPainter* painter, qreal start, qreal end,
const QRectF& targetRect, bool drawRowLabels, bool drawColumnLabels )
{
d->scene.print(painter, start, end, targetRect, drawRowLabels, drawColumnLabels);
}
#include "moc_kganttgraphicsview.cpp"
diff --git a/src/KGantt/kganttgraphicsview.h b/src/KGantt/kganttgraphicsview.h
index a7b9ad9..628e633 100644
--- a/src/KGantt/kganttgraphicsview.h
+++ b/src/KGantt/kganttgraphicsview.h
@@ -1,133 +1,134 @@
/*
* Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
*
* This file is part of the KGantt library.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef KGANTTGRAPHICSVIEW_H
#define KGANTTGRAPHICSVIEW_H
#include
#include "kganttglobal.h"
QT_BEGIN_NAMESPACE
class QPrinter;
class QModelIndex;
class QAbstractItemModel;
class QAbstractProxyModel;
class QItemSelectionModel;
QT_END_NAMESPACE
namespace KGantt {
class AbstractRowController;
class AbstractGrid;
class GraphicsItem;
class ConstraintModel;
class ItemDelegate;
class KGANTT_EXPORT GraphicsView : public QGraphicsView {
Q_OBJECT
KGANTT_DECLARE_PRIVATE_BASE_POLYMORPHIC(GraphicsView)
Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly )
Q_PRIVATE_SLOT( d, void slotGridChanged() )
Q_PRIVATE_SLOT( d, void slotHorizontalScrollValueChanged( int ) )
Q_PRIVATE_SLOT( d, void slotHeaderContextMenuRequested( const QPoint& ) )
/* slots for QAbstractItemModel signals */
Q_PRIVATE_SLOT( d, void slotColumnsInserted( const QModelIndex& parent, int start, int end ) )
Q_PRIVATE_SLOT( d, void slotColumnsRemoved( const QModelIndex& parent, int start, int end ) )
Q_PRIVATE_SLOT( d, void slotDataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ) )
Q_PRIVATE_SLOT( d, void slotLayoutChanged() )
Q_PRIVATE_SLOT( d, void slotModelReset() )
Q_PRIVATE_SLOT( d, void slotRowsInserted( const QModelIndex& parent, int start, int end ) )
Q_PRIVATE_SLOT( d, void slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end ) )
Q_PRIVATE_SLOT( d, void slotRowsRemoved( const QModelIndex& parent, int start, int end ) )
Q_PRIVATE_SLOT( d, void slotItemClicked( const QModelIndex& idx ) )
Q_PRIVATE_SLOT( d, void slotItemDoubleClicked( const QModelIndex& idx ) )
public:
explicit GraphicsView( QWidget* parent = nullptr );
virtual ~GraphicsView();
QAbstractItemModel* model() const;
QAbstractProxyModel* summaryHandlingModel() const;
ConstraintModel* constraintModel() const;
QModelIndex rootIndex() const;
QItemSelectionModel* selectionModel() const;
AbstractRowController* rowController() const;
AbstractGrid* grid() const;
+ AbstractGrid* takeGrid();
ItemDelegate* itemDelegate() const;
bool isReadOnly() const;
void setHeaderContextMenuPolicy( Qt::ContextMenuPolicy );
Qt::ContextMenuPolicy headerContextMenuPolicy() const;
QModelIndex indexAt( const QPoint& pos ) const;
virtual void addConstraint( const QModelIndex& from,
const QModelIndex& to,
Qt::KeyboardModifiers modifiers );
void updateRow( const QModelIndex& );
void updateScene();
virtual GraphicsItem* createItem( ItemType type ) const;
public Q_SLOTS:
void updateSceneRect();
public:
void deleteSubtree( const QModelIndex& );
void print( QPrinter* printer, bool drawRowLabels = true, bool drawColumnLabels = true );
void print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels = true, bool drawColumnLabels = true );
void print( QPainter* painter, const QRectF& target = QRectF(), bool drawRowLabels = true, bool drawColumnLabels = true );
void print( QPainter* painter, qreal start, qreal end,
const QRectF& target = QRectF(), bool drawRowLabels = true, bool drawColumnLabels = true );
public Q_SLOTS:
void setModel( QAbstractItemModel* );
void setSummaryHandlingModel( QAbstractProxyModel* model );
void setConstraintModel( ConstraintModel* );
void setRootIndex( const QModelIndex& );
void setSelectionModel( QItemSelectionModel* );
void setRowController( AbstractRowController* );
void setGrid( AbstractGrid* );
void setItemDelegate( ItemDelegate* delegate );
void setReadOnly( bool );
Q_SIGNALS:
void activated( const QModelIndex & index );
void clicked( const QModelIndex & index );
void qrealClicked( const QModelIndex & index );
void entered( const QModelIndex & index );
void pressed( const QModelIndex & index );
void headerContextMenuRequested( const QPoint& pt );
protected:
void clearItems();
/*reimp*/void resizeEvent( QResizeEvent* ) override;
private:
friend class View;
};
}
#endif /* KGANTTGRAPHICSVIEW_H */
diff --git a/src/KGantt/kganttview.cpp b/src/KGantt/kganttview.cpp
index 72d2710..fb99b20 100644
--- a/src/KGantt/kganttview.cpp
+++ b/src/KGantt/kganttview.cpp
@@ -1,665 +1,665 @@
/*
* Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
*
* This file is part of the KGantt library.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "kganttview.h"
#include "kganttview_p.h"
#include "kganttitemdelegate.h"
#include "kganttgraphicsitem.h"
#include "kganttsummaryhandlingproxymodel.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if defined KDAB_EVAL
#include "../evaldialog/evaldialog.h"
#endif
using namespace KGantt;
namespace {
class HeaderView : public QHeaderView {
public:
explicit HeaderView( QWidget* parent=nullptr ) : QHeaderView( Qt::Horizontal, parent ) {
}
QSize sizeHint() const override { QSize s = QHeaderView::sizeHint(); s.rheight() *= 2; return s; }
};
}
KGanttTreeView::KGanttTreeView( QAbstractProxyModel* proxy, QWidget* parent )
: QTreeView( parent ),
m_controller( this, proxy )
{
setHeader( new HeaderView );
}
KGanttTreeView::~KGanttTreeView()
{
}
void KGanttTreeView::expandAll(QModelIndex index)
{
for (int i = 0; i < model()->rowCount(index); i++) {
QModelIndex indexAt = model()->index(i, 0, index);
if (model()->hasChildren(indexAt))
expandAll(indexAt);
if (isExpanded(indexAt))
continue;
expand(indexAt);
}
}
void KGanttTreeView::collapseAll(QModelIndex index)
{
for (int i = 0; i < model()->rowCount(index); i++) {
QModelIndex indexAt = model()->index(i, 0, index);
if (model()->hasChildren(indexAt))
collapseAll(indexAt);
if (!isExpanded(indexAt))
continue;
collapse(indexAt);
}
}
View::Private::Private(View* v)
: q(v),
splitter(v),
rowController(nullptr),
gfxview( new GraphicsView( &splitter ) ),
model(nullptr)
{
//init();
}
View::Private::~Private()
{
delete gfxview;
}
void View::Private::init()
{
KGanttTreeView* tw = new KGanttTreeView( &ganttProxyModel, &splitter );
tw->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
tw->setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
q->setLeftView( tw );
q->setRowController( tw->rowController() );
//gfxview.setRenderHints( QPainter::Antialiasing );
tw->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
QVBoxLayout* layout = new QVBoxLayout(q);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(&splitter);
q->setLayout(layout);
constraintProxy.setProxyModel( &ganttProxyModel );
constraintProxy.setDestinationModel( &mappedConstraintModel );
setupGraphicsView();
}
void View::Private::setupGraphicsView()
{
gfxview->setParent( &splitter );
gfxview->setAlignment(Qt::AlignTop|Qt::AlignLeft);
gfxview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
gfxview->setSelectionModel( leftWidget->selectionModel() );
gfxview->setConstraintModel( &mappedConstraintModel );
q->setLeftView( leftWidget );
q->setRowController( rowController );
updateScene();
}
void View::Private::updateScene()
{
gfxview->clearItems();
if ( !model) return;
if ( QTreeView* tw = qobject_cast(leftWidget)) {
QModelIndex idx = ganttProxyModel.mapFromSource( model->index( 0, 0, leftWidget->rootIndex() ) );
do {
gfxview->updateRow( idx );
} while ( ( idx = tw->indexBelow( idx ) ) != QModelIndex() &&
gfxview->rowController()->isRowVisible(idx) );
gfxview->updateSceneRect();
} else {
const QModelIndex rootidx = ganttProxyModel.mapFromSource( leftWidget->rootIndex() );
for ( int r = 0; r < ganttProxyModel.rowCount(rootidx); ++r ) {
gfxview->updateRow( ganttProxyModel.index( r, 0, rootidx ) );
}
}
}
void View::Private::slotCollapsed(const QModelIndex& _idx)
{
QTreeView* tw = qobject_cast(leftWidget);
if (!tw) return;
bool blocked = gfxview->blockSignals( true );
QModelIndex idx( _idx );
const QAbstractItemModel* model = leftWidget->model();
const QModelIndex pidx = ganttProxyModel.mapFromSource(idx);
bool isMulti = false;
for ( QModelIndex treewalkidx = pidx; treewalkidx.isValid(); treewalkidx = treewalkidx.parent() ) {
if ( treewalkidx.data( ItemTypeRole ).toInt() == TypeMulti
&& !gfxview->rowController()->isRowExpanded( treewalkidx ) ) {
isMulti = true;
break;
}
}
if ( !isMulti ) {
for ( int i = 0; i < model->rowCount( idx ); ++i ) {
gfxview->deleteSubtree( ganttProxyModel.index( i, 0, pidx ) );
}
} else {
gfxview->updateRow(pidx);
}
//qDebug() << "Looking to update from " << idx;
while ( ( idx=tw->indexBelow( idx ) ) != QModelIndex() &&
gfxview->rowController()->isRowVisible( ganttProxyModel.mapFromSource(idx) ) ) {
const QModelIndex proxyidx( ganttProxyModel.mapFromSource( idx ) );
gfxview->updateRow(proxyidx);
}
gfxview->blockSignals( blocked );
gfxview->updateSceneRect();
}
void View::Private::slotExpanded(const QModelIndex& _idx)
{
QModelIndex idx( ganttProxyModel.mapFromSource( _idx ) );
do {
//qDebug() << "Updating row" << idx << idx.data( Qt::DisplayRole ).toString();
gfxview->updateRow(idx);
} while ( ( idx=gfxview->rowController()->indexBelow( idx ) ) != QModelIndex()
&& gfxview->rowController()->isRowVisible( idx ) );
gfxview->updateSceneRect();
}
void View::Private::slotVerticalScrollValueChanged( int val )
{
#if 0
qDebug() << "View::Private::slotVerticalScrollValueChanged("<verticalScrollBar()->singleStep();
#endif
leftWidget->verticalScrollBar()->setValue( val/gfxview->verticalScrollBar()->singleStep() );
}
void View::Private::slotLeftWidgetVerticalRangeChanged(int min, int max )
{
//qDebug() << "View::Private::slotLeftWidgetVerticalRangeChanged("<verticalScrollBar()->setRange( min, max );
gfxview->updateSceneRect();
}
}
void View::Private::slotGfxViewVerticalRangeChanged( int min, int max )
{
//qDebug() << "View::Private::slotGfxViewVerticalRangeChanged("<verticalScrollBar()->minimum();
int leftMax = leftWidget->verticalScrollBar()->maximum();
bool blocked = gfxview->verticalScrollBar()->blockSignals( true );
gfxview->verticalScrollBar()->setRange( qMax( min, leftMin ), qMax( max, leftMax ) );
gfxview->verticalScrollBar()->blockSignals( blocked );
}
}
/*!\class KGantt::View kganttview.h KGanttView
* \ingroup KGantt
* \brief This widget that consists of a QTreeView and a GraphicsView
*
* This is the easy to use, complete gantt chart widget. It
* consists of a QTreeView on the left and a KGantt::GraphicsView
* on the right separated by a QSplitter. The two views share the same
* model.
*/
/*! Constructor. Creates a View with parent \a parent,
* a DateTimeGrid as default grid implementaion and no model etc.
*/
View::View(QWidget* parent)
: QWidget(parent),
_d(new Private(this))
{
#if defined KDAB_EVAL
EvalDialog::checkEvalLicense( "KD Gantt" );
#endif
_d->init();
}
View::~View()
{
delete _d;
}
#define d d_func()
/*! Replaces the left widget with a custom QAbstractItemView.
*
* \param aiv The view to be used to the left, instead of the default tree view
* \sa setRowController()
*/
void View::setLeftView( QAbstractItemView* aiv )
{
assert( aiv );
if ( aiv==d->leftWidget ) return;
if ( !d->leftWidget.isNull() ) {
d->leftWidget->disconnect( this );
d->leftWidget->hide();
d->leftWidget->verticalScrollBar()->disconnect( d->gfxview->verticalScrollBar() );
d->gfxview->verticalScrollBar()->disconnect( d->leftWidget->verticalScrollBar() );
}
d->leftWidget = aiv;
d->splitter.insertWidget( 0, d->leftWidget );
if ( qobject_cast(d->leftWidget) ) {
connect( d->leftWidget, SIGNAL(collapsed(QModelIndex)),
this, SLOT(slotCollapsed(QModelIndex)) );
connect( d->leftWidget, SIGNAL(expanded(QModelIndex)),
this, SLOT(slotExpanded(QModelIndex)) );
}
connect( d->gfxview->verticalScrollBar(), SIGNAL(valueChanged(int)),
d->leftWidget->verticalScrollBar(), SLOT(setValue(int)) );
connect( d->leftWidget->verticalScrollBar(), SIGNAL(valueChanged(int)),
d->gfxview->verticalScrollBar(), SLOT(setValue(int)) );
connect( d->leftWidget->verticalScrollBar(), SIGNAL(rangeChanged(int,int)),
this, SLOT(slotLeftWidgetVerticalRangeChanged(int,int)) );
connect( d->gfxview->verticalScrollBar(), SIGNAL(rangeChanged(int,int)),
this, SLOT(slotGfxViewVerticalRangeChanged(int,int)) );
}
/*! Sets \a ctrl to be the rowcontroller used by this View.
* The default rowcontroller is owned by KGantt::View and is
* suitable for the default treeview in the left part of the view.
* You probably only want to change this if you replace the treeview.
*/
void View::setRowController( AbstractRowController* ctrl )
{
if ( ctrl == d->rowController && d->gfxview->rowController() == ctrl ) return;
d->rowController = ctrl;
d->gfxview->setRowController( d->rowController );
}
/*! \returns a pointer to the current rowcontroller.
* \see AbstractRowController
*/
AbstractRowController* View::rowController()
{
return d->rowController;
}
/*! \overload AbstractRowController* KGantt::View::rowController()
*/
const AbstractRowController* View::rowController() const
{
return d->rowController;
}
/*!
* \returns a pointer to the QAbstractItemView in the left
* part of the widget.
* */
const QAbstractItemView* View::leftView() const
{
return d->leftWidget;
}
/*!
* \overload const QAbstractItemView* KGantt::View::leftView() const
*/
QAbstractItemView* View::leftView()
{
return d->leftWidget;
}
/*! Set the GraphicsView to be used for this View. It only makes sense to call this
* if you need to subclass GraphicsView.
*
* NOTE: _Only_ call this right after creating the View, before setting a model or any other
* attributes.
*/
void View::setGraphicsView( GraphicsView* gv )
{
if ( gv != d->gfxview ) {
GraphicsView* old = d->gfxview;
d->gfxview = gv;
d->gfxview->setModel(old->model()); // use the old ForwardingProxyModel
d->setupGraphicsView();
- d->gfxview->setGrid( old->grid() );
+ d->gfxview->setGrid( old->takeGrid() );
delete old;
}
}
/*!
* \returns a pointer to the GraphicsView
*/
const GraphicsView* View::graphicsView() const
{
return d->gfxview;
}
/*!
* \overload const GraphicsView* KGantt::View::graphicsView() const
*/
GraphicsView* View::graphicsView()
{
return d->gfxview;
}
/*!
* \returns a pointer to the QSplitter that manages the left view and graphicsView
*/
const QSplitter* View::splitter() const
{
return &d->splitter;
}
/*!
* \overload const QSplitter* KGantt::View::splitter() const
*/
QSplitter* View::splitter()
{
return &d->splitter;
}
/*! \returns the current model displayed by this view
*/
QAbstractItemModel* View::model() const
{
return leftView()->model();
}
/*! Sets the QAbstractItemModel to be displayed in this view
* to \a model.
*
* \see GraphicsView::setModel
*/
void View::setModel( QAbstractItemModel* model )
{
leftView()->setModel( model );
d->ganttProxyModel.setSourceModel( model );
d->gfxview->setModel( &d->ganttProxyModel );
}
/*! \returns the QItemSelectionModel used by this view
*/
QItemSelectionModel* View::selectionModel() const
{
return leftView()->selectionModel();
}
/*! Sets the QItemSelectionModel used by this view to manage
* selections. Similar to QAbstractItemView::setSelectionModel
*/
void View::setSelectionModel( QItemSelectionModel* smodel )
{
leftView()->setSelectionModel( smodel );
d->gfxview->setSelectionModel( new QItemSelectionModel( &( d->ganttProxyModel ),this ) );
}
/*! Sets the AbstractGrid for this view. The grid is an
* object that controls how QModelIndexes are mapped
* to and from the view and how the background and header
* is rendered. \see AbstractGrid and DateTimeGrid.
*/
void View::setGrid( AbstractGrid* grid )
{
d->gfxview->setGrid( grid );
}
void View::expandAll( QModelIndex index )
{
// FIXME:
// It is legal to call setLeftView() with any QAbstractItemView,
// so expandAll should be reimplemented to work with that.
KGanttTreeView* tw = qobject_cast(leftView());
if (tw) {
tw->expandAll(index);
}
}
void View::collapseAll( QModelIndex index )
{
// FIXME:
// It is legal to call setLeftView() with any QAbstractItemView,
// so expandAll should be reimplemented to work with that.
KGanttTreeView* tw = qobject_cast(leftView());
if (tw) {
tw->collapseAll(index);
}
}
/*! \returns the AbstractGrid used by this view.
*/
AbstractGrid* View::grid() const
{
return d->gfxview->grid();
}
/*! \returns the rootindex for this view.
*/
QModelIndex View::rootIndex() const
{
return leftView()->rootIndex();
}
/*! Sets the root index of the model displayed by this view.
* Similar to QAbstractItemView::setRootIndex, default is QModelIndex().
*/
void View::setRootIndex( const QModelIndex& idx )
{
leftView()->setRootIndex( idx );
d->gfxview->setRootIndex( idx );
}
/*! \returns the ItemDelegate used by this view to render items
*/
ItemDelegate* View::itemDelegate() const
{
return d->gfxview->itemDelegate();
}
/*! Sets the KGantt::ItemDelegate used for rendering items on this
* view. \see ItemDelegate and QAbstractItemDelegate.
*/
void View::setItemDelegate( ItemDelegate* delegate )
{
leftView()->setItemDelegate( delegate );
d->gfxview->setItemDelegate( delegate );
}
/*! Sets the constraintmodel displayed by this view.
* \see KGantt::ConstraintModel.
*/
void View::setConstraintModel( ConstraintModel* cm )
{
d->constraintProxy.setSourceModel( cm );
d->gfxview->setConstraintModel( &d->mappedConstraintModel );
}
/*! \returns the KGantt::ConstraintModel displayed by this view.
*/
ConstraintModel* View::constraintModel() const
{
return d->constraintProxy.sourceModel();
}
const QAbstractProxyModel* View::ganttProxyModel() const
{
return &( d->ganttProxyModel );
}
QAbstractProxyModel* View::ganttProxyModel()
{
return &( d->ganttProxyModel );
}
void View::ensureVisible(const QModelIndex& index)
{
QGraphicsView* view = graphicsView();
KGantt::GraphicsScene* scene = static_cast(view->scene());
if (!scene)
return;
KGantt::SummaryHandlingProxyModel* model = static_cast(scene->summaryHandlingModel());
const QModelIndex pidx = d->ganttProxyModel.mapFromSource(index);
const QModelIndex idx = model->mapFromSource( pidx );
QGraphicsItem* item = scene->findItem(idx);
view->ensureVisible(item);
}
void View::resizeEvent(QResizeEvent*ev)
{
QWidget::resizeEvent(ev);
}
/*!\returns The QModelIndex for the item located at
* position \a pos in the view or an invalid index
* if no item was present at that position.
*
* \see GraphicsView::indexAt
*/
QModelIndex View::indexAt( const QPoint& pos ) const
{
return d->gfxview->indexAt( pos );
}
/*! Print the Gantt chart using \a printer. If \a drawRowLabels
* is true (the default), each row will have it's label printed
* on the left side. If \a drawColumnLabels is true (the
* default), each column will have it's label printed at the
* top side.
*
* This version of print() will print multiple pages.
*/
void View::print( QPrinter* printer, bool drawRowLabels, bool drawColumnLabels )
{
graphicsView()->print( printer, drawRowLabels, drawColumnLabels );
}
/*! Print part of the Gantt chart from \a start to \a end using \a printer.
* If \a drawRowLabels is true (the default), each row will have it's
* label printed on the left side. If \a drawColumnLabels is true (the
* default), each column will have it's label printed at the
* top side.
*
* This version of print() will print multiple pages.
*
* To print a certain range of a chart with a DateTimeGrid, use
* qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const
* to figure out the values for \a start and \a end.
*/
void View::print( QPrinter* printer, qreal start, qreal end, bool drawRowLabels, bool drawColumnLabels )
{
graphicsView()->print( printer, start, end, drawRowLabels, drawColumnLabels );
}
/*! Render the GanttView inside the rectangle \a target using the painter \a painter.
* If \a drawRowLabels is true (the default), each row will have it's
* label printed on the left side. If \a drawColumnLabels is true (the
* default), each column will have it's label printed at the
* top side.
*/
void View::print( QPainter* painter, const QRectF& target, bool drawRowLabels, bool drawColumnLabels)
{
d->gfxview->print( painter,
target,
drawRowLabels,
drawColumnLabels);
}
/*! Render the GanttView inside the rectangle \a target using the painter \a painter.
* If \a drawRowLabels is true (the default), each row will have it's
* label printed on the left side. If \a drawColumnLabels is true (the
* default), each column will have it's label printed at the
* top side.
*
* To print a certain range of a chart with a DateTimeGrid, use
* qreal DateTimeGrid::mapFromDateTime( const QDateTime& dt) const
* to figure out the values for \a start and \a end.
*/
void View::print( QPainter* painter, qreal start, qreal end, const QRectF& target, bool drawRowLabels, bool drawColumnLabels)
{
d->gfxview->print( painter,
start, end,
target,
drawRowLabels,
drawColumnLabels);
}
#include "moc_kganttview.cpp"
#ifndef KDAB_NO_UNIT_TESTS
#include "unittest/test.h"
#include "kganttlistviewrowcontroller.h"
#include
#include
#include
#include
KDAB_SCOPED_UNITTEST_SIMPLE( KGantt, View, "test" ) {
View view( nullptr );
#if 0 // GUI tests do not work well on the server
QTimer::singleShot( 1000, qApp, SLOT(quit()) );
view.show();
qApp->exec();
QPixmap screenshot1 = QPixmap::grabWidget( &view );
QTreeView* tv = new QTreeView;
view.setLeftView( tv );
view.setRowController( new TreeViewRowController(tv,view.ganttProxyModel()) );
QTimer::singleShot( 1000, qApp, SLOT(quit()) );
qApp->exec();
QPixmap screenshot2 = QPixmap::grabWidget( &view );
assertEqual( screenshot1.toImage(), screenshot2.toImage() );
QListView* lv = new QListView;
view.setLeftView(lv);
view.setRowController( new ListViewRowController(lv,view.ganttProxyModel()));
view.show();
QTimer::singleShot( 1000, qApp, SLOT(quit()) );
qApp->exec();
#endif
}
#endif /* KDAB_NO_UNIT_TESTS */