2010-10-26 14:01:59 +02:00
/*
* Copyright 2010 by Marco Martin < mart @ kde . org >
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation ; either version 2 , 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 Library General Public
* License along with this program ; if not , write to the
* Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*/
# include "datamodel.h"
2010-10-26 15:18:19 +02:00
# include "datasource_p.h"
2010-10-26 14:01:59 +02:00
2011-04-07 22:37:34 +02:00
# include <QTimer>
2010-10-26 14:01:59 +02:00
# include <KDebug>
namespace Plasma
{
2010-10-28 15:10:10 +02:00
SortFilterModel : : SortFilterModel ( QObject * parent )
2010-10-26 23:19:04 +02:00
: QSortFilterProxyModel ( parent )
2010-10-26 14:01:59 +02:00
{
2010-10-28 15:10:10 +02:00
setObjectName ( " SortFilterModel " ) ;
2010-10-26 23:19:04 +02:00
setDynamicSortFilter ( true ) ;
2010-12-05 23:23:13 +01:00
connect ( this , SIGNAL ( rowsInserted ( const QModelIndex & , int , int ) ) ,
this , SIGNAL ( countChanged ( ) ) ) ;
connect ( this , SIGNAL ( rowsRemoved ( const QModelIndex & , int , int ) ) ,
this , SIGNAL ( countChanged ( ) ) ) ;
connect ( this , SIGNAL ( modelReset ( ) ) ,
this , SIGNAL ( countChanged ( ) ) ) ;
2010-10-26 14:01:59 +02:00
}
2010-10-28 15:10:10 +02:00
SortFilterModel : : ~ SortFilterModel ( )
2010-10-26 14:01:59 +02:00
{
}
2010-10-28 15:10:10 +02:00
void SortFilterModel : : syncRoleNames ( )
2010-10-26 23:19:04 +02:00
{
2010-10-28 15:06:40 +02:00
m_roleIds . clear ( ) ;
2010-10-26 23:19:04 +02:00
2010-10-28 15:06:40 +02:00
setRoleNames ( sourceModel ( ) - > roleNames ( ) ) ;
QHash < int , QByteArray > : : const_iterator i ;
for ( i = roleNames ( ) . constBegin ( ) ; i ! = roleNames ( ) . constEnd ( ) ; + + i ) {
m_roleIds [ i . value ( ) ] = i . key ( ) ;
}
setFilterRole ( m_filterRole ) ;
setSortRole ( m_sortRole ) ;
2010-10-26 23:19:04 +02:00
}
2010-10-28 15:10:10 +02:00
int SortFilterModel : : roleNameToId ( const QString & name )
2010-10-26 23:19:04 +02:00
{
2010-10-28 15:06:40 +02:00
if ( ! m_roleIds . contains ( name ) ) {
return - 1 ;
}
return m_roleIds . value ( name ) ;
2010-10-26 23:19:04 +02:00
}
2010-10-28 15:10:10 +02:00
void SortFilterModel : : setModel ( QObject * source )
2010-10-26 23:19:04 +02:00
{
2010-10-28 15:06:40 +02:00
QAbstractItemModel * model = qobject_cast < QAbstractItemModel * > ( source ) ;
if ( ! model ) {
kWarning ( ) < < " Error: QAbstractItemModel type expected " ;
return ;
}
connect ( model , SIGNAL ( modelReset ( ) ) , this , SLOT ( syncRoleNames ( ) ) ) ;
QSortFilterProxyModel : : setSourceModel ( model ) ;
2010-10-26 23:19:04 +02:00
}
2010-10-28 15:06:40 +02:00
2010-10-28 15:10:10 +02:00
void SortFilterModel : : setFilterRegExp ( const QString & exp )
2010-10-26 23:19:04 +02:00
{
2010-12-06 14:28:45 +01:00
//FIXME: this delaying of the reset signal seems to make the views behave a bit better, i.e. less holes and avoids some crashes, in theory shouldn't be necessary
blockSignals ( true ) ;
2010-10-29 14:27:38 +02:00
QSortFilterProxyModel : : setFilterRegExp ( QRegExp ( exp , Qt : : CaseInsensitive ) ) ;
2010-12-06 14:28:45 +01:00
blockSignals ( false ) ;
reset ( ) ;
2010-10-26 23:19:04 +02:00
}
2010-10-28 15:10:10 +02:00
QString SortFilterModel : : filterRegExp ( ) const
2010-10-26 23:19:04 +02:00
{
return QSortFilterProxyModel : : filterRegExp ( ) . pattern ( ) ;
}
2010-10-28 15:10:10 +02:00
void SortFilterModel : : setFilterRole ( const QString & role )
2010-10-26 23:19:04 +02:00
{
2010-10-28 15:06:40 +02:00
QSortFilterProxyModel : : setFilterRole ( roleNameToId ( role ) ) ;
2010-10-26 23:19:04 +02:00
m_filterRole = role ;
}
2010-10-28 15:10:10 +02:00
QString SortFilterModel : : filterRole ( ) const
2010-10-26 23:19:04 +02:00
{
return m_filterRole ;
}
2010-10-28 15:10:10 +02:00
void SortFilterModel : : setSortRole ( const QString & role )
2010-10-26 23:19:04 +02:00
{
2010-10-28 15:06:40 +02:00
QSortFilterProxyModel : : setSortRole ( roleNameToId ( role ) ) ;
2010-10-26 23:19:04 +02:00
m_sortRole = role ;
2010-10-27 23:19:03 +02:00
sort ( 0 , sortOrder ( ) ) ;
2010-10-26 23:19:04 +02:00
}
2010-10-28 15:10:10 +02:00
QString SortFilterModel : : sortRole ( ) const
2010-10-26 23:19:04 +02:00
{
return m_sortRole ;
}
2010-10-28 15:10:10 +02:00
void SortFilterModel : : setSortOrder ( const Qt : : SortOrder order )
2010-10-27 23:19:03 +02:00
{
sort ( 0 , order ) ;
}
2010-10-26 23:19:04 +02:00
2010-10-28 15:10:10 +02:00
DataModel : : DataModel ( QObject * parent )
2010-10-26 23:19:04 +02:00
: QAbstractItemModel ( parent ) ,
2011-01-20 23:03:52 +01:00
m_dataSource ( 0 ) ,
2011-02-19 14:10:59 +01:00
m_maxRoleId ( Qt : : UserRole + 1 )
2010-10-26 23:19:04 +02:00
{
2011-02-19 14:10:59 +01:00
//There is one reserved role name: DataEngineSource
m_roleNames [ m_maxRoleId ] = " DataEngineSource " ;
m_roleIds [ " DataEngineSource " ] = m_maxRoleId ;
+ + m_maxRoleId ;
2010-10-28 15:10:10 +02:00
setObjectName ( " DataModel " ) ;
2010-12-05 23:23:13 +01:00
connect ( this , SIGNAL ( rowsInserted ( const QModelIndex & , int , int ) ) ,
this , SIGNAL ( countChanged ( ) ) ) ;
connect ( this , SIGNAL ( rowsRemoved ( const QModelIndex & , int , int ) ) ,
this , SIGNAL ( countChanged ( ) ) ) ;
connect ( this , SIGNAL ( modelReset ( ) ) ,
this , SIGNAL ( countChanged ( ) ) ) ;
2010-10-26 23:19:04 +02:00
}
2010-10-28 15:06:40 +02:00
DataModel : : ~ DataModel ( )
2010-10-26 23:19:04 +02:00
{
}
2010-11-08 11:27:36 +01:00
void DataModel : : dataUpdated ( const QString & sourceName , const Plasma : : DataEngine : : Data & data )
{
if ( ! m_keyRoleFilter . isEmpty ( ) ) {
2011-04-09 20:14:22 +02:00
QRegExp sourceRegExp ( m_sourceFilter ) ;
2010-11-08 11:27:36 +01:00
//a key that matches the one we want exists and is a list of DataEngine::Data
2011-04-09 20:14:22 +02:00
if ( data . contains ( m_keyRoleFilter ) & &
data . value ( m_keyRoleFilter ) . canConvert < QVariantList > ( ) & &
//matches the source filter regexp?
( m_sourceFilter . isEmpty ( ) | | ( sourceRegExp . isValid ( ) & & sourceRegExp . exactMatch ( sourceName ) ) ) ) {
2010-11-08 11:27:36 +01:00
setItems ( sourceName , data . value ( m_keyRoleFilter ) . value < QVariantList > ( ) ) ;
//try to match the key we want with a regular expression if set
} else {
QRegExp regExp ( m_keyRoleFilter ) ;
2011-04-09 20:14:22 +02:00
if ( regExp . isValid ( ) & &
//matches the source filter regexp?
( m_sourceFilter . isEmpty ( ) | | ( sourceRegExp . isValid ( ) & & sourceRegExp . exactMatch ( sourceName ) ) ) ) {
2010-11-08 11:27:36 +01:00
QHash < QString , QVariant > : : const_iterator i ;
QVariantList list ;
for ( i = data . constBegin ( ) ; i ! = data . constEnd ( ) ; + + i ) {
if ( regExp . exactMatch ( i . key ( ) ) ) {
list . append ( i . value ( ) ) ;
}
}
setItems ( sourceName , list ) ;
}
2010-10-26 15:18:19 +02:00
}
2010-11-08 11:27:36 +01:00
//an item is represented by a source: keys are roles m_roleLevel == FirstLevel
} else {
2010-10-26 15:18:19 +02:00
QVariantList list ;
2010-11-08 11:27:36 +01:00
2011-02-19 14:10:59 +01:00
if ( ! m_dataSource - > data ( ) . isEmpty ( ) ) {
QVariantMap : : const_iterator i = m_dataSource - > data ( ) . constBegin ( ) ;
2011-04-09 20:14:22 +02:00
QRegExp sourceRegExp ( m_sourceFilter ) ;
2011-02-19 14:10:59 +01:00
while ( i ! = m_dataSource - > data ( ) . constEnd ( ) ) {
QVariant value = i . value ( ) ;
2011-04-09 20:14:22 +02:00
if ( value . isValid ( ) & &
value . canConvert < Plasma : : DataEngine : : Data > ( ) & &
//matches the source filter regexp?
( m_sourceFilter . isEmpty ( ) | | ( sourceRegExp . isValid ( ) & & sourceRegExp . exactMatch ( i . key ( ) ) ) ) ) {
2011-02-19 14:10:59 +01:00
Plasma : : DataEngine : : Data data = value . value < Plasma : : DataEngine : : Data > ( ) ;
data [ " DataEngineSource " ] = i . key ( ) ;
list . append ( data ) ;
}
+ + i ;
}
2010-10-26 15:18:19 +02:00
}
2010-11-08 11:27:36 +01:00
setItems ( QString ( ) , list ) ;
2010-10-26 15:18:19 +02:00
}
}
2010-10-28 15:06:40 +02:00
void DataModel : : setDataSource ( QObject * object )
2010-10-26 15:18:19 +02:00
{
DataSource * source = qobject_cast < DataSource * > ( object ) ;
if ( ! source ) {
kWarning ( ) < < " Error: DataSource type expected " ;
return ;
}
if ( m_dataSource = = source ) {
return ;
}
disconnect ( m_dataSource , 0 , this , 0 ) ;
m_dataSource = source ;
connect ( m_dataSource , SIGNAL ( newData ( const QString & , const Plasma : : DataEngine : : Data & ) ) ,
this , SLOT ( dataUpdated ( const QString & , const Plasma : : DataEngine : : Data & ) ) ) ;
2010-11-09 00:07:25 +01:00
connect ( m_dataSource , SIGNAL ( sourceRemoved ( const QString & ) ) , this , SLOT ( removeSource ( const QString & ) ) ) ;
connect ( m_dataSource , SIGNAL ( sourceDisconnected ( const QString & ) ) , this , SLOT ( removeSource ( const QString & ) ) ) ;
2010-10-26 15:18:19 +02:00
}
2010-10-28 15:06:40 +02:00
QObject * DataModel : : dataSource ( ) const
2010-10-26 15:18:19 +02:00
{
return m_dataSource ;
}
2011-04-03 15:29:54 +02:00
void DataModel : : setKeyRoleFilter ( const QString & key )
2010-10-26 15:18:19 +02:00
{
2010-11-08 11:27:36 +01:00
if ( m_keyRoleFilter = = key ) {
2010-10-26 15:18:19 +02:00
return ;
}
2010-11-08 11:27:36 +01:00
m_keyRoleFilter = key ;
2010-10-26 15:18:19 +02:00
}
2011-04-09 20:14:22 +02:00
void DataModel : : setSourceFilter ( const QString & key )
{
if ( m_sourceFilter = = key ) {
return ;
}
m_sourceFilter = key ;
}
QString DataModel : : sourceFilter ( ) const
{
return m_sourceFilter ;
}
2010-11-08 11:27:36 +01:00
QString DataModel : : keyRoleFilter ( ) const
2010-10-26 15:18:19 +02:00
{
2010-11-08 11:27:36 +01:00
return m_keyRoleFilter ;
2010-10-26 15:18:19 +02:00
}
2010-10-26 14:01:59 +02:00
2010-11-08 11:27:36 +01:00
void DataModel : : setItems ( const QString & sourceName , const QVariantList & list )
2010-10-26 14:01:59 +02:00
{
emit modelAboutToBeReset ( ) ;
//convert to vector, so data() will be O(1)
2010-11-08 11:27:36 +01:00
m_items [ sourceName ] = list . toVector ( ) ;
2010-10-26 14:01:59 +02:00
2010-10-26 17:48:39 +02:00
if ( ! list . isEmpty ( ) ) {
2010-10-26 19:32:08 +02:00
if ( list . first ( ) . canConvert < QVariantHash > ( ) ) {
2010-10-28 09:44:05 +02:00
foreach ( const QString & roleName , list . first ( ) . value < QVariantHash > ( ) . keys ( ) ) {
2011-01-20 23:03:52 +01:00
if ( ! m_roleIds . contains ( roleName ) ) {
+ + m_maxRoleId ;
m_roleNames [ m_maxRoleId ] = roleName . toLatin1 ( ) ;
m_roleIds [ roleName ] = m_maxRoleId ;
}
2010-10-26 19:32:08 +02:00
}
} else {
2010-10-28 09:44:05 +02:00
foreach ( const QString & roleName , list . first ( ) . value < QVariantMap > ( ) . keys ( ) ) {
2011-01-20 23:03:52 +01:00
if ( ! m_roleIds . contains ( roleName ) ) {
+ + m_maxRoleId ;
m_roleNames [ m_maxRoleId ] = roleName . toLatin1 ( ) ;
m_roleIds [ roleName ] = m_maxRoleId ;
}
2010-10-26 19:32:08 +02:00
}
2010-10-26 17:48:39 +02:00
}
2010-10-26 23:19:04 +02:00
2010-10-26 17:48:39 +02:00
setRoleNames ( m_roleNames ) ;
2010-10-26 14:01:59 +02:00
}
2011-04-07 22:37:34 +02:00
setRoleNames ( m_roleNames ) ;
2010-10-26 14:01:59 +02:00
//make the declarative view reload everything,
//would be nice an incremental update but is not possible
emit modelReset ( ) ;
}
2010-11-09 00:07:25 +01:00
void DataModel : : removeSource ( const QString & sourceName )
{
//FIXME: this could be way more efficient by not resetting the whole model
2010-11-09 00:10:16 +01:00
//FIXME: find a way to remove only the proper things also in the case where sources are items
2010-11-09 00:07:25 +01:00
emit modelAboutToBeReset ( ) ;
m_items . remove ( sourceName ) ;
emit modelReset ( ) ;
}
2010-10-28 15:06:40 +02:00
QVariant DataModel : : data ( const QModelIndex & index , int role ) const
2010-10-26 14:01:59 +02:00
{
if ( ! index . isValid ( ) | | index . column ( ) > 0 | |
2010-11-08 11:27:36 +01:00
index . row ( ) < 0 | | index . row ( ) > = countItems ( ) ) {
2010-10-26 14:01:59 +02:00
return QVariant ( ) ;
}
2010-11-08 11:27:36 +01:00
int count = 0 ;
int actualRow = 0 ;
QString source ;
QMap < QString , QVector < QVariant > > : : const_iterator i ;
for ( i = m_items . constBegin ( ) ; i ! = m_items . constEnd ( ) ; + + i ) {
const int oldCount = count ;
count + = i . value ( ) . count ( ) ;
if ( index . row ( ) < count ) {
source = i . key ( ) ;
actualRow = index . row ( ) - oldCount ;
break ;
}
}
2011-02-19 14:10:59 +01:00
//is it the reserved role: DataEngineSource ?
//also, if each source is an item DataEngineSource is a role between all the others, otherwise we know it from the role variable
if ( ! m_keyRoleFilter . isEmpty ( ) & & m_roleNames . value ( role ) = = " DataEngineSource " ) {
return source ;
//sub items are some times QVariantHash some times QVariantMaps
} else if ( m_items . value ( source ) . value ( actualRow ) . canConvert < QVariantHash > ( ) ) {
2010-11-08 11:27:36 +01:00
return m_items . value ( source ) . value ( actualRow ) . value < QVariantHash > ( ) . value ( m_roleNames . value ( role ) ) ;
2010-10-26 19:32:08 +02:00
} else {
2010-11-08 11:27:36 +01:00
return m_items . value ( source ) . value ( actualRow ) . value < QVariantMap > ( ) . value ( m_roleNames . value ( role ) ) ;
2010-10-26 19:32:08 +02:00
}
2010-10-26 14:01:59 +02:00
}
2010-10-28 15:06:40 +02:00
QVariant DataModel : : headerData ( int section , Qt : : Orientation orientation , int role ) const
2010-10-26 14:01:59 +02:00
{
Q_UNUSED ( section )
Q_UNUSED ( orientation )
Q_UNUSED ( role )
return QVariant ( ) ;
}
2010-10-28 15:06:40 +02:00
QModelIndex DataModel : : index ( int row , int column , const QModelIndex & parent ) const
2010-10-26 14:01:59 +02:00
{
2010-11-08 11:27:36 +01:00
if ( parent . isValid ( ) | | column > 0 | | row < 0 | | row > = countItems ( ) ) {
2010-10-26 14:01:59 +02:00
return QModelIndex ( ) ;
}
return createIndex ( row , column , 0 ) ;
}
2010-10-28 15:06:40 +02:00
QModelIndex DataModel : : parent ( const QModelIndex & child ) const
2010-10-26 14:01:59 +02:00
{
Q_UNUSED ( child )
return QModelIndex ( ) ;
}
2010-10-28 15:06:40 +02:00
int DataModel : : rowCount ( const QModelIndex & parent ) const
2010-10-26 14:01:59 +02:00
{
//this is not a tree
//TODO: make it possible some day?
if ( parent . isValid ( ) ) {
return 0 ;
}
2010-11-08 11:27:36 +01:00
return countItems ( ) ;
2010-10-26 14:01:59 +02:00
}
2010-10-28 15:06:40 +02:00
int DataModel : : columnCount ( const QModelIndex & parent ) const
2010-10-26 14:01:59 +02:00
{
if ( parent . isValid ( ) ) {
return 0 ;
}
return 1 ;
}
2010-10-28 15:06:40 +02:00
int DataModel : : roleNameToId ( const QString & name )
2010-10-26 23:19:04 +02:00
{
if ( ! m_roleIds . contains ( name ) ) {
return - 1 ;
}
return m_roleIds . value ( name ) ;
}
2010-10-26 14:01:59 +02:00
}
2010-10-28 15:10:10 +02:00
2010-10-26 14:01:59 +02:00
# include "datamodel.moc"