• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdepim
  • Sitemap
  • Contact Us
 

kleopatra

keylistmodel.cpp

Go to the documentation of this file.
00001 /* -*- mode: c++; c-basic-offset:4 -*-
00002     models/keylistmodel.cpp
00003 
00004     This file is part of Kleopatra, the KDE keymanager
00005     Copyright (c) 2007 Klarälvdalens Datakonsult AB
00006 
00007     Kleopatra is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     Kleopatra is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015     General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00020 
00021     In addition, as a special exception, the copyright holders give
00022     permission to link the code of this program with any edition of
00023     the Qt library by Trolltech AS, Norway (or with modified versions
00024     of Qt that use the same license as Qt), and distribute linked
00025     combinations including the two.  You must obey the GNU General
00026     Public License in all respects for all of the code used other than
00027     Qt.  If you modify this file, you may extend this exception to
00028     your version of the file, but you are not obligated to do so.  If
00029     you do not wish to do so, delete this exception statement from
00030     your version.
00031 */
00032 
00033 #include <config-kleopatra.h>
00034 
00035 #include "keylistmodel.h"
00036 #include "predicates.h"
00037 
00038 #ifdef KLEO_MODEL_TEST
00039 # include "modeltest.h"
00040 #endif
00041 
00042 #include <utils/formatting.h>
00043 
00044 #include <kleo/keyfiltermanager.h>
00045 #include <kleo/keyfilter.h>
00046 
00047 #include <KLocale>
00048 #include <KIcon>
00049 
00050 #include <QDateTime>
00051 #include <QFont>
00052 #include <QColor>
00053 #include <QApplication>
00054 
00055 #include <gpgme++/key.h>
00056 
00057 #include <boost/bind.hpp>
00058 
00059 #include <boost/graph/adjacency_list.hpp>
00060 #include <boost/graph/topological_sort.hpp>
00061 
00062 #include <algorithm>
00063 #include <vector>
00064 #include <map>
00065 #include <set>
00066 #include <iterator>
00067 #include <cassert>
00068 
00069 #ifdef __GNUC__
00070 #include <ext/algorithm> // for is_sorted
00071 #endif
00072 
00073 using namespace GpgME;
00074 using namespace Kleo;
00075 using namespace boost;
00076 
00077 class AbstractKeyListModel::Private {
00078 public:
00079     Private() : m_toolTipOptions( Formatting::Validity ) {}
00080     int m_toolTipOptions;
00081 };
00082 AbstractKeyListModel::AbstractKeyListModel( QObject * p )
00083     : QAbstractItemModel( p ), KeyListModelInterface(), d( new Private )
00084 {
00085 
00086 }
00087 
00088 AbstractKeyListModel::~AbstractKeyListModel() {}
00089 
00090 void AbstractKeyListModel::setToolTipOptions( int opts )
00091 {
00092     d->m_toolTipOptions = opts;
00093 }
00094 
00095 int AbstractKeyListModel::toolTipOptions() const
00096 {
00097     return d->m_toolTipOptions;
00098 }
00099 
00100 Key AbstractKeyListModel::key( const QModelIndex & idx ) const {
00101     if ( idx.isValid() )
00102         return doMapToKey( idx );
00103     else
00104         return Key::null;
00105 }
00106 
00107 std::vector<Key> AbstractKeyListModel::keys( const QList<QModelIndex> & indexes ) const {
00108     std::vector<Key> result;
00109     result.reserve( indexes.size() );
00110     std::transform( indexes.begin(), indexes.end(),
00111                     std::back_inserter( result ),
00112                     bind( &AbstractKeyListModel::key, this, _1 ) );
00113     result.erase( std::unique( result.begin(), result.end(), _detail::ByFingerprint<std::equal_to>() ), result.end() );
00114     return result;
00115 }
00116 
00117 QModelIndex AbstractKeyListModel::index( const Key & key, int col ) const {
00118     if ( key.isNull() || col < 0 || col >= NumColumns )
00119         return QModelIndex();
00120     else
00121         return doMapFromKey( key, col );
00122 }
00123 
00124 QList<QModelIndex> AbstractKeyListModel::indexes( const std::vector<Key> & keys ) const {
00125     QList<QModelIndex> result;
00126     std::transform( keys.begin(), keys.end(),
00127                     std::back_inserter( result ),
00128                     // if some compilers are complaining about ambigious overloads, use this line instead:
00129                     //bind( static_cast<QModelIndex(AbstractKeyListModel::*)(const Key&,int)const>( &AbstractKeyListModel::index ), this, _1, 0 ) );
00130                     bind( &AbstractKeyListModel::index, this, _1, 0 ) );
00131     return result;
00132 }
00133 
00134 void AbstractKeyListModel::setKeys( const std::vector<Key> & keys ) {
00135     clear();
00136     addKeys( keys );
00137 }
00138 
00139 QModelIndex AbstractKeyListModel::addKey( const Key & key ) {
00140     const std::vector<Key> vec( 1, key );
00141     const QList<QModelIndex> l = doAddKeys( vec );
00142     return l.empty() ? QModelIndex() : l.front() ;
00143 }
00144 
00145 void AbstractKeyListModel::removeKey( const Key & key ) {
00146     if ( key.isNull() )
00147         return;
00148     doRemoveKey( key );
00149 }
00150 
00151 QList<QModelIndex> AbstractKeyListModel::addKeys( const std::vector<Key> & keys ) {
00152     std::vector<Key> sorted;
00153     sorted.reserve( keys.size() );
00154     std::remove_copy_if( keys.begin(), keys.end(),
00155              std::back_inserter( sorted ),
00156              bind( &Key::isNull, _1 ) );
00157     std::sort( sorted.begin(), sorted.end(), _detail::ByFingerprint<std::less>() );
00158     return doAddKeys( sorted );
00159 }
00160 
00161 void AbstractKeyListModel::clear() {
00162     doClear();
00163     reset();
00164 }
00165 
00166 int AbstractKeyListModel::columnCount( const QModelIndex & ) const {
00167     return NumColumns;
00168 }
00169 
00170 QVariant AbstractKeyListModel::headerData( int section, Qt::Orientation o, int role ) const {
00171     if ( o == Qt::Horizontal )
00172         if ( role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole )
00173             switch ( section ) {
00174             case PrettyName:       return i18n( "Name" );
00175             case PrettyEMail:      return i18n( "E-Mail" );
00176             case ValidFrom:        return i18n( "Valid From" );
00177             case ValidUntil:       return i18n( "Valid Until" );
00178             case TechnicalDetails: return i18n( "Details" );
00179             case Fingerprint:      return i18n( "Fingerprint" );
00180             case NumColumns:       ;
00181             }
00182     return QVariant();
00183 }
00184 
00185 static QVariant returnIfValid( const QColor & t ) {
00186     if ( t.isValid() )
00187         return t;
00188     else
00189         return QVariant();
00190 }
00191 
00192 static QVariant returnIfValidIcon( const QString & t ) {
00193     if ( !t.isEmpty() )
00194         return QIcon( KIcon( t ) );
00195     else
00196         return QVariant();
00197 }
00198 
00199 QVariant AbstractKeyListModel::data( const QModelIndex & index, int role ) const {
00200     const Key key = this->key( index );
00201     if ( key.isNull() )
00202         return QVariant();
00203 
00204     const int column = index.column();
00205 
00206     if ( role == Qt::DisplayRole || role == Qt::EditRole )
00207         switch ( column ) {
00208         case PrettyName:
00209             return Formatting::prettyName( key );
00210         case PrettyEMail:
00211             return Formatting::prettyEMail( key );
00212         case ValidFrom:
00213         if ( role == Qt::EditRole )
00214         return Formatting::creationDate( key );
00215         else
00216         return Formatting::creationDateString( key );
00217         case ValidUntil:
00218         if ( role == Qt::EditRole )
00219         return Formatting::expirationDate( key );
00220         else
00221                 return Formatting::expirationDateString( key );
00222         case TechnicalDetails:
00223             return Formatting::type( key );
00224         case Fingerprint:
00225             return QString::fromLatin1( key.primaryFingerprint() );
00226         case NumColumns:
00227             break;
00228         }
00229     else if ( role == Qt::ToolTipRole )
00230         return Formatting::toolTip( key, toolTipOptions() );
00231     else if ( role == Qt::FontRole ) {
00232         QFont font = qApp->font(); // ### correct font?
00233         if ( column == Fingerprint )
00234             font.setFamily( "courier" );
00235         if ( const shared_ptr<KeyFilter> & filter = KeyFilterManager::instance()->filterMatching( key, KeyFilter::Appearance ) )
00236             return filter->font( font );
00237         else
00238             return font;
00239     } else if ( role == Qt::DecorationRole || role == Qt::BackgroundRole || role == Qt::ForegroundRole ) {
00240         if ( const shared_ptr<KeyFilter> & filter = KeyFilterManager::instance()->filterMatching( key, KeyFilter::Appearance ) ) {
00241             switch ( role ) {
00242             case Qt::DecorationRole: return column == Icon ? returnIfValidIcon( filter->icon() ) : QVariant() ;
00243             case Qt::BackgroundRole: return returnIfValid( filter->bgColor() );
00244             case Qt::ForegroundRole: return returnIfValid( filter->fgColor() );
00245             default: ; // silence compiler
00246             }
00247         }
00248     } else if ( role == Qt::TextAlignmentRole ) { // needed?
00249     }
00250     return QVariant();
00251 }
00252 
00253 
00254 namespace {
00255     template <typename Base>
00256     class TableModelMixin : public Base {
00257     public:
00258         explicit TableModelMixin( QObject * p=0 ) : Base( p ) {}
00259         ~TableModelMixin() {}
00260 
00261         using Base::index;
00262         /* reimp */ QModelIndex index( int row, int column, const QModelIndex & pidx=QModelIndex() ) const {
00263             return this->hasIndex( row, column, pidx ) ? this->createIndex( row, column, 0 ) : QModelIndex() ;
00264         }
00265 
00266     private:
00267         /* reimp */ QModelIndex parent( const QModelIndex & ) const { return QModelIndex(); }
00268         /* reimp */ bool hasChildren( const QModelIndex & pidx ) const {
00269             return ( pidx.model() == this || !pidx.isValid() ) && this->rowCount( pidx ) > 0 && this->columnCount( pidx ) > 0 ;
00270         }
00271     };
00272 
00273     class FlatKeyListModel
00274 #ifndef Q_MOC_RUN
00275         : public TableModelMixin<AbstractKeyListModel>
00276 #else
00277         : public AbstractKeyListModel
00278 #endif
00279     {
00280         Q_OBJECT
00281     public:
00282         explicit FlatKeyListModel( QObject * parent=0 );
00283         ~FlatKeyListModel();
00284 
00285         /* reimp */ int rowCount( const QModelIndex & pidx ) const { return pidx.isValid() ? 0 : mKeysByFingerprint.size() ; }
00286 
00287     private:
00288         /* reimp */ Key doMapToKey( const QModelIndex & index ) const;
00289         /* reimp */ QModelIndex doMapFromKey( const Key & key, int col ) const;
00290         /* reimp */ QList<QModelIndex> doAddKeys( const std::vector<Key> & keys );
00291         /* reimp */ void doRemoveKey( const Key & key );
00292         /* reimp */ void doClear() {
00293             mKeysByFingerprint.clear();
00294         }
00295 
00296     private:
00297         std::vector<Key> mKeysByFingerprint;
00298     };
00299 
00300     class HierarchicalKeyListModel : public AbstractKeyListModel {
00301         Q_OBJECT
00302     public:
00303         explicit HierarchicalKeyListModel( QObject * parent=0 );
00304         ~HierarchicalKeyListModel();
00305 
00306         /* reimp */ int rowCount( const QModelIndex & pidx ) const;
00307     using AbstractKeyListModel::index;
00308         /* reimp */ QModelIndex index( int row, int col, const QModelIndex & pidx ) const;
00309         /* reimp */ QModelIndex parent( const QModelIndex & idx ) const;
00310 
00311     bool hasChildren( const QModelIndex & pidx ) const { return rowCount( pidx ) > 0 ; }
00312 
00313     private:
00314         /* reimp */ Key doMapToKey( const QModelIndex & index ) const;
00315         /* reimp */ QModelIndex doMapFromKey( const Key & key, int col ) const;
00316         /* reimp */ QList<QModelIndex> doAddKeys( const std::vector<Key> & keys );
00317         /* reimp */ void doRemoveKey( const Key & key );
00318         /* reimp */ void doClear() {
00319             mTopLevels.clear();
00320             mKeysByFingerprint.clear();
00321             mKeysByExistingParent.clear();
00322             mKeysByNonExistingParent.clear();
00323         }
00324 
00325     private:
00326         void addTopLevelKey( const Key & key );
00327         void addKeyWithParent( const char * issuer_fpr, const Key & key );
00328         void addKeyWithoutParent( const char * issuer_fpr, const Key & key );
00329 
00330     private:
00331         typedef std::map< std::string, std::vector<Key> > Map;
00332         std::vector<Key> mKeysByFingerprint; // all keys
00333         Map mKeysByExistingParent, mKeysByNonExistingParent; // parent->child map
00334         std::vector<Key> mTopLevels; // all roots + parent-less
00335     };
00336 
00337     static const char * cleanChainID( const Key & key ) {
00338         if ( key.isRoot() )
00339             return "";
00340         if ( const char * chid = key.chainID() )
00341         return chid;
00342         return "";
00343     }
00344 
00345 }
00346 
00347 
00348 FlatKeyListModel::FlatKeyListModel( QObject * p )
00349     : TableModelMixin<AbstractKeyListModel>( p ),
00350       mKeysByFingerprint()
00351 {
00352 
00353 }
00354 
00355 FlatKeyListModel::~FlatKeyListModel() {}
00356 
00357 Key FlatKeyListModel::doMapToKey( const QModelIndex & idx ) const {
00358     assert( idx.isValid() );
00359     if ( static_cast<unsigned>( idx.row() ) < mKeysByFingerprint.size() && idx.column() < NumColumns )
00360         return mKeysByFingerprint[ idx.row() ];
00361     else
00362         return Key::null;
00363 }
00364 
00365 QModelIndex FlatKeyListModel::doMapFromKey( const Key & key, int col ) const {
00366     assert( !key.isNull() );
00367     const std::vector<Key>::const_iterator it
00368         = qBinaryFind( mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
00369                        key, _detail::ByFingerprint<std::less>() );
00370     if ( it == mKeysByFingerprint.end() )
00371         return QModelIndex();
00372     else
00373         return createIndex( it - mKeysByFingerprint.begin(), col );
00374 }
00375 
00376 QList<QModelIndex> FlatKeyListModel::doAddKeys( const std::vector<Key> & keys ) {
00377 #ifdef __GNUC__
00378     assert( __gnu_cxx::is_sorted( keys.begin(), keys.end(), _detail::ByFingerprint<std::less>() ) );
00379 #endif
00380     if ( keys.empty() )
00381         return QList<QModelIndex>();
00382 
00383     for ( std::vector<Key>::const_iterator it = keys.begin(), end = keys.end() ; it != end ; ++it ) {
00384 
00385     // find an insertion point:
00386         const std::vector<Key>::iterator pos = std::upper_bound( mKeysByFingerprint.begin(), mKeysByFingerprint.end(), *it, _detail::ByFingerprint<std::less>() );
00387         const unsigned int idx = std::distance( mKeysByFingerprint.begin(), pos );
00388 
00389     if ( idx > 0 && qstrcmp( mKeysByFingerprint[idx-1].primaryFingerprint(), it->primaryFingerprint() ) == 0 ) {
00390         // key existed before - replace with new one:
00391         mKeysByFingerprint[idx-1] = *it;
00392         emit dataChanged( createIndex( idx-1, 0 ), createIndex( idx-1, NumColumns-1 ) );
00393     } else {
00394         // new key - insert:
00395         beginInsertRows( QModelIndex(), idx, idx );
00396         mKeysByFingerprint.insert( pos, *it );
00397         endInsertRows();
00398     }
00399     }
00400 
00401     return indexes( keys );
00402 }
00403 
00404 
00405 void FlatKeyListModel::doRemoveKey( const Key & key ) {
00406     const std::vector<Key>::iterator it
00407         = qBinaryFind( mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
00408                        key, _detail::ByFingerprint<std::less>() );
00409     if ( it == mKeysByFingerprint.end() )
00410         return;
00411 
00412     const unsigned int row = std::distance( mKeysByFingerprint.begin(), it );
00413     beginRemoveRows( QModelIndex(), row, row );
00414     mKeysByFingerprint.erase( it );
00415     endRemoveRows();
00416 }
00417 
00418 
00419 
00420 
00421 
00422 
00423 
00424 
00425 HierarchicalKeyListModel::HierarchicalKeyListModel( QObject * p )
00426     : AbstractKeyListModel( p ),
00427       mKeysByFingerprint(),
00428       mKeysByExistingParent(),
00429       mKeysByNonExistingParent(),
00430       mTopLevels()
00431 {
00432 
00433 }
00434 
00435 HierarchicalKeyListModel::~HierarchicalKeyListModel() {}
00436 
00437 int HierarchicalKeyListModel::rowCount( const QModelIndex & pidx ) const {
00438 
00439     // toplevel item:
00440     if ( !pidx.isValid() )
00441         return mTopLevels.size();
00442 
00443     if ( pidx.column() != 0 )
00444         return 0;
00445 
00446     // non-toplevel item - find the number of subjects for this issuer:
00447     const Key issuer = this->key( pidx );
00448     const char * const fpr = issuer.primaryFingerprint();
00449     if ( !fpr || !*fpr )
00450         return 0;
00451     const Map::const_iterator it = mKeysByExistingParent.find( fpr );
00452     if ( it == mKeysByExistingParent.end() )
00453         return 0;
00454     return it->second.size();
00455 }
00456 
00457 QModelIndex HierarchicalKeyListModel::index( int row, int col, const QModelIndex & pidx ) const {
00458 
00459     if ( row < 0 || col < 0 || col >= NumColumns )
00460         return QModelIndex();
00461 
00462     // toplevel item:
00463     if ( !pidx.isValid() ) {
00464     if ( static_cast<unsigned>( row ) < mTopLevels.size() )
00465         return index( mTopLevels[row], col );
00466     else
00467         return QModelIndex();
00468     }
00469 
00470     // non-toplevel item - find the row'th subject of this key:
00471     const Key issuer = this->key( pidx );
00472     const char * const fpr = issuer.primaryFingerprint();
00473     if ( !fpr || !*fpr )
00474     return QModelIndex();
00475     const Map::const_iterator it = mKeysByExistingParent.find( fpr );
00476     if ( it == mKeysByExistingParent.end() || static_cast<unsigned>( row ) >= it->second.size() )
00477         return QModelIndex();
00478     return index( it->second[row], col );
00479 }
00480 
00481 QModelIndex HierarchicalKeyListModel::parent( const QModelIndex & idx ) const {
00482     const Key key = this->key( idx );
00483     if ( key.isNull() || key.isRoot() )
00484         return QModelIndex();
00485     const std::vector<Key>::const_iterator it
00486         = qBinaryFind( mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
00487                        cleanChainID( key ), _detail::ByFingerprint<std::less>() );
00488     return it != mKeysByFingerprint.end() ? index( *it ) : QModelIndex();
00489 }
00490 
00491 Key HierarchicalKeyListModel::doMapToKey( const QModelIndex & idx ) const {
00492 
00493     if ( !idx.isValid() )
00494     return Key::null;
00495 
00496     const char * const issuer_fpr = static_cast<const char*>( idx.internalPointer() );
00497     if ( !issuer_fpr || !*issuer_fpr ) {
00498     // top-level:
00499     if ( static_cast<unsigned>( idx.row() ) >= mTopLevels.size() )
00500         return Key::null;
00501     else
00502         return mTopLevels[idx.row()];
00503     }
00504 
00505     // non-toplevel:
00506     const Map::const_iterator it
00507     = mKeysByExistingParent.find( issuer_fpr );
00508     if ( it == mKeysByExistingParent.end() || static_cast<unsigned>( idx.row() ) >= it->second.size() )
00509     return Key::null;
00510     return it->second[idx.row()];
00511 }
00512 
00513 QModelIndex HierarchicalKeyListModel::doMapFromKey( const Key & key, int col ) const {
00514 
00515     if ( key.isNull() )
00516         return QModelIndex();
00517 
00518     const char * issuer_fpr = cleanChainID( key );
00519 
00520     // we need to look in the toplevels list,...
00521     const std::vector<Key> * v = &mTopLevels;
00522     if ( issuer_fpr && *issuer_fpr ) {
00523     const std::map< std::string, std::vector<Key> >::const_iterator it
00524         = mKeysByExistingParent.find( issuer_fpr );
00525     // ...unless we find an existing parent:
00526     if ( it != mKeysByExistingParent.end() )
00527         v = &it->second;
00528     else
00529         issuer_fpr = 0; // force internalPointer to zero for toplevels
00530     }
00531 
00532     const std::vector<Key>::const_iterator it
00533     = qBinaryFind( v->begin(), v->end(), key, _detail::ByFingerprint<std::less>() );
00534     if ( it == v->end() )
00535     return QModelIndex();
00536 
00537     const unsigned int row = std::distance( v->begin(), it );
00538     return createIndex( row, col, const_cast<char* /* thanks, Trolls :/ */ >( issuer_fpr ) );
00539 }
00540 
00541 void HierarchicalKeyListModel::addKeyWithParent( const char * issuer_fpr, const Key & key ) {
00542 
00543     assert( issuer_fpr ); assert( *issuer_fpr ); assert( !key.isNull() );
00544 
00545     std::vector<Key> & subjects = mKeysByExistingParent[issuer_fpr];
00546 
00547     // find insertion point:
00548     const std::vector<Key>::iterator it = std::lower_bound( subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>() );
00549     const int row = std::distance( subjects.begin(), it );
00550 
00551     if ( it != subjects.end() && qstricmp( it->primaryFingerprint(), key.primaryFingerprint() ) == 0 ) {
00552     // exists -> replace
00553     *it = key;
00554     emit dataChanged( createIndex( row, 0, const_cast<char*>( issuer_fpr ) ), createIndex( row, NumColumns-1, const_cast<char*>( issuer_fpr ) ) );
00555     } else {
00556     // doesn't exist -> insert
00557     const std::vector<Key>::const_iterator pos = qBinaryFind( mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>() );
00558     assert( pos != mKeysByFingerprint.end() );
00559     beginInsertRows( index( *pos ), row, row );
00560     subjects.insert( it, key );
00561     endInsertRows();
00562     }
00563 }
00564 
00565 void HierarchicalKeyListModel::addKeyWithoutParent( const char * issuer_fpr, const Key & key ) {
00566 
00567     assert( issuer_fpr ); assert( *issuer_fpr ); assert( !key.isNull() );
00568 
00569     std::vector<Key> & subjects = mKeysByNonExistingParent[issuer_fpr];
00570 
00571     // find insertion point:
00572     const std::vector<Key>::iterator it = std::lower_bound( subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>() );
00573 
00574     if ( it != subjects.end() && qstricmp( it->primaryFingerprint(), key.primaryFingerprint() ) == 0 )
00575     // exists -> replace
00576     *it = key;
00577     else
00578     // doesn't exist -> insert
00579     subjects.insert( it, key );
00580 
00581     addTopLevelKey( key );
00582 }
00583 
00584 void HierarchicalKeyListModel::addTopLevelKey( const Key & key ) {
00585 
00586     // find insertion point:
00587     const std::vector<Key>::iterator it = std::lower_bound( mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>() );
00588     const int row = std::distance( mTopLevels.begin(), it );
00589 
00590     if ( it != mTopLevels.end() && qstricmp( it->primaryFingerprint(), key.primaryFingerprint() ) == 0 ) {
00591     // exists -> replace
00592     *it = key;
00593     emit dataChanged( createIndex( row, 0 ), createIndex( row, NumColumns-1 ) );
00594     } else {
00595     // doesn't exist -> insert
00596     beginInsertRows( QModelIndex(), row, row );
00597     mTopLevels.insert( it, key );
00598     endInsertRows();
00599     }
00600 
00601 }
00602 
00603 namespace {
00604 
00605     // sorts 'keys' such that parent always come before their children:
00606     std::vector<Key> topological_sort( const std::vector<Key> & keys ) {
00607 
00608         adjacency_list<> graph( keys.size() );
00609 
00610         // add edges from children to parents:
00611         for ( unsigned int i = 0, end = keys.size() ; i != end ; ++i ) {
00612             const char * const issuer_fpr = cleanChainID( keys[i] );
00613             if ( !issuer_fpr || !*issuer_fpr )
00614                 continue;
00615             const std::vector<Key>::const_iterator it
00616                 = qBinaryFind( keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>() );
00617             if ( it == keys.end() )
00618                 continue;
00619             add_edge( i, std::distance( keys.begin(), it ), graph );
00620         }
00621 
00622         std::vector<int> order;
00623         order.reserve( keys.size() );
00624         topological_sort( graph, std::back_inserter( order ) );
00625 
00626         assert( order.size() == keys.size() );
00627 
00628         std::vector<Key> result;
00629         result.reserve( keys.size() );
00630         Q_FOREACH( int i, order )
00631         result.push_back( keys[i] );
00632         return result;
00633     }
00634 
00635 }
00636 
00637 QList<QModelIndex> HierarchicalKeyListModel::doAddKeys( const std::vector<Key> & keys ) {
00638 #ifdef __GNUC__
00639     assert( __gnu_cxx::is_sorted( keys.begin(), keys.end(), _detail::ByFingerprint<std::less>() ) );
00640 #endif
00641     if ( keys.empty() )
00642         return QList<QModelIndex>();
00643 
00644 
00645     const std::vector<Key> oldKeys = mKeysByFingerprint;
00646 
00647     std::vector<Key> merged;
00648     merged.reserve( keys.size() + mKeysByFingerprint.size() );
00649     std::set_union( keys.begin(), keys.end(),
00650                     mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
00651                     std::back_inserter( merged ), _detail::ByFingerprint<std::less>() );
00652 
00653     mKeysByFingerprint = merged;
00654 
00655     std::set<Key, _detail::ByFingerprint<std::less> > changedParents;
00656 
00657     Q_FOREACH( const Key & key, topological_sort( keys ) ) {
00658 
00659         // check to see whether this key is a parent for a previously parent-less group:
00660         const char * const fpr = key.primaryFingerprint();
00661         if ( !fpr || !*fpr )
00662             continue;
00663 
00664         const bool keyAlreadyExisted = qBinaryFind( oldKeys.begin(), oldKeys.end(), key, _detail::ByFingerprint<std::less>() ) != oldKeys.end();
00665 
00666         const Map::iterator it = mKeysByNonExistingParent.find( fpr );
00667         const std::vector<Key> children = it != mKeysByNonExistingParent.end() ? it->second : std::vector<Key>();
00668         if ( it != mKeysByNonExistingParent.end() )
00669             mKeysByNonExistingParent.erase( it );
00670 
00671         // Step 1: For new keys, remove children from toplevel:
00672 
00673         if ( !keyAlreadyExisted ) {
00674             std::vector<Key>::iterator last = mTopLevels.begin();
00675             std::vector<Key>::iterator lastFP = mKeysByFingerprint.begin();
00676 
00677             Q_FOREACH( const Key & k, children ) {
00678                 last = qBinaryFind( last, mTopLevels.end(), k, _detail::ByFingerprint<std::less>() );
00679                 assert( last != mTopLevels.end() );
00680                 const int row = std::distance( mTopLevels.begin(), last );
00681 
00682                 lastFP = qBinaryFind( lastFP, mKeysByFingerprint.end(), k, _detail::ByFingerprint<std::less>() );
00683                 assert( lastFP != mKeysByFingerprint.end() );
00684 
00685                 emit rowAboutToBeMoved( QModelIndex(), row );
00686                 beginRemoveRows( QModelIndex(), row, row );
00687                 last = mTopLevels.erase( last );
00688                 lastFP = mKeysByFingerprint.erase( lastFP );
00689                 endRemoveRows();
00690             }
00691         }
00692         // Step 2: add/update key
00693 
00694         const char * const issuer_fpr = cleanChainID( key );
00695         if ( !issuer_fpr || !*issuer_fpr )
00696             // root or something...
00697             addTopLevelKey( key );
00698         else if ( std::binary_search( mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>() ) )
00699             // parent exists...
00700             addKeyWithParent( issuer_fpr, key );
00701         else
00702             // parent does't exist yet...
00703             addKeyWithoutParent( issuer_fpr, key );
00704 
00705         const QModelIndex key_idx = index( key );
00706         QModelIndex key_parent = key_idx.parent();
00707         while ( key_parent.isValid() ) {
00708             changedParents.insert( doMapToKey( key_parent ) );
00709             key_parent = key_parent.parent();
00710         }
00711 
00712         // Step 3: Add children to new parent ( == key )
00713 
00714         if ( !keyAlreadyExisted && !children.empty() ) {
00715             addKeys( children );
00716             const QModelIndex new_parent = index( key );
00717             // emit the rowMoved() signals in reversed direction, so the
00718             // implementation can use a stack for mapping.
00719             for ( int i = children.size() - 1 ; i >= 0 ; --i )
00720                 emit rowMoved( new_parent, i );
00721         }
00722     }
00723     //emit dataChanged for all parents with new children. This triggers KeyListSortFilterProxyModel to
00724     //show a parent node if it just got children matching the proxy's filter
00725     Q_FOREACH( const Key & i, changedParents ) {
00726         const QModelIndex idx = index( i );
00727         if ( idx.isValid() )
00728             emit dataChanged( idx.sibling( idx.row(), 0 ), idx.sibling( idx.row(), NumColumns - 1 ) );
00729     }
00730     return indexes( keys );
00731 }
00732 
00733 void HierarchicalKeyListModel::doRemoveKey( const Key & key ) {
00734     const QModelIndex idx = index( key );
00735     if ( !idx.isValid() )
00736         return;
00737 
00738     const char * const fpr = key.primaryFingerprint();
00739     if ( mKeysByExistingParent.find( fpr ) != mKeysByExistingParent.end() ) {
00740         //handle non-leave nodes:
00741         std::vector<Key> keys = mKeysByFingerprint;
00742         const std::vector<Key>::iterator it = qBinaryFind( keys.begin(), keys.end(),
00743                                                            key, _detail::ByFingerprint<std::less>() );
00744         if ( it == keys.end() )
00745             return;
00746         keys.erase( it );
00747         // FIXME for simplicity, we just clear the model and re-add all keys minus the removed one. This is suboptimal,
00748         // but acceptable given that deletion of non-leave nodes is rather rare.
00749         clear();
00750         addKeys( keys );
00751         return;
00752     }
00753 
00754     //handle leave nodes:
00755 
00756     const std::vector<Key>::iterator it = qBinaryFind( mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
00757                                                        key, _detail::ByFingerprint<std::less>() );
00758 
00759     assert( it != mKeysByFingerprint.end() );
00760     assert( mKeysByNonExistingParent.find( fpr ) == mKeysByNonExistingParent.end() );
00761     assert( mKeysByExistingParent.find( fpr ) == mKeysByExistingParent.end() );
00762 
00763     beginRemoveRows( parent( idx ), idx.row(), idx.row() );
00764     mKeysByFingerprint.erase( it );
00765 
00766     const char * const issuer_fpr = cleanChainID( key );
00767 
00768     const std::vector<Key>::iterator tlIt = qBinaryFind( mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>() );
00769     if( tlIt != mTopLevels.end() )
00770         mTopLevels.erase( tlIt );
00771 
00772     if ( issuer_fpr && *issuer_fpr ) {
00773         const Map::iterator nexIt = mKeysByNonExistingParent.find( issuer_fpr );
00774         if ( nexIt != mKeysByNonExistingParent.end() ) {
00775             const std::vector<Key>::iterator eit = qBinaryFind( nexIt->second.begin(), nexIt->second.end(), key, _detail::ByFingerprint<std::less>() );
00776             if ( eit != nexIt->second.end() )
00777                 nexIt->second.erase( eit );
00778             if ( nexIt->second.empty() )
00779                 mKeysByNonExistingParent.erase( nexIt );
00780         }
00781 
00782         const Map::iterator exIt = mKeysByExistingParent.find( issuer_fpr );
00783         if ( exIt != mKeysByExistingParent.end() ) {
00784             const std::vector<Key>::iterator eit = qBinaryFind( exIt->second.begin(), exIt->second.end(), key, _detail::ByFingerprint<std::less>() );
00785             if ( eit != exIt->second.end() )
00786                 exIt->second.erase( eit );
00787             if ( exIt->second.empty() )
00788                 mKeysByExistingParent.erase( exIt );
00789         }
00790     }
00791     endRemoveRows();
00792 }
00793 
00794 // static
00795 AbstractKeyListModel * AbstractKeyListModel::createFlatKeyListModel( QObject * p ) {
00796     AbstractKeyListModel * const m = new FlatKeyListModel( p );
00797 #ifdef KLEO_MODEL_TEST
00798     new ModelTest( m, p );
00799 #endif
00800     return m;
00801 }
00802 
00803 // static
00804 AbstractKeyListModel * AbstractKeyListModel::createHierarchicalKeyListModel( QObject * p ) {
00805     AbstractKeyListModel * const m = new HierarchicalKeyListModel( p );
00806 #ifdef KLEO_MODEL_TEST
00807     new ModelTest( m, p );
00808 #endif
00809     return m;
00810 }
00811 
00812 #include "moc_keylistmodel.cpp"
00813 #include "keylistmodel.moc"
00814 

kleopatra

Skip menu "kleopatra"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

kdepim