/**************************************************************************** ** Copyright (C) 2001-2011 Klaralvdalens Datakonsult AB. All rights reserved. ** ** This file is part of the KD Chart library. ** ** Licensees holding valid commercial KD Chart licenses may use this file in ** accordance with the KD Chart Commercial License Agreement provided with ** the Software. ** ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 and version 3 as published by the ** Free Software Foundation and appearing in the file LICENSE.GPL.txt included. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** Contact info@kdab.com if any conditions of this licensing are not ** clear to you. ** **********************************************************************/ #include "KDChartAttributesModel.h" #include "KDChartPalette.h" #include "KDChartGlobal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDChart; AttributesModel::AttributesModel( QAbstractItemModel* model, QObject * parent/* = 0 */ ) : AbstractProxyModel( parent ), mPaletteType( PaletteTypeDefault ) { setSourceModel(model); setDefaultForRole( KDChart::DataValueLabelAttributesRole, DataValueAttributes::defaultAttributesAsVariant() ); } AttributesModel::~AttributesModel() { } void AttributesModel::initFrom( const AttributesModel* other ) { if( other == this || ! other ) return; mDataMap = other->mDataMap; mHorizontalHeaderDataMap = other->mHorizontalHeaderDataMap; mVerticalHeaderDataMap = other->mVerticalHeaderDataMap; mModelDataMap = other->mModelDataMap; mDefaultsMap = other->mDefaultsMap; setPaletteType( other->paletteType() ); } bool AttributesModel::compare( const AttributesModel* other )const { if( other == this ) return true; if( ! other ){ //qDebug() << "AttributesModel::compare() cannot compare to Null pointer"; return false; } { if (mDataMap.count() != other->mDataMap.count()){ //qDebug() << "AttributesModel::compare() dataMap have different sizes"; return false; } QMap > >::const_iterator itA = mDataMap.constBegin(); QMap > >::const_iterator itB = other->mDataMap.constBegin(); while (itA != mDataMap.constEnd()) { if ((*itA).count() != (*itB).count()){ //qDebug() << "AttributesModel::compare() dataMap/map have different sizes"; return false; } QMap >::const_iterator it2A = (*itA).constBegin(); QMap >::const_iterator it2B = (*itB).constBegin(); while (it2A != itA->constEnd()) { if ((*it2A).count() != (*it2B).count()){ //qDebug() << "AttributesModel::compare() dataMap/map/map have different sizes:" // << (*it2A).count() << (*it2B).count(); return false; } QMap::const_iterator it3A = (*it2A).constBegin(); QMap::const_iterator it3B = (*it2B).constBegin(); while (it3A != it2A->constEnd()) { if ( it3A.key() != it3B.key() ){ //qDebug( "AttributesModel::compare()\n" // " dataMap[%i, %i] values have different types. A: %x B: %x", // itA.key(), it2A.key(), it3A.key(), it3B.key()); return false; } if ( ! compareAttributes( it3A.key(), it3A.value(), it3B.value() ) ){ //qDebug( "AttributesModel::compare()\n" // " dataMap[%i, %i] values are different. Role: %x", itA.key(), it2A.key(), it3A.key()); return false; } ++it3A; ++it3B; } ++it2A; ++it2B; } ++itA; ++itB; } } { if (mHorizontalHeaderDataMap.count() != other->mHorizontalHeaderDataMap.count()){ //qDebug() << "AttributesModel::compare() horizontalHeaderDataMap have different sizes"; return false; } QMap >::const_iterator itA = mHorizontalHeaderDataMap.constBegin(); QMap >::const_iterator itB = other->mHorizontalHeaderDataMap.constBegin(); while (itA != mHorizontalHeaderDataMap.constEnd()) { if ((*itA).count() != (*itB).count()){ //qDebug() << "AttributesModel::compare() horizontalHeaderDataMap/map have different sizes"; return false; } QMap::const_iterator it2A = (*itA).constBegin(); QMap::const_iterator it2B = (*itB).constBegin(); while (it2A != itA->constEnd()) { if ( it2A.key() != it2B.key() ){ //qDebug( "AttributesModel::compare()\n" // " horizontalHeaderDataMap[ %i ] values have different types. A: %x B: %x", // itA.key(), it2A.key(), it2B.key()); return false; } if ( ! compareAttributes( it2A.key(), it2A.value(), it2B.value() ) ){ //qDebug( "AttributesModel::compare()\n" // " horizontalHeaderDataMap[ %i ] values are different. Role: %x", itA.key(), it2A.key() ); return false; } ++it2A; ++it2B; } ++itA; ++itB; } } { if (mVerticalHeaderDataMap.count() != other->mVerticalHeaderDataMap.count()){ //qDebug() << "AttributesModel::compare() verticalHeaderDataMap have different sizes"; return false; } QMap >::const_iterator itA = mVerticalHeaderDataMap.constBegin(); QMap >::const_iterator itB = other->mVerticalHeaderDataMap.constBegin(); while (itA != mVerticalHeaderDataMap.constEnd()) { if ((*itA).count() != (*itB).count()){ //qDebug() << "AttributesModel::compare() verticalHeaderDataMap/map have different sizes"; return false; } QMap::const_iterator it2A = (*itA).constBegin(); QMap::const_iterator it2B = (*itB).constBegin(); while (it2A != itA->constEnd()) { if ( it2A.key() != it2B.key() ){ //qDebug( "AttributesModel::compare()\n" // " verticalHeaderDataMap[ %i ] values have different types. A: %x B: %x", // itA.key(), it2A.key(), it2B.key()); return false; } if ( ! compareAttributes( it2A.key(), it2A.value(), it2B.value() ) ){ //qDebug( "AttributesModel::compare()\n" // " verticalHeaderDataMap[ %i ] values are different. Role: %x", itA.key(), it2A.key() ); return false; } ++it2A; ++it2B; } ++itA; ++itB; } } { if (mModelDataMap.count() != other->mModelDataMap.count()){ //qDebug() << "AttributesModel::compare() modelDataMap have different sizes:" << mModelDataMap.count() << other->mModelDataMap.count(); return false; } QMap::const_iterator itA = mModelDataMap.constBegin(); QMap::const_iterator itB = other->mModelDataMap.constBegin(); while (itA != mModelDataMap.constEnd()) { if ( itA.key() != itB.key() ){ //qDebug( "AttributesModel::compare()\n" // " modelDataMap values have different types. A: %x B: %x", // itA.key(), itB.key()); return false; } if ( ! compareAttributes( itA.key(), itA.value(), itB.value() ) ){ //qDebug( "AttributesModel::compare()\n" // " modelDataMap values are different. Role: %x", itA.key() ); return false; } ++itA; ++itB; } } if (paletteType() != other->paletteType()){ //qDebug() << "AttributesModel::compare() palette types are different"; return false; } return true; } bool AttributesModel::compareAttributes( int role, const QVariant& a, const QVariant& b )const { if( isKnownAttributesRole( role ) ){ switch( role ) { case DataValueLabelAttributesRole: return (qVariantValue( a ) == qVariantValue( b )); case DatasetBrushRole: return (qVariantValue( a ) == qVariantValue( b )); case DatasetPenRole: return (qVariantValue( a ) == qVariantValue( b )); case ThreeDAttributesRole: // As of yet there is no ThreeDAttributes class, // and the AbstractThreeDAttributes class is pure virtual, // so we ignore this role for now. // (khz, 04.04.2007) /* return (qVariantValue( a ) == qVariantValue( b )); */ break; case LineAttributesRole: return (qVariantValue( a ) == qVariantValue( b )); case ThreeDLineAttributesRole: return (qVariantValue( a ) == qVariantValue( b )); case BarAttributesRole: return (qVariantValue( a ) == qVariantValue( b )); case StockBarAttributesRole: return (qVariantValue( a ) == qVariantValue( b )); case ThreeDBarAttributesRole: return (qVariantValue( a ) == qVariantValue( b )); case PieAttributesRole: return (qVariantValue( a ) == qVariantValue( b )); case ThreeDPieAttributesRole: return (qVariantValue( a ) == qVariantValue( b )); case ValueTrackerAttributesRole: return (qVariantValue( a ) == qVariantValue( b )); case DataHiddenRole: return (qVariantValue( a ) == qVariantValue( b )); default: Q_ASSERT( false ); // all of our own roles need to be handled break; } }else{ return (a == b); } return true; } QVariant AttributesModel::headerData ( int section, Qt::Orientation orientation, int role/* = Qt::DisplayRole */ ) const { if( sourceModel() ) { const QVariant sourceData = sourceModel()->headerData( section, orientation, role ); if ( sourceData.isValid() ) return sourceData; } if( orientation == Qt::Horizontal && role == ColumnDataRole ) { // it seems the source model doesn't like the idea of handing out all the column data at once... // so we have to do it manually. QVariantList result; const int rows = sourceModel()->rowCount(); for( int row = 0; row < rows; ++row ) result.push_back( sourceModel()->index( row, section ).data() ); return result; } // the source model didn't have data set, let's use our stored values const QMap >& map = orientation == Qt::Horizontal ? mHorizontalHeaderDataMap : mVerticalHeaderDataMap; if ( map.contains( section ) ) { const QMap &dataMap = map[ section ]; if ( dataMap.contains( role ) ) { return dataMap[ role ]; } } return defaultHeaderData( section, orientation, role ); } QVariant AttributesModel::defaultHeaderData ( int section, Qt::Orientation orientation, int role ) const { // Default values if nothing else matches switch ( role ) { case Qt::DisplayRole: //TODO for KDChart 3.0: Change to "return QString::number( section+1 );" return QString(QLatin1String( orientation == Qt::Vertical ? "Series " : "Item " ) + QString::number( section ) ); case KDChart::DatasetBrushRole: { if ( paletteType() == PaletteTypeSubdued ) return Palette::subduedPalette().getBrush( section ); else if ( paletteType() == PaletteTypeRainbow ) return Palette::rainbowPalette().getBrush( section ); else if ( paletteType() == PaletteTypeDefault ) return Palette::defaultPalette().getBrush( section ); else qWarning("Unknown type of fallback palette!"); } break; case KDChart::DatasetPenRole: { // default to the color set for the brush (or it's defaults) // but only if no per model override was set if ( !modelData( role ).isValid() ) { QBrush brush = qVariantValue( headerData( section, orientation, DatasetBrushRole ) ); return QPen( brush.color() ); } } break; default: break; } return QVariant(); } // Note: Our users NEED this method - even if // we do not need it at drawing time! // (khz, 2006-07-28) QVariant AttributesModel::data( int role ) const { if ( isKnownAttributesRole( role ) ) { // check if there is something set at global level QVariant v = modelData( role ); // else return the default setting, if any if ( !v.isValid() ) v = defaultsForRole( role ); return v; } return QVariant(); } // Note: Our users NEED this method - even if // we do not need it at drawing time! // (khz, 2006-07-28) QVariant AttributesModel::data( int column, int role ) const { if ( isKnownAttributesRole( role ) ) { // check if there is something set for the column (dataset) QVariant v; v = headerData( column, Qt::Horizontal, role ); // check if there is something set at global level if ( !v.isValid() ) v = data( role ); // includes automatic fallback to default return v; } return QVariant(); } QVariant AttributesModel::data( const QModelIndex& index, int role ) const { //qDebug() << "AttributesModel::data(" << index << role << ")"; if( index.isValid() ) { Q_ASSERT( index.model() == this ); } if( sourceModel() == 0 ) return QVariant(); if( index.isValid() ) { const QVariant sourceData = sourceModel()->data( mapToSource(index), role ); if( sourceData.isValid() ) return sourceData; } // check if we are storing a value for this role at this cell index if( mDataMap.contains( index.column() ) ) { const QMap< int, QMap< int, QVariant > >& colDataMap = mDataMap[ index.column() ]; if( colDataMap.contains( index.row() ) ) { const QMap< int, QVariant >& dataMap = colDataMap[ index.row() ]; if( dataMap.contains( role ) ) { const QVariant v = dataMap[ role ]; if( v.isValid() ) return v; } } } // check if there is something set for the column (dataset), or at global level if( index.isValid() ) return data( index.column(), role ); // includes automatic fallback to default return QVariant(); } bool AttributesModel::isKnownAttributesRole( int role ) const { bool oneOfOurs = false; switch( role ) { // fallthrough intended case DataValueLabelAttributesRole: case DatasetBrushRole: case DatasetPenRole: case ThreeDAttributesRole: case LineAttributesRole: case ThreeDLineAttributesRole: case BarAttributesRole: case StockBarAttributesRole: case ThreeDBarAttributesRole: case PieAttributesRole: case ThreeDPieAttributesRole: case ValueTrackerAttributesRole: case DataHiddenRole: oneOfOurs = true; default: break; } return oneOfOurs; } QVariant AttributesModel::defaultsForRole( int role ) const { // returns default-constructed QVariant if not found return mDefaultsMap.value( role ); } bool AttributesModel::setData ( const QModelIndex & index, const QVariant & value, int role ) { if ( !isKnownAttributesRole( role ) ) { return sourceModel()->setData( mapToSource(index), value, role ); } else { QMap< int, QMap< int, QVariant> > &colDataMap = mDataMap[ index.column() ]; QMap &dataMap = colDataMap[ index.row() ]; //qDebug() << "AttributesModel::setData" <<"role" << role << "value" << value; dataMap.insert( role, value ); emit attributesChanged( index, index ); return true; } } bool AttributesModel::resetData ( const QModelIndex & index, int role ) { return setData ( index, QVariant(), role ); } bool AttributesModel::setHeaderData ( int section, Qt::Orientation orientation, const QVariant & value, int role ) { if( sourceModel() != 0 && headerData( section, orientation, role ) == value ) return true; if ( !isKnownAttributesRole( role ) ) { return sourceModel()->setHeaderData( section, orientation, value, role ); } else { QMap > §ionDataMap = orientation == Qt::Horizontal ? mHorizontalHeaderDataMap : mVerticalHeaderDataMap; QMap &dataMap = sectionDataMap[ section ]; dataMap.insert( role, value ); if( sourceModel() ){ emit attributesChanged( index( 0, section, QModelIndex() ), index( rowCount( QModelIndex() ), section, QModelIndex() ) ); emit headerDataChanged( orientation, section, section ); if ( section != -1 ) emit dataChanged( index( 0, section, QModelIndex() ), index( rowCount( QModelIndex() ) - 1, section, QModelIndex() ) ); } return true; } } bool AttributesModel::resetHeaderData ( int section, Qt::Orientation orientation, int role ) { return setHeaderData ( section, orientation, QVariant(), role ); } void AttributesModel::setPaletteType( AttributesModel::PaletteType type ) { mPaletteType = type; } AttributesModel::PaletteType AttributesModel::paletteType() const { return mPaletteType; } bool KDChart::AttributesModel::setModelData( const QVariant value, int role ) { mModelDataMap.insert( role, value ); if( sourceModel() ){ emit attributesChanged( index( 0, 0, QModelIndex() ), index( rowCount( QModelIndex() ), columnCount( QModelIndex() ), QModelIndex() ) ); } return true; } QVariant KDChart::AttributesModel::modelData( int role ) const { return mModelDataMap.value( role, QVariant() ); } int AttributesModel::rowCount( const QModelIndex& index ) const { if ( sourceModel() ) { return sourceModel()->rowCount( mapToSource(index) ); } else { return 0; } } int AttributesModel::columnCount( const QModelIndex& index ) const { if ( sourceModel() ) { return sourceModel()->columnCount( mapToSource(index) ); } else { return 0; } } void AttributesModel::setSourceModel( QAbstractItemModel* sourceModel ) { if( this->sourceModel() != 0 ) { disconnect( this->sourceModel(), SIGNAL( dataChanged( const QModelIndex&, const QModelIndex&)), this, SLOT( slotDataChanged( const QModelIndex&, const QModelIndex&))); disconnect( this->sourceModel(), SIGNAL( rowsInserted( const QModelIndex&, int, int ) ), this, SLOT( slotRowsInserted( const QModelIndex&, int, int ) ) ); disconnect( this->sourceModel(), SIGNAL( rowsRemoved( const QModelIndex&, int, int ) ), this, SLOT( slotRowsRemoved( const QModelIndex&, int, int ) ) ); disconnect( this->sourceModel(), SIGNAL( rowsAboutToBeInserted( const QModelIndex&, int, int ) ), this, SLOT( slotRowsAboutToBeInserted( const QModelIndex&, int, int ) ) ); disconnect( this->sourceModel(), SIGNAL( rowsAboutToBeRemoved( const QModelIndex&, int, int ) ), this, SLOT( slotRowsAboutToBeRemoved( const QModelIndex&, int, int ) ) ); disconnect( this->sourceModel(), SIGNAL( columnsInserted( const QModelIndex&, int, int ) ), this, SLOT( slotColumnsInserted( const QModelIndex&, int, int ) ) ); disconnect( this->sourceModel(), SIGNAL( columnsRemoved( const QModelIndex&, int, int ) ), this, SLOT( slotColumnsRemoved( const QModelIndex&, int, int ) ) ); disconnect( this->sourceModel(), SIGNAL( columnsAboutToBeInserted( const QModelIndex&, int, int ) ), this, SLOT( slotColumnsAboutToBeInserted( const QModelIndex&, int, int ) ) ); disconnect( this->sourceModel(), SIGNAL( columnsAboutToBeRemoved( const QModelIndex&, int, int ) ), this, SLOT( slotColumnsAboutToBeRemoved( const QModelIndex&, int, int ) ) ); disconnect( this->sourceModel(), SIGNAL( modelReset() ), this, SIGNAL( modelReset() ) ); disconnect( this->sourceModel(), SIGNAL( layoutChanged() ), this, SIGNAL( layoutChanged() ) ); } QAbstractProxyModel::setSourceModel( sourceModel ); if( this->sourceModel() != NULL ) { connect( this->sourceModel(), SIGNAL( dataChanged( const QModelIndex&, const QModelIndex&)), this, SLOT( slotDataChanged( const QModelIndex&, const QModelIndex&))); connect( this->sourceModel(), SIGNAL( rowsInserted( const QModelIndex&, int, int ) ), this, SLOT( slotRowsInserted( const QModelIndex&, int, int ) ) ); connect( this->sourceModel(), SIGNAL( rowsRemoved( const QModelIndex&, int, int ) ), this, SLOT( slotRowsRemoved( const QModelIndex&, int, int ) ) ); connect( this->sourceModel(), SIGNAL( rowsAboutToBeInserted( const QModelIndex&, int, int ) ), this, SLOT( slotRowsAboutToBeInserted( const QModelIndex&, int, int ) ) ); connect( this->sourceModel(), SIGNAL( rowsAboutToBeRemoved( const QModelIndex&, int, int ) ), this, SLOT( slotRowsAboutToBeRemoved( const QModelIndex&, int, int ) ) ); connect( this->sourceModel(), SIGNAL( columnsInserted( const QModelIndex&, int, int ) ), this, SLOT( slotColumnsInserted( const QModelIndex&, int, int ) ) ); connect( this->sourceModel(), SIGNAL( columnsRemoved( const QModelIndex&, int, int ) ), this, SLOT( slotColumnsRemoved( const QModelIndex&, int, int ) ) ); connect( this->sourceModel(), SIGNAL( columnsAboutToBeInserted( const QModelIndex&, int, int ) ), this, SLOT( slotColumnsAboutToBeInserted( const QModelIndex&, int, int ) ) ); connect( this->sourceModel(), SIGNAL( columnsAboutToBeRemoved( const QModelIndex&, int, int ) ), this, SLOT( slotColumnsAboutToBeRemoved( const QModelIndex&, int, int ) ) ); connect( this->sourceModel(), SIGNAL( modelReset() ), this, SIGNAL( modelReset() ) ); connect( this->sourceModel(), SIGNAL( layoutChanged() ), this, SIGNAL( layoutChanged() ) ); } } void AttributesModel::slotRowsAboutToBeInserted( const QModelIndex& parent, int start, int end ) { beginInsertRows( mapFromSource( parent ), start, end ); } void AttributesModel::slotColumnsAboutToBeInserted( const QModelIndex& parent, int start, int end ) { beginInsertColumns( mapFromSource( parent ), start, end ); } void AttributesModel::slotRowsInserted( const QModelIndex& parent, int start, int end ) { Q_UNUSED( parent ); Q_UNUSED( start ); Q_UNUSED( end ); endInsertRows(); } void AttributesModel::slotColumnsInserted( const QModelIndex& parent, int start, int end ) { Q_UNUSED( parent ); Q_UNUSED( start ); Q_UNUSED( end ); endInsertColumns(); } void AttributesModel::slotRowsAboutToBeRemoved( const QModelIndex& parent, int start, int end ) { beginRemoveRows( mapFromSource( parent ), start, end ); } void AttributesModel::slotColumnsAboutToBeRemoved( const QModelIndex& parent, int start, int end ) { beginRemoveColumns( mapFromSource( parent ), start, end ); } void AttributesModel::slotRowsRemoved( const QModelIndex& parent, int start, int end ) { Q_UNUSED( parent ); Q_UNUSED( start ); Q_UNUSED( end ); endRemoveRows(); } void AttributesModel::slotColumnsRemoved( const QModelIndex& parent, int start, int end ) { Q_UNUSED( parent ); Q_UNUSED( start ); Q_UNUSED( end ); endRemoveColumns(); } void AttributesModel::slotDataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ) { emit dataChanged( mapFromSource( topLeft ), mapFromSource( bottomRight ) ); } /** needed for serialization */ const QMap > > AttributesModel::dataMap()const { return mDataMap; } /** needed for serialization */ const QMap > AttributesModel::horizontalHeaderDataMap()const { return mHorizontalHeaderDataMap; } /** needed for serialization */ const QMap > AttributesModel::verticalHeaderDataMap()const { return mVerticalHeaderDataMap; } /** needed for serialization */ const QMap AttributesModel::modelDataMap()const { return mModelDataMap; } /** needed for serialization */ void AttributesModel::setDataMap( const QMap > > map ) { mDataMap = map; } /** needed for serialization */ void AttributesModel::setHorizontalHeaderDataMap( const QMap > map ) { mHorizontalHeaderDataMap = map; } /** needed for serialization */ void AttributesModel::setVerticalHeaderDataMap( const QMap > map ) { mVerticalHeaderDataMap = map; } /** needed for serialization */ void AttributesModel::setModelDataMap( const QMap map ) { mModelDataMap = map; } void AttributesModel::setDefaultForRole( int role, const QVariant& value ) { if ( value.isValid() ) { mDefaultsMap.insert( role, value ); } else { // erase the possibily existing value to not let the map grow: QMap::iterator it = mDefaultsMap.find( role ); if ( it != mDefaultsMap.end() ) { mDefaultsMap.erase( it ); } } Q_ASSERT( defaultsForRole( role ) == value ); }