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

kleopatra

  • sources
  • kde-4.14
  • kdepim
  • kleopatra
  • models
keylistmodel.cpp
Go to the documentation of this file.
1 /* -*- mode: c++; c-basic-offset:4 -*-
2  models/keylistmodel.cpp
3 
4  This file is part of Kleopatra, the KDE keymanager
5  Copyright (c) 2007 Klarälvdalens Datakonsult AB
6 
7  Kleopatra is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11 
12  Kleopatra is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 
21  In addition, as a special exception, the copyright holders give
22  permission to link the code of this program with any edition of
23  the Qt library by Trolltech AS, Norway (or with modified versions
24  of Qt that use the same license as Qt), and distribute linked
25  combinations including the two. You must obey the GNU General
26  Public License in all respects for all of the code used other than
27  Qt. If you modify this file, you may extend this exception to
28  your version of the file, but you are not obligated to do so. If
29  you do not wish to do so, delete this exception statement from
30  your version.
31 */
32 
33 #include <config-kleopatra.h>
34 
35 #include "keylistmodel.h"
36 #include "predicates.h"
37 
38 #ifdef KLEO_MODEL_TEST
39 # include "modeltest.h"
40 #endif
41 
42 #include <utils/formatting.h>
43 
44 #include <kleo/keyfiltermanager.h>
45 #include <kleo/keyfilter.h>
46 
47 #include <KLocalizedString>
48 
49 #include <QFont>
50 #include <QColor>
51 #include <QHash>
52 #include <QIcon>
53 #include <QDate>
54 #include <gpgme++/key.h>
55 
56 
57 #ifndef Q_MOC_RUN // QTBUG-22829
58 #include <boost/bind.hpp>
59 #include <boost/graph/topological_sort.hpp>
60 #include <boost/graph/adjacency_list.hpp>
61 #endif
62 
63 #include <algorithm>
64 #include <vector>
65 #include <map>
66 #include <set>
67 #include <iterator>
68 #include <cassert>
69 
70 #ifdef __GLIBCXX__
71 #include <ext/algorithm> // for is_sorted
72 #endif
73 
74 using namespace GpgME;
75 using namespace Kleo;
76 
77 /****************************************************************************
78 **
79 ** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
80 ** Contact: Qt Software Information (qt-info@nokia.com)
81 **
82 ** This file is part of the QtCore module of the Qt Toolkit.
83 **
84 ** Commercial Usage
85 ** Licensees holding valid Qt Commercial licenses may use this file in
86 ** accordance with the Qt Commercial License Agreement provided with the
87 ** Software or, alternatively, in accordance with the terms contained in
88 ** a written agreement between you and Nokia.
89 **
90 **
91 ** GNU General Public License Usage
92 ** Alternatively, this file may be used under the terms of the GNU
93 ** General Public License versions 2.0 or 3.0 as published by the Free
94 ** Software Foundation and appearing in the file LICENSE.GPL included in
95 ** the packaging of this file. Please review the following information
96 ** to ensure GNU General Public Licensing requirements will be met:
97 ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
98 ** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
99 ** exception, Nokia gives you certain additional rights. These rights
100 ** are described in the Nokia Qt GPL Exception version 1.3, included in
101 ** the file GPL_EXCEPTION.txt in this package.
102 **
103 ** Qt for Windows(R) Licensees
104 ** As a special exception, Nokia, as the sole copyright holder for Qt
105 ** Designer, grants users of the Qt/Eclipse Integration plug-in the
106 ** right for the Qt/Eclipse Integration to link to functionality
107 ** provided by Qt Designer and its related libraries.
108 **
109 ** If you are unsure which license is appropriate for your use, please
110 ** contact the sales department at qt-sales@nokia.com.
111 **
112 ****************************************************************************/
113 
114 /*
115  These functions are based on Peter J. Weinberger's hash function
116  (from the Dragon Book). The constant 24 in the original function
117  was replaced with 23 to produce fewer collisions on input such as
118  "a", "aa", "aaa", "aaaa", ...
119 */
120 
121 // adjustment to null-terminated strings
122 // (c) 2008 Klarälvdalens Datakonsult AB
123 static uint hash(const uchar *p)
124 {
125  uint h = 0;
126  uint g;
127 
128  while (*p) {
129  h = (h << 4) + *p++;
130  if ((g = (h & 0xf0000000)) != 0)
131  h ^= g >> 23;
132  h &= ~g;
133  }
134  return h;
135 }
136 
137 //
138 // end Nokia-copyrighted code
139 //
140 
141 static inline uint qHash( const char * data ) {
142  if ( !data )
143  return 1; // something != 0
144  return ::hash( reinterpret_cast<const uchar*>( data ) );
145 }
146 
147 class AbstractKeyListModel::Private {
148 public:
149  Private() : m_toolTipOptions( Formatting::Validity ) {}
150  int m_toolTipOptions;
151  mutable QHash<const char*,QVariant> prettyEMailCache;
152 };
153 AbstractKeyListModel::AbstractKeyListModel( QObject * p )
154  : QAbstractItemModel( p ), KeyListModelInterface(), d( new Private )
155 {
156 
157 }
158 
159 AbstractKeyListModel::~AbstractKeyListModel() {}
160 
161 void AbstractKeyListModel::setToolTipOptions( int opts )
162 {
163  d->m_toolTipOptions = opts;
164 }
165 
166 int AbstractKeyListModel::toolTipOptions() const
167 {
168  return d->m_toolTipOptions;
169 }
170 
171 Key AbstractKeyListModel::key( const QModelIndex & idx ) const {
172  if ( idx.isValid() )
173  return doMapToKey( idx );
174  else
175  return Key::null;
176 }
177 
178 std::vector<Key> AbstractKeyListModel::keys( const QList<QModelIndex> & indexes ) const {
179  std::vector<Key> result;
180  result.reserve( indexes.size() );
181  std::transform( indexes.begin(), indexes.end(),
182  std::back_inserter( result ),
183  boost::bind( &AbstractKeyListModel::key, this, _1 ) );
184  result.erase( std::unique( result.begin(), result.end(), _detail::ByFingerprint<std::equal_to>() ), result.end() );
185  return result;
186 }
187 
188 QModelIndex AbstractKeyListModel::index( const Key & key, int col ) const {
189  if ( key.isNull() || col < 0 || col >= NumColumns )
190  return QModelIndex();
191  else
192  return doMapFromKey( key, col );
193 }
194 
195 QList<QModelIndex> AbstractKeyListModel::indexes( const std::vector<Key> & keys ) const {
196  QList<QModelIndex> result;
197  std::transform( keys.begin(), keys.end(),
198  std::back_inserter( result ),
199  // if some compilers are complaining about ambigious overloads, use this line instead:
200  //bind( static_cast<QModelIndex(AbstractKeyListModel::*)(const Key&,int)const>( &AbstractKeyListModel::index ), this, _1, 0 ) );
201  boost::bind( &AbstractKeyListModel::index, this, _1, 0 ) );
202  return result;
203 }
204 
205 void AbstractKeyListModel::setKeys( const std::vector<Key> & keys ) {
206  clear();
207  addKeys( keys );
208 }
209 
210 QModelIndex AbstractKeyListModel::addKey( const Key & key ) {
211  const std::vector<Key> vec( 1, key );
212  const QList<QModelIndex> l = doAddKeys( vec );
213  return l.empty() ? QModelIndex() : l.front() ;
214 }
215 
216 void AbstractKeyListModel::removeKey( const Key & key ) {
217  if ( key.isNull() )
218  return;
219  doRemoveKey( key );
220  d->prettyEMailCache.remove( key.primaryFingerprint() );
221 }
222 
223 QList<QModelIndex> AbstractKeyListModel::addKeys( const std::vector<Key> & keys ) {
224  std::vector<Key> sorted;
225  sorted.reserve( keys.size() );
226  std::remove_copy_if( keys.begin(), keys.end(),
227  std::back_inserter( sorted ),
228  boost::bind( &Key::isNull, _1 ) );
229  std::sort( sorted.begin(), sorted.end(), _detail::ByFingerprint<std::less>() );
230  return doAddKeys( sorted );
231 }
232 
233 void AbstractKeyListModel::clear() {
234  doClear();
235  d->prettyEMailCache.clear();
236  reset();
237 }
238 
239 int AbstractKeyListModel::columnCount( const QModelIndex & ) const {
240  return NumColumns;
241 }
242 
243 QVariant AbstractKeyListModel::headerData( int section, Qt::Orientation o, int role ) const {
244  if ( o == Qt::Horizontal )
245  if ( role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole )
246  switch ( section ) {
247  case PrettyName: return i18n( "Name" );
248 #ifndef KDEPIM_MOBILE_UI
249  case PrettyEMail: return i18n( "E-Mail" );
250  case ValidFrom: return i18n( "Valid From" );
251  case ValidUntil: return i18n( "Valid Until" );
252  case TechnicalDetails: return i18n( "Details" );
253  case ShortKeyID: return i18n( "Key-ID" );
254 #endif
255  case NumColumns: ;
256  }
257  return QVariant();
258 }
259 
260 static QVariant returnIfValid( const QColor & t ) {
261  if ( t.isValid() )
262  return t;
263  else
264  return QVariant();
265 }
266 
267 static QVariant returnIfValid( const QIcon & t ) {
268  if ( !t.isNull() )
269  return t;
270  else
271  return QVariant();
272 }
273 
274 QVariant AbstractKeyListModel::data( const QModelIndex & index, int role ) const {
275  const Key key = this->key( index );
276  if ( key.isNull() )
277  return QVariant();
278 
279  const int column = index.column();
280 
281  if ( role == Qt::DisplayRole || role == Qt::EditRole )
282  switch ( column ) {
283  case PrettyName:
284 #ifdef KDEPIM_MOBILE_UI
285  return Formatting::formatForComboBox( key );
286 #else
287  return Formatting::prettyName( key );
288  case PrettyEMail:
289  if ( const char * const fpr = key.primaryFingerprint() ) {
290  const QHash<const char*,QVariant>::const_iterator it = d->prettyEMailCache.constFind( fpr );
291  if ( it != d->prettyEMailCache.constEnd() )
292  return *it;
293  else
294  return d->prettyEMailCache[fpr] = Formatting::prettyEMail( key );
295  } else {
296  return QVariant();
297  }
298  case ValidFrom:
299  if ( role == Qt::EditRole )
300  return Formatting::creationDate( key );
301  else
302  return Formatting::creationDateString( key );
303  case ValidUntil:
304  if ( role == Qt::EditRole )
305  return Formatting::expirationDate( key );
306  else
307  return Formatting::expirationDateString( key );
308  case TechnicalDetails:
309  return Formatting::type( key );
310  case ShortKeyID:
311  return QString::fromLatin1( key.shortKeyID() );
312 #endif // KDEPIM_MOBILE_UI
313  case NumColumns:
314  break;
315  }
316 #ifndef KDEPIM_MOBILE_UI
317  else if ( role == Qt::ToolTipRole )
318  return Formatting::toolTip( key, toolTipOptions() );
319  else if ( role == Qt::FontRole )
320  return KeyFilterManager::instance()->font( key, ( column == ShortKeyID ) ? QFont( QLatin1String("courier") ) : QFont() );
321 #endif
322  else if ( role == Qt::DecorationRole )
323  return column == Icon ? returnIfValid( KeyFilterManager::instance()->icon( key ) ) : QVariant() ;
324  else if ( role == Qt::BackgroundRole )
325  return returnIfValid( KeyFilterManager::instance()->bgColor( key ) );
326  else if ( role == Qt::ForegroundRole )
327  return returnIfValid( KeyFilterManager::instance()->fgColor( key ) );
328  else if ( role == FingerprintRole )
329  return QString::fromLatin1( key.primaryFingerprint() );
330  return QVariant();
331 }
332 
333 
334 namespace {
335  template <typename Base>
336  class TableModelMixin : public Base {
337  public:
338  explicit TableModelMixin( QObject * p=0 ) : Base( p ) {}
339  ~TableModelMixin() {}
340 
341  using Base::index;
342  /* reimp */ QModelIndex index( int row, int column, const QModelIndex & pidx=QModelIndex() ) const {
343  return this->hasIndex( row, column, pidx ) ? this->createIndex( row, column, 0 ) : QModelIndex() ;
344  }
345 
346  private:
347  /* reimp */ QModelIndex parent( const QModelIndex & ) const { return QModelIndex(); }
348  /* reimp */ bool hasChildren( const QModelIndex & pidx ) const {
349  return ( pidx.model() == this || !pidx.isValid() ) && this->rowCount( pidx ) > 0 && this->columnCount( pidx ) > 0 ;
350  }
351  };
352 
353  class FlatKeyListModel
354 #ifndef Q_MOC_RUN
355  : public TableModelMixin<AbstractKeyListModel>
356 #else
357  : public AbstractKeyListModel
358 #endif
359  {
360  Q_OBJECT
361  public:
362  explicit FlatKeyListModel( QObject * parent=0 );
363  ~FlatKeyListModel();
364 
365  /* reimp */ int rowCount( const QModelIndex & pidx ) const { return pidx.isValid() ? 0 : mKeysByFingerprint.size() ; }
366 
367  private:
368  /* reimp */ Key doMapToKey( const QModelIndex & index ) const;
369  /* reimp */ QModelIndex doMapFromKey( const Key & key, int col ) const;
370  /* reimp */ QList<QModelIndex> doAddKeys( const std::vector<Key> & keys );
371  /* reimp */ void doRemoveKey( const Key & key );
372  /* reimp */ void doClear() {
373  mKeysByFingerprint.clear();
374  }
375 
376  private:
377  std::vector<Key> mKeysByFingerprint;
378  };
379 
380  class HierarchicalKeyListModel : public AbstractKeyListModel {
381  Q_OBJECT
382  public:
383  explicit HierarchicalKeyListModel( QObject * parent=0 );
384  ~HierarchicalKeyListModel();
385 
386  /* reimp */ int rowCount( const QModelIndex & pidx ) const;
387  using AbstractKeyListModel::index;
388  /* reimp */ QModelIndex index( int row, int col, const QModelIndex & pidx ) const;
389  /* reimp */ QModelIndex parent( const QModelIndex & idx ) const;
390 
391  bool hasChildren( const QModelIndex & pidx ) const { return rowCount( pidx ) > 0 ; }
392 
393  private:
394  /* reimp */ Key doMapToKey( const QModelIndex & index ) const;
395  /* reimp */ QModelIndex doMapFromKey( const Key & key, int col ) const;
396  /* reimp */ QList<QModelIndex> doAddKeys( const std::vector<Key> & keys );
397  /* reimp */ void doRemoveKey( const Key & key );
398  /* reimp */ void doClear() {
399  mTopLevels.clear();
400  mKeysByFingerprint.clear();
401  mKeysByExistingParent.clear();
402  mKeysByNonExistingParent.clear();
403  }
404 
405  private:
406  void addTopLevelKey( const Key & key );
407  void addKeyWithParent( const char * issuer_fpr, const Key & key );
408  void addKeyWithoutParent( const char * issuer_fpr, const Key & key );
409 
410  private:
411  typedef std::map< std::string, std::vector<Key> > Map;
412  std::vector<Key> mKeysByFingerprint; // all keys
413  Map mKeysByExistingParent, mKeysByNonExistingParent; // parent->child map
414  std::vector<Key> mTopLevels; // all roots + parent-less
415  };
416 
417  static const char * cleanChainID( const Key & key ) {
418  if ( key.isRoot() )
419  return "";
420  if ( const char * chid = key.chainID() )
421  return chid;
422  return "";
423  }
424 
425 }
426 
427 
428 FlatKeyListModel::FlatKeyListModel( QObject * p )
429  : TableModelMixin<AbstractKeyListModel>( p ),
430  mKeysByFingerprint()
431 {
432 
433 }
434 
435 FlatKeyListModel::~FlatKeyListModel() {}
436 
437 Key FlatKeyListModel::doMapToKey( const QModelIndex & idx ) const {
438  assert( idx.isValid() );
439  if ( static_cast<unsigned>( idx.row() ) < mKeysByFingerprint.size() && idx.column() < NumColumns )
440  return mKeysByFingerprint[ idx.row() ];
441  else
442  return Key::null;
443 }
444 
445 QModelIndex FlatKeyListModel::doMapFromKey( const Key & key, int col ) const {
446  assert( !key.isNull() );
447  const std::vector<Key>::const_iterator it
448  = std::lower_bound( mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
449  key, _detail::ByFingerprint<std::less>() );
450  if ( it == mKeysByFingerprint.end() || !_detail::ByFingerprint<std::equal_to>()( *it, key ) )
451  return QModelIndex();
452  else
453  return createIndex( it - mKeysByFingerprint.begin(), col );
454 }
455 
456 QList<QModelIndex> FlatKeyListModel::doAddKeys( const std::vector<Key> & keys ) {
457 #ifdef __GLIBCXX__
458  assert( __gnu_cxx::is_sorted( keys.begin(), keys.end(), _detail::ByFingerprint<std::less>() ) );
459 #endif
460  if ( keys.empty() )
461  return QList<QModelIndex>();
462 
463  for ( std::vector<Key>::const_iterator it = keys.begin(), end = keys.end() ; it != end ; ++it ) {
464 
465  // find an insertion point:
466  const std::vector<Key>::iterator pos = std::upper_bound( mKeysByFingerprint.begin(), mKeysByFingerprint.end(), *it, _detail::ByFingerprint<std::less>() );
467  const unsigned int idx = std::distance( mKeysByFingerprint.begin(), pos );
468 
469  if ( idx > 0 && qstrcmp( mKeysByFingerprint[idx-1].primaryFingerprint(), it->primaryFingerprint() ) == 0 ) {
470  // key existed before - replace with new one:
471  mKeysByFingerprint[idx-1] = *it;
472  emit dataChanged( createIndex( idx-1, 0 ), createIndex( idx-1, NumColumns-1 ) );
473  } else {
474  // new key - insert:
475  beginInsertRows( QModelIndex(), idx, idx );
476  mKeysByFingerprint.insert( pos, *it );
477  endInsertRows();
478  }
479  }
480 
481  return indexes( keys );
482 }
483 
484 
485 void FlatKeyListModel::doRemoveKey( const Key & key ) {
486  const std::vector<Key>::iterator it
487  = qBinaryFind( mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
488  key, _detail::ByFingerprint<std::less>() );
489  if ( it == mKeysByFingerprint.end() )
490  return;
491 
492  const unsigned int row = std::distance( mKeysByFingerprint.begin(), it );
493  beginRemoveRows( QModelIndex(), row, row );
494  mKeysByFingerprint.erase( it );
495  endRemoveRows();
496 }
497 
498 
499 
500 
501 
502 
503 
504 
505 HierarchicalKeyListModel::HierarchicalKeyListModel( QObject * p )
506  : AbstractKeyListModel( p ),
507  mKeysByFingerprint(),
508  mKeysByExistingParent(),
509  mKeysByNonExistingParent(),
510  mTopLevels()
511 {
512 
513 }
514 
515 HierarchicalKeyListModel::~HierarchicalKeyListModel() {}
516 
517 int HierarchicalKeyListModel::rowCount( const QModelIndex & pidx ) const {
518 
519  // toplevel item:
520  if ( !pidx.isValid() )
521  return mTopLevels.size();
522 
523  if ( pidx.column() != 0 )
524  return 0;
525 
526  // non-toplevel item - find the number of subjects for this issuer:
527  const Key issuer = this->key( pidx );
528  const char * const fpr = issuer.primaryFingerprint();
529  if ( !fpr || !*fpr )
530  return 0;
531  const Map::const_iterator it = mKeysByExistingParent.find( fpr );
532  if ( it == mKeysByExistingParent.end() )
533  return 0;
534  return it->second.size();
535 }
536 
537 QModelIndex HierarchicalKeyListModel::index( int row, int col, const QModelIndex & pidx ) const {
538 
539  if ( row < 0 || col < 0 || col >= NumColumns )
540  return QModelIndex();
541 
542  // toplevel item:
543  if ( !pidx.isValid() ) {
544  if ( static_cast<unsigned>( row ) < mTopLevels.size() )
545  return index( mTopLevels[row], col );
546  else
547  return QModelIndex();
548  }
549 
550  // non-toplevel item - find the row'th subject of this key:
551  const Key issuer = this->key( pidx );
552  const char * const fpr = issuer.primaryFingerprint();
553  if ( !fpr || !*fpr )
554  return QModelIndex();
555  const Map::const_iterator it = mKeysByExistingParent.find( fpr );
556  if ( it == mKeysByExistingParent.end() || static_cast<unsigned>( row ) >= it->second.size() )
557  return QModelIndex();
558  return index( it->second[row], col );
559 }
560 
561 QModelIndex HierarchicalKeyListModel::parent( const QModelIndex & idx ) const {
562  const Key key = this->key( idx );
563  if ( key.isNull() || key.isRoot() )
564  return QModelIndex();
565  const std::vector<Key>::const_iterator it
566  = qBinaryFind( mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
567  cleanChainID( key ), _detail::ByFingerprint<std::less>() );
568  return it != mKeysByFingerprint.end() ? index( *it ) : QModelIndex();
569 }
570 
571 Key HierarchicalKeyListModel::doMapToKey( const QModelIndex & idx ) const {
572 
573  if ( !idx.isValid() )
574  return Key::null;
575 
576  const char * const issuer_fpr = static_cast<const char*>( idx.internalPointer() );
577  if ( !issuer_fpr || !*issuer_fpr ) {
578  // top-level:
579  if ( static_cast<unsigned>( idx.row() ) >= mTopLevels.size() )
580  return Key::null;
581  else
582  return mTopLevels[idx.row()];
583  }
584 
585  // non-toplevel:
586  const Map::const_iterator it
587  = mKeysByExistingParent.find( issuer_fpr );
588  if ( it == mKeysByExistingParent.end() || static_cast<unsigned>( idx.row() ) >= it->second.size() )
589  return Key::null;
590  return it->second[idx.row()];
591 }
592 
593 QModelIndex HierarchicalKeyListModel::doMapFromKey( const Key & key, int col ) const {
594 
595  if ( key.isNull() )
596  return QModelIndex();
597 
598  const char * issuer_fpr = cleanChainID( key );
599 
600  // we need to look in the toplevels list,...
601  const std::vector<Key> * v = &mTopLevels;
602  if ( issuer_fpr && *issuer_fpr ) {
603  const std::map< std::string, std::vector<Key> >::const_iterator it
604  = mKeysByExistingParent.find( issuer_fpr );
605  // ...unless we find an existing parent:
606  if ( it != mKeysByExistingParent.end() )
607  v = &it->second;
608  else
609  issuer_fpr = 0; // force internalPointer to zero for toplevels
610  }
611 
612  const std::vector<Key>::const_iterator it
613  = std::lower_bound( v->begin(), v->end(), key, _detail::ByFingerprint<std::less>() );
614  if ( it == v->end() || !_detail::ByFingerprint<std::equal_to>()( *it, key ) )
615  return QModelIndex();
616 
617  const unsigned int row = std::distance( v->begin(), it );
618  return createIndex( row, col, const_cast<char* /* thanks, Trolls :/ */ >( issuer_fpr ) );
619 }
620 
621 void HierarchicalKeyListModel::addKeyWithParent( const char * issuer_fpr, const Key & key ) {
622 
623  assert( issuer_fpr ); assert( *issuer_fpr ); assert( !key.isNull() );
624 
625  std::vector<Key> & subjects = mKeysByExistingParent[issuer_fpr];
626 
627  // find insertion point:
628  const std::vector<Key>::iterator it = std::lower_bound( subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>() );
629  const int row = std::distance( subjects.begin(), it );
630 
631  if ( it != subjects.end() && qstricmp( it->primaryFingerprint(), key.primaryFingerprint() ) == 0 ) {
632  // exists -> replace
633  *it = key;
634  emit dataChanged( createIndex( row, 0, const_cast<char*>( issuer_fpr ) ), createIndex( row, NumColumns-1, const_cast<char*>( issuer_fpr ) ) );
635  } else {
636  // doesn't exist -> insert
637  const std::vector<Key>::const_iterator pos = qBinaryFind( mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>() );
638  assert( pos != mKeysByFingerprint.end() );
639  beginInsertRows( index( *pos ), row, row );
640  subjects.insert( it, key );
641  endInsertRows();
642  }
643 }
644 
645 void HierarchicalKeyListModel::addKeyWithoutParent( const char * issuer_fpr, const Key & key ) {
646 
647  assert( issuer_fpr ); assert( *issuer_fpr ); assert( !key.isNull() );
648 
649  std::vector<Key> & subjects = mKeysByNonExistingParent[issuer_fpr];
650 
651  // find insertion point:
652  const std::vector<Key>::iterator it = std::lower_bound( subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>() );
653 
654  if ( it != subjects.end() && qstricmp( it->primaryFingerprint(), key.primaryFingerprint() ) == 0 )
655  // exists -> replace
656  *it = key;
657  else
658  // doesn't exist -> insert
659  subjects.insert( it, key );
660 
661  addTopLevelKey( key );
662 }
663 
664 void HierarchicalKeyListModel::addTopLevelKey( const Key & key ) {
665 
666  // find insertion point:
667  const std::vector<Key>::iterator it = std::lower_bound( mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>() );
668  const int row = std::distance( mTopLevels.begin(), it );
669 
670  if ( it != mTopLevels.end() && qstricmp( it->primaryFingerprint(), key.primaryFingerprint() ) == 0 ) {
671  // exists -> replace
672  *it = key;
673  emit dataChanged( createIndex( row, 0 ), createIndex( row, NumColumns-1 ) );
674  } else {
675  // doesn't exist -> insert
676  beginInsertRows( QModelIndex(), row, row );
677  mTopLevels.insert( it, key );
678  endInsertRows();
679  }
680 
681 }
682 
683 // sorts 'keys' such that parent always come before their children:
684 static std::vector<Key> topological_sort( const std::vector<Key> & keys ) {
685 
686  boost::adjacency_list<> graph( keys.size() );
687 
688  // add edges from children to parents:
689  for ( unsigned int i = 0, end = keys.size() ; i != end ; ++i ) {
690  const char * const issuer_fpr = cleanChainID( keys[i] );
691  if ( !issuer_fpr || !*issuer_fpr )
692  continue;
693  const std::vector<Key>::const_iterator it
694  = qBinaryFind( keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>() );
695  if ( it == keys.end() )
696  continue;
697  add_edge( i, std::distance( keys.begin(), it ), graph );
698  }
699 
700  std::vector<int> order;
701  order.reserve( keys.size() );
702  topological_sort( graph, std::back_inserter( order ) );
703 
704  assert( order.size() == keys.size() );
705 
706  std::vector<Key> result;
707  result.reserve( keys.size() );
708  Q_FOREACH( int i, order )
709  result.push_back( keys[i] );
710  return result;
711 }
712 
713 QList<QModelIndex> HierarchicalKeyListModel::doAddKeys( const std::vector<Key> & keys ) {
714 #ifdef __GLIBCXX__
715  assert( __gnu_cxx::is_sorted( keys.begin(), keys.end(), _detail::ByFingerprint<std::less>() ) );
716 #endif
717  if ( keys.empty() )
718  return QList<QModelIndex>();
719 
720 
721  const std::vector<Key> oldKeys = mKeysByFingerprint;
722 
723  std::vector<Key> merged;
724  merged.reserve( keys.size() + mKeysByFingerprint.size() );
725  std::set_union( keys.begin(), keys.end(),
726  mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
727  std::back_inserter( merged ), _detail::ByFingerprint<std::less>() );
728 
729  mKeysByFingerprint = merged;
730 
731  std::set<Key, _detail::ByFingerprint<std::less> > changedParents;
732 
733  Q_FOREACH( const Key & key, topological_sort( keys ) ) {
734 
735  // check to see whether this key is a parent for a previously parent-less group:
736  const char * const fpr = key.primaryFingerprint();
737  if ( !fpr || !*fpr )
738  continue;
739 
740  const bool keyAlreadyExisted = qBinaryFind( oldKeys.begin(), oldKeys.end(), key, _detail::ByFingerprint<std::less>() ) != oldKeys.end();
741 
742  const Map::iterator it = mKeysByNonExistingParent.find( fpr );
743  const std::vector<Key> children = it != mKeysByNonExistingParent.end() ? it->second : std::vector<Key>();
744  if ( it != mKeysByNonExistingParent.end() )
745  mKeysByNonExistingParent.erase( it );
746 
747  // Step 1: For new keys, remove children from toplevel:
748 
749  if ( !keyAlreadyExisted ) {
750  std::vector<Key>::iterator last = mTopLevels.begin();
751  std::vector<Key>::iterator lastFP = mKeysByFingerprint.begin();
752 
753  Q_FOREACH( const Key & k, children ) {
754  last = qBinaryFind( last, mTopLevels.end(), k, _detail::ByFingerprint<std::less>() );
755  assert( last != mTopLevels.end() );
756  const int row = std::distance( mTopLevels.begin(), last );
757 
758  lastFP = qBinaryFind( lastFP, mKeysByFingerprint.end(), k, _detail::ByFingerprint<std::less>() );
759  assert( lastFP != mKeysByFingerprint.end() );
760 
761  emit rowAboutToBeMoved( QModelIndex(), row );
762  beginRemoveRows( QModelIndex(), row, row );
763  last = mTopLevels.erase( last );
764  lastFP = mKeysByFingerprint.erase( lastFP );
765  endRemoveRows();
766  }
767  }
768  // Step 2: add/update key
769 
770  const char * const issuer_fpr = cleanChainID( key );
771  if ( !issuer_fpr || !*issuer_fpr )
772  // root or something...
773  addTopLevelKey( key );
774  else if ( std::binary_search( mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>() ) )
775  // parent exists...
776  addKeyWithParent( issuer_fpr, key );
777  else
778  // parent does't exist yet...
779  addKeyWithoutParent( issuer_fpr, key );
780 
781  const QModelIndex key_idx = index( key );
782  QModelIndex key_parent = key_idx.parent();
783  while ( key_parent.isValid() ) {
784  changedParents.insert( doMapToKey( key_parent ) );
785  key_parent = key_parent.parent();
786  }
787 
788  // Step 3: Add children to new parent ( == key )
789 
790  if ( !keyAlreadyExisted && !children.empty() ) {
791  addKeys( children );
792  const QModelIndex new_parent = index( key );
793  // emit the rowMoved() signals in reversed direction, so the
794  // implementation can use a stack for mapping.
795  for ( int i = children.size() - 1 ; i >= 0 ; --i )
796  emit rowMoved( new_parent, i );
797  }
798  }
799  //emit dataChanged for all parents with new children. This triggers KeyListSortFilterProxyModel to
800  //show a parent node if it just got children matching the proxy's filter
801  Q_FOREACH( const Key & i, changedParents ) {
802  const QModelIndex idx = index( i );
803  if ( idx.isValid() )
804  emit dataChanged( idx.sibling( idx.row(), 0 ), idx.sibling( idx.row(), NumColumns - 1 ) );
805  }
806  return indexes( keys );
807 }
808 
809 void HierarchicalKeyListModel::doRemoveKey( const Key & key ) {
810  const QModelIndex idx = index( key );
811  if ( !idx.isValid() )
812  return;
813 
814  const char * const fpr = key.primaryFingerprint();
815  if ( mKeysByExistingParent.find( fpr ) != mKeysByExistingParent.end() ) {
816  //handle non-leave nodes:
817  std::vector<Key> keys = mKeysByFingerprint;
818  const std::vector<Key>::iterator it = qBinaryFind( keys.begin(), keys.end(),
819  key, _detail::ByFingerprint<std::less>() );
820  if ( it == keys.end() )
821  return;
822  keys.erase( it );
823  // FIXME for simplicity, we just clear the model and re-add all keys minus the removed one. This is suboptimal,
824  // but acceptable given that deletion of non-leave nodes is rather rare.
825  clear();
826  addKeys( keys );
827  return;
828  }
829 
830  //handle leave nodes:
831 
832  const std::vector<Key>::iterator it = qBinaryFind( mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
833  key, _detail::ByFingerprint<std::less>() );
834 
835  assert( it != mKeysByFingerprint.end() );
836  assert( mKeysByNonExistingParent.find( fpr ) == mKeysByNonExistingParent.end() );
837  assert( mKeysByExistingParent.find( fpr ) == mKeysByExistingParent.end() );
838 
839  beginRemoveRows( parent( idx ), idx.row(), idx.row() );
840  mKeysByFingerprint.erase( it );
841 
842  const char * const issuer_fpr = cleanChainID( key );
843 
844  const std::vector<Key>::iterator tlIt = qBinaryFind( mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>() );
845  if( tlIt != mTopLevels.end() )
846  mTopLevels.erase( tlIt );
847 
848  if ( issuer_fpr && *issuer_fpr ) {
849  const Map::iterator nexIt = mKeysByNonExistingParent.find( issuer_fpr );
850  if ( nexIt != mKeysByNonExistingParent.end() ) {
851  const std::vector<Key>::iterator eit = qBinaryFind( nexIt->second.begin(), nexIt->second.end(), key, _detail::ByFingerprint<std::less>() );
852  if ( eit != nexIt->second.end() )
853  nexIt->second.erase( eit );
854  if ( nexIt->second.empty() )
855  mKeysByNonExistingParent.erase( nexIt );
856  }
857 
858  const Map::iterator exIt = mKeysByExistingParent.find( issuer_fpr );
859  if ( exIt != mKeysByExistingParent.end() ) {
860  const std::vector<Key>::iterator eit = qBinaryFind( exIt->second.begin(), exIt->second.end(), key, _detail::ByFingerprint<std::less>() );
861  if ( eit != exIt->second.end() )
862  exIt->second.erase( eit );
863  if ( exIt->second.empty() )
864  mKeysByExistingParent.erase( exIt );
865  }
866  }
867  endRemoveRows();
868 }
869 
870 // static
871 AbstractKeyListModel * AbstractKeyListModel::createFlatKeyListModel( QObject * p ) {
872  AbstractKeyListModel * const m = new FlatKeyListModel( p );
873 #ifdef KLEO_MODEL_TEST
874  new ModelTest( m, p );
875 #endif
876  return m;
877 }
878 
879 // static
880 AbstractKeyListModel * AbstractKeyListModel::createHierarchicalKeyListModel( QObject * p ) {
881  AbstractKeyListModel * const m = new HierarchicalKeyListModel( p );
882 #ifdef KLEO_MODEL_TEST
883  new ModelTest( m, p );
884 #endif
885  return m;
886 }
887 
888 #include "keylistmodel.moc"
889 
qHash
static uint qHash(const char *data)
Definition: keylistmodel.cpp:141
Kleo::KeyListModelInterface::PrettyName
Definition: keylistmodelinterface.h:54
QModelIndex
Kleo::AbstractKeyListModel::data
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
Definition: keylistmodel.cpp:274
Kleo::KeyListModelInterface::ValidUntil
Definition: keylistmodelinterface.h:58
Kleo::AbstractKeyListModel::addKey
QModelIndex addKey(const GpgME::Key &key)
Definition: keylistmodel.cpp:210
Kleo::AbstractKeyListModel::columnCount
int columnCount(const QModelIndex &pidx) const
Definition: keylistmodel.cpp:239
Kleo::Formatting::type
QString type(const GpgME::Key &key)
QList::reserve
void reserve(int alloc)
Kleo::KeyListModelInterface::ShortKeyID
Definition: keylistmodelinterface.h:61
QFont
Kleo::Formatting::creationDateString
QString creationDateString(const GpgME::Key &key)
Kleo::AbstractKeyListModel::indexes
QList< QModelIndex > indexes(const std::vector< GpgME::Key > &keys) const
Definition: keylistmodel.cpp:195
Kleo::Formatting::toolTip
QString toolTip(const GpgME::Key &key, int opts)
formatting.h
Kleo::AbstractKeyListModel::clear
void clear()
Definition: keylistmodel.cpp:233
Kleo::KeyListModelInterface::PrettyEMail
Definition: keylistmodelinterface.h:56
Kleo::AbstractKeyListModel::setToolTipOptions
void setToolTipOptions(int opts)
Definition: keylistmodel.cpp:161
Kleo::KeyListModelInterface::ValidFrom
Definition: keylistmodelinterface.h:57
Kleo::Formatting::expirationDateString
QString expirationDateString(const GpgME::Key &key)
QList::size
int size() const
Kleo::AbstractKeyListModel::~AbstractKeyListModel
~AbstractKeyListModel()
Definition: keylistmodel.cpp:159
Kleo::AbstractKeyListModel::createHierarchicalKeyListModel
static AbstractKeyListModel * createHierarchicalKeyListModel(QObject *parent=0)
Definition: keylistmodel.cpp:880
Kleo::Formatting::formatForComboBox
QString formatForComboBox(const GpgME::Key &key)
Definition: formatting.cpp:497
QAbstractItemModel::reset
void reset()
QModelIndex::isValid
bool isValid() const
d
#define d
Definition: adduseridcommand.cpp:89
hash
static uint hash(const uchar *p)
Definition: keylistmodel.cpp:123
QList::empty
bool empty() const
QHash< const char *, QVariant >
Kleo::AbstractKeyListModel::index
QModelIndex index(const GpgME::Key &key) const
Definition: keylistmodel.h:62
QObject
modeltest.h
topological_sort
static std::vector< Key > topological_sort(const std::vector< Key > &keys)
Definition: keylistmodel.cpp:684
QModelIndex::row
int row() const
Kleo::Formatting::expirationDate
QDate expirationDate(const GpgME::Key &key)
returnIfValid
static QVariant returnIfValid(const QColor &t)
Definition: keylistmodel.cpp:260
QModelIndex::internalPointer
void * internalPointer() const
QList::front
T & front()
QList
Definition: commands/command.h:46
QColor
QModelIndex::parent
QModelIndex parent() const
Kleo::AbstractKeyListModel::addKeys
QList< QModelIndex > addKeys(const std::vector< GpgME::Key > &keys)
Definition: keylistmodel.cpp:223
Kleo::Formatting::prettyEMail
QString prettyEMail(const char *email, const char *id)
Definition: formatting.cpp:184
Kleo::KeyListModelInterface::TechnicalDetails
Definition: keylistmodelinterface.h:59
Kleo::AbstractKeyListModel
Definition: keylistmodel.h:49
QList::end
iterator end()
Kleo::KeyListModelInterface::NumColumns
Definition: keylistmodelinterface.h:72
Kleo::KeyListModelInterface
Definition: keylistmodelinterface.h:47
keylistmodel.h
Kleo::AbstractKeyListModel::toolTipOptions
int toolTipOptions() const
defines which information is displayed in tooltips see Kleo::Formatting::ToolTipOption ...
Definition: keylistmodel.cpp:166
QModelIndex::model
const QAbstractItemModel * model() const
Kleo::Formatting::creationDate
QDate creationDate(const GpgME::Key &key)
QLatin1String
Kleo::AbstractKeyListModel::headerData
QVariant headerData(int section, Qt::Orientation o, int role=Qt::DisplayRole) const
Definition: keylistmodel.cpp:243
ModelTest
Definition: modeltest.h:31
QIcon::isNull
bool isNull() const
QModelIndex::sibling
QModelIndex sibling(int row, int column) const
predicates.h
Kleo::AbstractKeyListModel::key
GpgME::Key key(const QModelIndex &idx) const
Definition: keylistmodel.cpp:171
Kleo::AbstractKeyListModel::keys
std::vector< GpgME::Key > keys(const QList< QModelIndex > &indexes) const
Definition: keylistmodel.cpp:178
Kleo::Formatting::prettyName
QString prettyName(int proto, const char *id, const char *name, const char *comment)
Definition: formatting.cpp:64
Kleo::AbstractKeyListModel::createFlatKeyListModel
static AbstractKeyListModel * createFlatKeyListModel(QObject *parent=0)
Definition: keylistmodel.cpp:871
Kleo::KeyListModelInterface::FingerprintRole
static const int FingerprintRole
Definition: keylistmodelinterface.h:51
Kleo::AbstractKeyListModel::removeKey
void removeKey(const GpgME::Key &key)
Definition: keylistmodel.cpp:216
Kleo::KeyListModelInterface::Icon
Definition: keylistmodelinterface.h:73
QModelIndex::column
int column() const
QAbstractItemModel
Kleo::Formatting::Validity
Definition: formatting.h:69
QString::fromLatin1
QString fromLatin1(const char *str, int size)
Kleo::AbstractKeyListModel::setKeys
void setKeys(const std::vector< GpgME::Key > &keys)
Definition: keylistmodel.cpp:205
QList::begin
iterator begin()
QIcon
QColor::isValid
bool isValid() const
QVariant
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:33:11 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kleopatra

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

kdepim API Reference

Skip menu "kdepim API Reference"
  • akonadi_next
  • akregator
  • blogilo
  • calendarsupport
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt2
  • kjots
  • kleopatra
  • kmail
  • knode
  • knotes
  • kontact
  • korgac
  • korganizer
  • ktimetracker
  • libkdepim
  • libkleo
  • libkpgp
  • mailcommon
  • messagelist
  • messageviewer
  • pimprint

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal