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

akonadi

  • sources
  • kde-4.12
  • kdepimlibs
  • akonadi
statisticsproxymodel.cpp
1 /*
2  Copyright (c) 2009 Kevin Ottens <ervin@kde.org>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "statisticsproxymodel.h"
21 
22 #include "entitytreemodel.h"
23 #include "collectionutils_p.h"
24 
25 #include <akonadi/collectionquotaattribute.h>
26 #include <akonadi/collectionstatistics.h>
27 #include <akonadi/entitydisplayattribute.h>
28 
29 #include <kdebug.h>
30 #include <kiconloader.h>
31 #include <klocalizedstring.h>
32 #include <kio/global.h>
33 
34 #include <QApplication>
35 #include <QPalette>
36 #include <KIcon>
37 using namespace Akonadi;
38 
42 class StatisticsProxyModel::Private
43 {
44  public:
45  Private( StatisticsProxyModel *parent )
46  : mParent( parent ), mToolTipEnabled( false ), mExtraColumnsEnabled( true )
47  {
48  }
49 
50  int sourceColumnCount( const QModelIndex &parent )
51  {
52  return mParent->sourceModel()->columnCount( mParent->mapToSource( parent ) );
53  }
54 
55  void getCountRecursive( const QModelIndex &index, qint64 &totalSize ) const
56  {
57  Collection collection = qvariant_cast<Collection>( index.data( EntityTreeModel::CollectionRole ) );
58  // Do not assert on invalid collections, since a collection may be deleted
59  // in the meantime and deleted collections are invalid.
60  if ( collection.isValid() ) {
61  CollectionStatistics statistics = collection.statistics();
62  totalSize += qMax( 0LL, statistics.size() );
63  if ( index.model()->hasChildren( index ) ) {
64  const int rowCount = index.model()->rowCount( index );
65  for ( int row = 0; row < rowCount; row++ ) {
66  static const int column = 0;
67  getCountRecursive( index.model()->index( row, column, index ), totalSize );
68  }
69  }
70  }
71  }
72 
73  QString toolTipForCollection( const QModelIndex &index, const Collection &collection )
74  {
75  QString bckColor = QApplication::palette().color( QPalette::ToolTipBase ).name();
76  QString txtColor = QApplication::palette().color( QPalette::ToolTipText ).name();
77 
78  QString tip = QString::fromLatin1(
79  "<table width=\"100%\" border=\"0\" cellpadding=\"2\" cellspacing=\"0\">\n"
80  );
81  const QString textDirection = ( QApplication::layoutDirection() == Qt::LeftToRight ) ? QLatin1String( "left" ) : QLatin1String( "right" );
82  tip += QString::fromLatin1(
83  " <tr>\n"
84  " <td bgcolor=\"%1\" colspan=\"2\" align=\"%4\" valign=\"middle\">\n"
85  " <div style=\"color: %2; font-weight: bold;\">\n"
86  " %3\n"
87  " </div>\n"
88  " </td>\n"
89  " </tr>\n"
90  ).arg( txtColor ).arg( bckColor ).arg( index.data( Qt::DisplayRole ).toString() ).arg( textDirection );
91 
92  tip += QString::fromLatin1(
93  " <tr>\n"
94  " <td align=\"%1\" valign=\"top\">\n"
95  ).arg( textDirection );
96 
97  QString tipInfo;
98  tipInfo += QString::fromLatin1(
99  " <strong>%1</strong>: %2<br>\n"
100  " <strong>%3</strong>: %4<br><br>\n"
101  ).arg( i18n( "Total Messages" ) ).arg( collection.statistics().count() )
102  .arg( i18n( "Unread Messages" ) ).arg( collection.statistics().unreadCount() );
103 
104  if ( collection.hasAttribute<CollectionQuotaAttribute>() ) {
105  CollectionQuotaAttribute *quota = collection.attribute<CollectionQuotaAttribute>();
106  if ( quota->currentValue() > -1 && quota->maximumValue() > 0 ) {
107  qreal percentage = ( 100.0 * quota->currentValue() ) / quota->maximumValue();
108 
109  if ( qAbs( percentage ) >= 0.01 ) {
110  QString percentStr = QString::number( percentage, 'f', 2 );
111  tipInfo += QString::fromLatin1(
112  " <strong>%1</strong>: %2%<br>\n"
113  ).arg( i18n( "Quota" ) ).arg( percentStr );
114  }
115  }
116  }
117 
118  qint64 currentFolderSize( collection.statistics().size() );
119  tipInfo += QString::fromLatin1(
120  " <strong>%1</strong>: %2<br>\n"
121  ).arg( i18n( "Storage Size" ) ).arg( KIO::convertSize( (KIO::filesize_t)( currentFolderSize ) ) );
122 
123  qint64 totalSize = 0;
124  getCountRecursive( index, totalSize );
125  totalSize -= currentFolderSize;
126  if (totalSize > 0 ) {
127  tipInfo += QString::fromLatin1(
128  "<strong>%1</strong>: %2<br>"
129  ).arg( i18n("Subfolder Storage Size") ).arg( KIO::convertSize( (KIO::filesize_t)( totalSize ) ) );
130  }
131 
132  QString iconName = CollectionUtils::defaultIconName( collection );
133  if ( collection.hasAttribute<EntityDisplayAttribute>() &&
134  !collection.attribute<EntityDisplayAttribute>()->iconName().isEmpty() ) {
135  if ( !collection.attribute<EntityDisplayAttribute>()->activeIconName().isEmpty() && collection.statistics().unreadCount()> 0) {
136  iconName = collection.attribute<EntityDisplayAttribute>()->activeIconName();
137  }
138  else
139  iconName = collection.attribute<EntityDisplayAttribute>()->iconName();
140  }
141 
142  int iconSizes[] = { 32, 22 };
143  int icon_size_found = 32;
144 
145  QString iconPath;
146 
147  for ( int i = 0; i < 2; i++ ) {
148  iconPath = KIconLoader::global()->iconPath( iconName, -iconSizes[ i ], true );
149  if ( !iconPath.isEmpty() ) {
150  icon_size_found = iconSizes[ i ];
151  break;
152  }
153  }
154 
155  if ( iconPath.isEmpty() ) {
156  iconPath = KIconLoader::global()->iconPath( QLatin1String( "folder" ), -32, false );
157  }
158 
159  QString tipIcon = QString::fromLatin1(
160  " <table border=\"0\"><tr><td width=\"32\" height=\"32\" align=\"center\" valign=\"middle\">\n"
161  " <img src=\"%1\" width=\"%2\" height=\"32\">\n"
162  " </td></tr></table>\n"
163  " </td>\n"
164  ).arg( iconPath ).arg( icon_size_found ) ;
165 
166  if ( QApplication::layoutDirection() == Qt::LeftToRight )
167  {
168  tip += tipInfo + QString::fromLatin1( "</td><td align=\"%3\" valign=\"top\">" ).arg( textDirection ) + tipIcon;
169  }
170  else
171  {
172  tip += tipIcon + QString::fromLatin1( "</td><td align=\"%3\" valign=\"top\">" ).arg( textDirection ) + tipInfo;
173  }
174 
175  tip += QString::fromLatin1(
176  " </tr>" \
177  "</table>"
178  );
179 
180  return tip;
181  }
182 
183  void proxyDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
184 
185  void sourceLayoutAboutToBeChanged();
186  void sourceLayoutChanged();
187 
188  QVector<QModelIndex> m_nonPersistent;
189  QVector<QModelIndex> m_nonPersistentFirstColumn;
190  QVector<QPersistentModelIndex> m_persistent;
191  QVector<QPersistentModelIndex> m_persistentFirstColumn;
192 
193  StatisticsProxyModel *mParent;
194 
195  bool mToolTipEnabled;
196  bool mExtraColumnsEnabled;
197 };
198 
199 void StatisticsProxyModel::Private::proxyDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
200 {
201  if ( mExtraColumnsEnabled )
202  {
203  // Ugly hack.
204  // The proper solution is a KExtraColumnsProxyModel, but this will do for now.
205  QModelIndex parent = topLeft.parent();
206  int parentColumnCount = mParent->columnCount( parent );
207  QModelIndex extraTopLeft = mParent->index( topLeft.row(), parentColumnCount - 1 - 3 , parent );
208  QModelIndex extraBottomRight = mParent->index( bottomRight.row(), parentColumnCount -1, parent );
209  mParent->disconnect( mParent, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
210  mParent, SLOT(proxyDataChanged(QModelIndex,QModelIndex)) );
211  emit mParent->dataChanged( extraTopLeft, extraBottomRight );
212 
213  // We get this signal when the statistics of a row changes.
214  // However, we need to emit data changed for the statistics of all ancestor rows too
215  // so that recursive totals can be updated.
216  while ( parent.isValid() )
217  {
218  emit mParent->dataChanged( parent.sibling( parent.row(), parentColumnCount - 1 - 3 ),
219  parent.sibling( parent.row(), parentColumnCount - 1 ) );
220  parent = parent.parent();
221  parentColumnCount = mParent->columnCount( parent );
222  }
223  mParent->connect( mParent, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
224  SLOT(proxyDataChanged(QModelIndex,QModelIndex)) );
225  }
226 }
227 
228 void StatisticsProxyModel::Private::sourceLayoutAboutToBeChanged()
229 {
230  QModelIndexList persistent = mParent->persistentIndexList();
231  const int columnCount = mParent->sourceModel()->columnCount();
232  foreach( const QModelIndex &idx, persistent ) {
233  if ( idx.column() >= columnCount ) {
234  m_nonPersistent.push_back( idx );
235  m_persistent.push_back( idx );
236  const QModelIndex firstColumn = idx.sibling( 0, idx.column() );
237  m_nonPersistentFirstColumn.push_back( firstColumn );
238  m_persistentFirstColumn.push_back( firstColumn );
239  }
240  }
241 }
242 
243 void StatisticsProxyModel::Private::sourceLayoutChanged()
244 {
245  QModelIndexList oldList;
246  QModelIndexList newList;
247 
248  const int columnCount = mParent->sourceModel()->columnCount();
249 
250  for ( int i = 0; i < m_persistent.size(); ++i ) {
251  const QModelIndex persistentIdx = m_persistent.at( i );
252  const QModelIndex nonPersistentIdx = m_nonPersistent.at( i );
253  if ( m_persistentFirstColumn.at( i ) != m_nonPersistentFirstColumn.at( i ) && persistentIdx.column() >= columnCount ) {
254  oldList.append( nonPersistentIdx );
255  newList.append( persistentIdx );
256  }
257  }
258  mParent->changePersistentIndexList( oldList, newList );
259 }
260 
261 void StatisticsProxyModel::setSourceModel(QAbstractItemModel* sourceModel)
262 {
263  // Order is important here. sourceLayoutChanged must be called *before* any downstreams react
264  // to the layoutChanged so that it can have the QPersistentModelIndexes uptodate in time.
265  disconnect(this, SIGNAL(layoutChanged()), this, SLOT(sourceLayoutChanged()));
266  connect(this, SIGNAL(layoutChanged()), SLOT(sourceLayoutChanged()));
267  QSortFilterProxyModel::setSourceModel(sourceModel);
268  // This one should come *after* any downstream handlers of layoutAboutToBeChanged.
269  // The connectNotify stuff below ensures that it remains the last one.
270  disconnect(this, SIGNAL(layoutAboutToBeChanged()), this, SLOT(sourceLayoutAboutToBeChanged()));
271  connect(this, SIGNAL(layoutAboutToBeChanged()), SLOT(sourceLayoutAboutToBeChanged()));
272 }
273 
274 void StatisticsProxyModel::connectNotify(const char *signal)
275 {
276  static bool ignore = false;
277  if (ignore || QLatin1String(signal) == SIGNAL(layoutAboutToBeChanged()))
278  return QSortFilterProxyModel::connectNotify(signal);
279  ignore = true;
280  disconnect(this, SIGNAL(layoutAboutToBeChanged()), this, SLOT(sourceLayoutAboutToBeChanged()));
281  connect(this, SIGNAL(layoutAboutToBeChanged()), SLOT(sourceLayoutAboutToBeChanged()));
282  ignore = false;
283  QSortFilterProxyModel::connectNotify(signal);
284 }
285 
286 StatisticsProxyModel::StatisticsProxyModel( QObject *parent )
287  : QSortFilterProxyModel( parent ),
288  d( new Private( this ) )
289 {
290  connect( this, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
291  SLOT(proxyDataChanged(QModelIndex,QModelIndex)) );
292 }
293 
294 StatisticsProxyModel::~StatisticsProxyModel()
295 {
296  delete d;
297 }
298 
299 void StatisticsProxyModel::setToolTipEnabled( bool enable )
300 {
301  d->mToolTipEnabled = enable;
302 }
303 
304 bool StatisticsProxyModel::isToolTipEnabled() const
305 {
306  return d->mToolTipEnabled;
307 }
308 
309 void StatisticsProxyModel::setExtraColumnsEnabled( bool enable )
310 {
311  d->mExtraColumnsEnabled = enable;
312 }
313 
314 bool StatisticsProxyModel::isExtraColumnsEnabled() const
315 {
316  return d->mExtraColumnsEnabled;
317 }
318 
319 QModelIndex Akonadi::StatisticsProxyModel::index( int row, int column, const QModelIndex & parent ) const
320 {
321  if (!hasIndex(row, column, parent))
322  return QModelIndex();
323 
324  int sourceColumn = column;
325 
326  if ( column>=d->sourceColumnCount( parent ) ) {
327  sourceColumn = 0;
328  }
329 
330  QModelIndex i = QSortFilterProxyModel::index( row, sourceColumn, parent );
331  return createIndex( i.row(), column, i.internalPointer() );
332 }
333 
334 QVariant StatisticsProxyModel::data( const QModelIndex & index, int role) const
335 {
336  if (!sourceModel())
337  return QVariant();
338  if ( role == Qt::DisplayRole && index.column()>=d->sourceColumnCount( index.parent() ) ) {
339  const QModelIndex sourceIndex = mapToSource( index.sibling( index.row(), 0 ) );
340  Collection collection = sourceModel()->data( sourceIndex, EntityTreeModel::CollectionRole ).value<Collection>();
341 
342  if ( collection.isValid() && collection.statistics().count()>=0 ) {
343  if ( index.column() == d->sourceColumnCount( QModelIndex() )+2 ) {
344  return KIO::convertSize( (KIO::filesize_t)( collection.statistics().size() ) );
345  } else if ( index.column() == d->sourceColumnCount( QModelIndex() )+1 ) {
346  return collection.statistics().count();
347  } else if ( index.column() == d->sourceColumnCount( QModelIndex() ) ) {
348  if ( collection.statistics().unreadCount() > 0 ) {
349  return collection.statistics().unreadCount();
350  } else {
351  return QString();
352  }
353  } else {
354  kWarning() << "We shouldn't get there for a column which is not total, unread or size.";
355  return QVariant();
356  }
357  }
358 
359  } else if ( role == Qt::TextAlignmentRole && index.column()>=d->sourceColumnCount( index.parent() ) ) {
360  return Qt::AlignRight;
361 
362  } else if ( role == Qt::ToolTipRole && d->mToolTipEnabled ) {
363  const QModelIndex sourceIndex = mapToSource( index.sibling( index.row(), 0 ) );
364  Collection collection
365  = sourceModel()->data( sourceIndex,
366  EntityTreeModel::CollectionRole ).value<Collection>();
367 
368  if ( collection.isValid() && collection.statistics().count()>0 ) {
369  return d->toolTipForCollection( index, collection );
370  }
371 
372  } else if ( role == Qt::DecorationRole && index.column() == 0 ) {
373  const QModelIndex sourceIndex = mapToSource( index.sibling( index.row(), 0 ) );
374  Collection collection = sourceModel()->data( sourceIndex, EntityTreeModel::CollectionRole ).value<Collection>();
375 
376  if ( collection.isValid() )
377  return KIcon( CollectionUtils::displayIconName( collection ) );
378  else
379  return QVariant();
380  }
381 
382  return QAbstractProxyModel::data( index, role );
383 }
384 
385 QVariant StatisticsProxyModel::headerData( int section, Qt::Orientation orientation, int role) const
386 {
387  if ( orientation == Qt::Horizontal && role == Qt::DisplayRole ) {
388  if ( section == d->sourceColumnCount( QModelIndex() ) + 2 ) {
389  return i18nc( "collection size", "Size" );
390  } else if ( section == d->sourceColumnCount( QModelIndex() ) + 1 ) {
391  return i18nc( "number of entities in the collection", "Total" );
392  } else if ( section == d->sourceColumnCount( QModelIndex() ) ) {
393  return i18nc( "number of unread entities in the collection", "Unread" );
394  }
395  }
396 
397  return QSortFilterProxyModel::headerData( section, orientation, role );
398 }
399 
400 Qt::ItemFlags StatisticsProxyModel::flags( const QModelIndex & index ) const
401 {
402  if ( index.column()>=d->sourceColumnCount( index.parent() ) ) {
403  return QSortFilterProxyModel::flags( index.sibling( index.row(), 0 ) )
404  & ( Qt::ItemIsSelectable | Qt::ItemIsDragEnabled // Allowed flags
405  | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled );
406  }
407 
408  return QSortFilterProxyModel::flags( index );
409 }
410 
411 int StatisticsProxyModel::columnCount( const QModelIndex & parent ) const
412 {
413  if ( sourceModel()==0 ) {
414  return 0;
415  } else {
416  return d->sourceColumnCount( parent )
417  + ( d->mExtraColumnsEnabled ? 3 : 0 );
418  }
419 }
420 
421 QModelIndexList StatisticsProxyModel::match( const QModelIndex& start, int role, const QVariant& value,
422  int hits, Qt::MatchFlags flags ) const
423 {
424  if ( role < Qt::UserRole )
425  return QSortFilterProxyModel::match( start, role, value, hits, flags );
426 
427  QModelIndexList list;
428  QModelIndex proxyIndex;
429  foreach ( const QModelIndex &idx, sourceModel()->match( mapToSource( start ), role, value, hits, flags ) ) {
430  proxyIndex = mapFromSource( idx );
431  if ( proxyIndex.isValid() )
432  list << proxyIndex;
433  }
434 
435  return list;
436 }
437 
438 #include "moc_statisticsproxymodel.cpp"
439 
Akonadi::EntityDisplayAttribute::activeIconName
QString activeIconName() const
Returns the icon name of an active item.
Definition: entitydisplayattribute.cpp:148
Akonadi::CollectionStatistics::count
qint64 count() const
Returns the number of items in this collection or -1 if this information is not available.
Definition: collectionstatistics.cpp:67
Akonadi::EntityDisplayAttribute::iconName
QString iconName() const
Returns the icon name of the icon returned by icon().
Definition: entitydisplayattribute.cpp:64
Akonadi::CollectionStatistics
Provides statistics information of a Collection.
Definition: collectionstatistics.h:69
Akonadi::StatisticsProxyModel::StatisticsProxyModel
StatisticsProxyModel(QObject *parent=0)
Creates a new statistics proxy model.
Definition: statisticsproxymodel.cpp:286
Akonadi::StatisticsProxyModel::isExtraColumnsEnabled
bool isExtraColumnsEnabled() const
Return true if we display extra statistics columns, otherwise false.
Definition: statisticsproxymodel.cpp:314
Akonadi::Collection
Represents a collection of PIM items.
Definition: collection.h:75
Akonadi::Entity::attribute
Attribute * attribute(const QByteArray &name) const
Returns the attribute of the given type name if available, 0 otherwise.
Definition: entity.cpp:165
Akonadi::CollectionQuotaAttribute::currentValue
qint64 currentValue() const
Returns the current quota value in bytes.
Definition: collectionquotaattribute.cpp:63
Akonadi::StatisticsProxyModel
A proxy model that exposes collection statistics through extra columns.
Definition: statisticsproxymodel.h:51
Akonadi::StatisticsProxyModel::setToolTipEnabled
void setToolTipEnabled(bool enable)
Definition: statisticsproxymodel.cpp:299
Akonadi::EntityTreeModel::CollectionRole
The collection.
Definition: entitytreemodel.h:335
Akonadi::CollectionQuotaAttribute::maximumValue
qint64 maximumValue() const
Returns the maximum quota value in bytes.
Definition: collectionquotaattribute.cpp:68
Akonadi::StatisticsProxyModel::~StatisticsProxyModel
virtual ~StatisticsProxyModel()
Destroys the statistics proxy model.
Definition: statisticsproxymodel.cpp:294
Akonadi::StatisticsProxyModel::isToolTipEnabled
bool isToolTipEnabled() const
Return true if we display tooltips, otherwise false.
Definition: statisticsproxymodel.cpp:304
Akonadi::Entity::hasAttribute
bool hasAttribute(const QByteArray &name) const
Returns true if the entity has an attribute of the given type name, false otherwise.
Definition: entity.cpp:146
Akonadi::CollectionStatistics::unreadCount
qint64 unreadCount() const
Returns the number of unread items in this collection or -1 if this information is not available...
Definition: collectionstatistics.cpp:77
Akonadi::Entity::isValid
bool isValid() const
Returns whether the entity is valid.
Definition: entity.cpp:97
Akonadi::StatisticsProxyModel::setExtraColumnsEnabled
void setExtraColumnsEnabled(bool enable)
Definition: statisticsproxymodel.cpp:309
Akonadi::CollectionQuotaAttribute
Attribute that provides quota information for a collection.
Definition: collectionquotaattribute.h:50
Akonadi::Collection::statistics
CollectionStatistics statistics() const
Returns the collection statistics of the collection.
Definition: collection.cpp:238
Akonadi::EntityDisplayAttribute
Attribute that stores the properties that are used to display an entity.
Definition: entitydisplayattribute.h:39
Akonadi::CollectionStatistics::size
qint64 size() const
Returns the total size of the items in this collection or -1 if this information is not available...
Definition: collectionstatistics.cpp:87
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 23:00:28 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kldap
  • kmbox
  • kmime
  • kpimidentities
  • kpimtextedit
  • kresources
  • ktnef
  • kxmlrpcclient
  • microblog

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