akonadi
contactgroupmodel.cpp
00001 /* 00002 This file is part of Akonadi Contact. 00003 00004 Copyright (c) 2009 Tobias Koenig <tokoe@kde.org> 00005 00006 This library is free software; you can redistribute it and/or modify it 00007 under the terms of the GNU Library General Public License as published by 00008 the Free Software Foundation; either version 2 of the License, or (at your 00009 option) any later version. 00010 00011 This library is distributed in the hope that it will be useful, but WITHOUT 00012 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00013 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00014 License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to the 00018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00019 02110-1301, USA. 00020 */ 00021 00022 #include "contactgroupmodel_p.h" 00023 00024 #include <akonadi/itemfetchjob.h> 00025 #include <akonadi/itemfetchscope.h> 00026 #include <kabc/addressee.h> 00027 #include <kicon.h> 00028 #include <kiconloader.h> 00029 #include <klocale.h> 00030 00031 using namespace Akonadi; 00032 00033 struct GroupMember 00034 { 00035 GroupMember() 00036 : loadingError( false ) 00037 { 00038 } 00039 00040 bool isReference; 00041 KABC::ContactGroup::ContactReference reference; 00042 KABC::ContactGroup::Data data; 00043 KABC::Addressee referencedContact; 00044 bool loadingError; 00045 }; 00046 00047 class ContactGroupModel::Private 00048 { 00049 public: 00050 Private( ContactGroupModel *parent ) 00051 : mParent( parent ) 00052 { 00053 } 00054 00055 void resolveContactReference( const KABC::ContactGroup::ContactReference &reference, int row ) 00056 { 00057 const Item item( reference.uid().toLongLong() ); 00058 00059 ItemFetchJob *job = new ItemFetchJob( item, mParent ); 00060 job->setProperty( "row", row ); 00061 job->fetchScope().fetchFullPayload(); 00062 00063 mParent->connect( job, SIGNAL( result( KJob* ) ), SLOT( itemFetched( KJob* ) ) ); 00064 } 00065 00066 void itemFetched( KJob *job ) 00067 { 00068 const int row = job->property( "row" ).toInt(); 00069 00070 if ( job->error() ) { 00071 mMembers[ row ].loadingError = true; 00072 emit mParent->dataChanged( mParent->index( row, 0, QModelIndex() ), mParent->index( row, 1, QModelIndex() ) ); 00073 return; 00074 } 00075 00076 ItemFetchJob *fetchJob = qobject_cast<ItemFetchJob*>( job ); 00077 00078 if ( fetchJob->items().count() != 1 ) { 00079 mMembers[ row ].loadingError = true; 00080 emit mParent->dataChanged( mParent->index( row, 0, QModelIndex() ), mParent->index( row, 1, QModelIndex() ) ); 00081 return; 00082 } 00083 00084 const Item item = fetchJob->items().first(); 00085 const KABC::Addressee contact = item.payload<KABC::Addressee>(); 00086 00087 GroupMember &member = mMembers[ row ]; 00088 member.referencedContact = contact; 00089 emit mParent->dataChanged( mParent->index( row, 0, QModelIndex() ), mParent->index( row, 1, QModelIndex() ) ); 00090 } 00091 00092 void normalizeMemberList() 00093 { 00094 // check whether a normalization is needed or not 00095 bool needsNormalization = false; 00096 if ( mMembers.isEmpty() ) { 00097 needsNormalization = true; 00098 } else { 00099 for ( int i = 0; i < mMembers.count(); ++i ) { 00100 const GroupMember &member = mMembers[ i ]; 00101 if ( !member.isReference && !(i == mMembers.count() - 1) ) { 00102 if ( member.data.name().isEmpty() && member.data.email().isEmpty() ) { 00103 needsNormalization = true; 00104 break; 00105 } 00106 } 00107 } 00108 00109 const GroupMember &member = mMembers.last(); 00110 if ( member.isReference || !(member.data.name().isEmpty() && member.data.email().isEmpty()) ) 00111 needsNormalization = true; 00112 } 00113 00114 // if not, avoid to update the model and view 00115 if ( !needsNormalization ) 00116 return; 00117 00118 bool foundEmpty = false; 00119 00120 // add an empty line at the end 00121 mParent->beginInsertRows( QModelIndex(), mMembers.count(), mMembers.count() ); 00122 GroupMember member; 00123 member.isReference = false; 00124 mMembers.append( member ); 00125 mParent->endInsertRows(); 00126 00127 // remove all empty lines first except the last line 00128 do { 00129 foundEmpty = false; 00130 for ( int i = 0; i < mMembers.count(); ++i ) { 00131 const GroupMember &member = mMembers[ i ]; 00132 if ( !member.isReference && !(i == mMembers.count() - 1) ) { 00133 if ( member.data.name().isEmpty() && member.data.email().isEmpty() ) { 00134 mParent->beginRemoveRows( QModelIndex(), i, i ); 00135 mMembers.remove( i ); 00136 mParent->endRemoveRows(); 00137 foundEmpty = true; 00138 break; 00139 } 00140 } 00141 } 00142 } while ( foundEmpty ); 00143 } 00144 00145 ContactGroupModel *mParent; 00146 QVector<GroupMember> mMembers; 00147 KABC::ContactGroup mGroup; 00148 QString mLastErrorMessage; 00149 }; 00150 00151 ContactGroupModel::ContactGroupModel( QObject *parent ) 00152 : QAbstractItemModel( parent ), d( new Private( this ) ) 00153 { 00154 } 00155 00156 ContactGroupModel::~ContactGroupModel() 00157 { 00158 delete d; 00159 } 00160 00161 void ContactGroupModel::loadContactGroup( const KABC::ContactGroup &contactGroup ) 00162 { 00163 emit layoutAboutToBeChanged(); 00164 00165 d->mMembers.clear(); 00166 d->mGroup = contactGroup; 00167 00168 for ( uint i = 0; i < d->mGroup.dataCount(); ++i ) { 00169 const KABC::ContactGroup::Data data = d->mGroup.data( i ); 00170 GroupMember member; 00171 member.isReference = false; 00172 member.data = data; 00173 00174 d->mMembers.append( member ); 00175 } 00176 00177 for ( uint i = 0; i < d->mGroup.contactReferenceCount(); ++i ) { 00178 const KABC::ContactGroup::ContactReference reference = d->mGroup.contactReference( i ); 00179 GroupMember member; 00180 member.isReference = true; 00181 member.reference = reference; 00182 00183 d->mMembers.append( member ); 00184 00185 d->resolveContactReference( reference, d->mMembers.count() - 1 ); 00186 } 00187 00188 d->normalizeMemberList(); 00189 00190 emit layoutChanged(); 00191 } 00192 00193 bool ContactGroupModel::storeContactGroup( KABC::ContactGroup &group ) const 00194 { 00195 group.removeAllContactReferences(); 00196 group.removeAllContactData(); 00197 00198 for ( int i = 0; i < d->mMembers.count(); ++i ) { 00199 const GroupMember &member = d->mMembers[ i ]; 00200 if ( member.isReference ) 00201 group.append( member.reference ); 00202 else { 00203 if ( i != (d->mMembers.count() - 1) ) { 00204 if ( member.data.email().isEmpty() ) { 00205 d->mLastErrorMessage = 00206 i18n( "The member with name <b>%1</b> is missing an email address", 00207 member.data.name() ); 00208 return false; 00209 } 00210 group.append( member.data ); 00211 } 00212 } 00213 } 00214 00215 return true; 00216 } 00217 00218 QString ContactGroupModel::lastErrorMessage() const 00219 { 00220 return d->mLastErrorMessage; 00221 } 00222 00223 QModelIndex ContactGroupModel::index( int row, int col, const QModelIndex& ) const 00224 { 00225 return createIndex( row, col, 0 ); 00226 } 00227 00228 QModelIndex ContactGroupModel::parent( const QModelIndex& ) const 00229 { 00230 return QModelIndex(); 00231 } 00232 00233 QVariant ContactGroupModel::data( const QModelIndex &index, int role ) const 00234 { 00235 if ( !index.isValid() ) 00236 return QVariant(); 00237 00238 if ( index.row() < 0 || index.row() >= d->mMembers.count() ) 00239 return QVariant(); 00240 00241 if ( index.column() < 0 || index.column() > 1 ) 00242 return QVariant(); 00243 00244 const GroupMember &member = d->mMembers[ index.row() ]; 00245 00246 if ( role == Qt::DisplayRole ) { 00247 if ( member.loadingError ) { 00248 if ( index.column() == 0 ) 00249 return i18n( "Contact does not exist any more" ); 00250 else 00251 return QString(); 00252 } 00253 00254 if ( member.isReference ) { 00255 if ( index.column() == 0 ) 00256 return member.referencedContact.realName(); 00257 else { 00258 if ( !member.reference.preferredEmail().isEmpty() ) 00259 return member.reference.preferredEmail(); 00260 else 00261 return member.referencedContact.preferredEmail(); 00262 } 00263 } else { 00264 if ( index.column() == 0 ) 00265 return member.data.name(); 00266 else 00267 return member.data.email(); 00268 } 00269 } 00270 00271 if ( role == Qt::DecorationRole ) { 00272 if ( index.column() == 1 ) 00273 return QVariant(); 00274 00275 if ( member.loadingError ) 00276 return KIcon( QLatin1String( "emblem-important" ) ); 00277 00278 if ( index.row() == (d->mMembers.count() - 1) ) 00279 return KIcon( QLatin1String( "contact-new" ) ); 00280 00281 if ( member.isReference ) { 00282 return KIcon( QLatin1String( "x-office-contact" ), KIconLoader::global(), 00283 QStringList() << QLatin1String( "emblem-symbolic-link" ) ); 00284 } else { 00285 return KIcon( QLatin1String( "x-office-contact" ) ); 00286 } 00287 } 00288 00289 if ( role == Qt::EditRole ) { 00290 if ( member.isReference ) { 00291 if ( index.column() == 0 ) 00292 return member.referencedContact.realName(); 00293 else { 00294 if ( !member.reference.preferredEmail().isEmpty() ) 00295 return member.reference.preferredEmail(); 00296 else 00297 return member.referencedContact.preferredEmail(); 00298 } 00299 } else { 00300 if ( index.column() == 0 ) 00301 return member.data.name(); 00302 else 00303 return member.data.email(); 00304 } 00305 } 00306 00307 if ( role == IsReferenceRole ) 00308 return member.isReference; 00309 00310 if ( role == AllEmailsRole ) { 00311 if ( member.isReference ) 00312 return member.referencedContact.emails(); 00313 else 00314 return QStringList(); 00315 } 00316 00317 return QVariant(); 00318 } 00319 00320 bool ContactGroupModel::setData( const QModelIndex &index, const QVariant &value, int role ) 00321 { 00322 if ( !index.isValid() ) 00323 return false; 00324 00325 if ( index.row() < 0 || index.row() >= d->mMembers.count() ) 00326 return false; 00327 00328 if ( index.column() < 0 || index.column() > 1 ) 00329 return false; 00330 00331 GroupMember &member = d->mMembers[ index.row() ]; 00332 00333 if ( role == Qt::EditRole ) { 00334 if ( member.isReference ) { 00335 if ( index.column() == 0 ) { 00336 member.reference.setUid( QString::number( value.toLongLong() ) ); 00337 d->resolveContactReference( member.reference, index.row() ); 00338 } 00339 if ( index.column() == 1 ) { 00340 const QString email = value.toString(); 00341 if ( email != member.referencedContact.preferredEmail() ) { 00342 member.reference.setPreferredEmail( email ); 00343 } else { 00344 member.reference.setPreferredEmail( QString() ); 00345 } 00346 } 00347 } else { 00348 if ( index.column() == 0 ) 00349 member.data.setName( value.toString() ); 00350 else 00351 member.data.setEmail( value.toString() ); 00352 } 00353 00354 d->normalizeMemberList(); 00355 00356 return true; 00357 } 00358 00359 if ( role == IsReferenceRole ) { 00360 if ( (value.toBool() == true) && !member.isReference ) { 00361 member.isReference = true; 00362 } 00363 if ( (value.toBool() == false) && member.isReference ) { 00364 member.isReference = false; 00365 member.data.setName( member.referencedContact.realName() ); 00366 member.data.setEmail( member.referencedContact.preferredEmail() ); 00367 } 00368 00369 return true; 00370 } 00371 00372 return false; 00373 } 00374 00375 QVariant ContactGroupModel::headerData( int section, Qt::Orientation orientation, int role ) const 00376 { 00377 if ( section < 0 || section > 1 ) 00378 return QVariant(); 00379 00380 if ( orientation != Qt::Horizontal ) 00381 return QVariant(); 00382 00383 if ( role != Qt::DisplayRole ) 00384 return QVariant(); 00385 00386 if ( section == 0 ) 00387 return i18nc( "contact's name", "Name" ); 00388 else 00389 return i18nc( "contact's email address", "EMail" ); 00390 } 00391 00392 Qt::ItemFlags ContactGroupModel::flags( const QModelIndex &index ) const 00393 { 00394 if ( !index.isValid() || index.row() < 0 || index.row() >= d->mMembers.count() ) 00395 return Qt::ItemIsEnabled; 00396 00397 if ( d->mMembers[ index.row() ].loadingError ) 00398 return Qt::ItemFlags( Qt::ItemIsEnabled ); 00399 00400 Qt::ItemFlags parentFlags = QAbstractItemModel::flags( index ); 00401 return (parentFlags | Qt::ItemIsEnabled | Qt::ItemIsEditable); 00402 } 00403 00404 int ContactGroupModel::columnCount( const QModelIndex &parent ) const 00405 { 00406 if ( !parent.isValid() ) 00407 return 2; 00408 else 00409 return 0; 00410 } 00411 00412 int ContactGroupModel::rowCount( const QModelIndex &parent ) const 00413 { 00414 if ( !parent.isValid() ) 00415 return d->mMembers.count(); 00416 else 00417 return 0; 00418 } 00419 00420 bool ContactGroupModel::removeRows( int row, int count, const QModelIndex &parent ) 00421 { 00422 if ( parent.isValid() ) 00423 return false; 00424 00425 beginRemoveRows( QModelIndex(), row, row + count - 1 ); 00426 for ( int i = 0; i < count; ++i ) 00427 d->mMembers.remove( row ); 00428 endRemoveRows(); 00429 00430 return true; 00431 } 00432 00433 #include "contactgroupmodel_p.moc"