• Skip to content
  • Skip to link menu
KDE 4.4 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

akonadi

krecursivefilterproxymodel.cpp

00001 /*
00002     Copyright (c) 2009 Stephen Kelly <steveire@gmail.com>
00003 
00004     This library is free software; you can redistribute it and/or modify it
00005     under the terms of the GNU Library General Public License as published by
00006     the Free Software Foundation; either version 2 of the License, or (at your
00007     option) any later version.
00008 
00009     This library is distributed in the hope that it will be useful, but WITHOUT
00010     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00011     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00012     License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public License
00015     along with this library; see the file COPYING.LIB.  If not, write to the
00016     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017     02110-1301, USA.
00018 */
00019 
00020 #include "krecursivefilterproxymodel.h"
00021 
00022 #include <kdebug.h>
00023 
00024 class KRecursiveFilterProxyModelPrivate
00025 {
00026   Q_DECLARE_PUBLIC(KRecursiveFilterProxyModel)
00027   KRecursiveFilterProxyModel *q_ptr;
00028 public:
00029   KRecursiveFilterProxyModelPrivate(KRecursiveFilterProxyModel *model)
00030     : q_ptr(model),
00031       ignoreRemove(false),
00032       completeInsert(false),
00033       completeRemove(false)
00034   {
00035     qRegisterMetaType<QModelIndex>("QModelIndex");
00036   }
00037 
00038   inline void invokeDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
00039   {
00040     Q_Q(KRecursiveFilterProxyModel);
00041     bool success = QMetaObject::invokeMethod(q, "_q_sourceDataChanged", Qt::DirectConnection,
00042         Q_ARG(QModelIndex, topLeft),
00043         Q_ARG(QModelIndex, bottomRight));
00044     Q_ASSERT(success);
00045   }
00046 
00047   inline void invokeRowsInserted(const QModelIndex &source_parent, int start, int end)
00048   {
00049     Q_Q(KRecursiveFilterProxyModel);
00050     bool success = QMetaObject::invokeMethod(q, "_q_sourceRowsInserted", Qt::DirectConnection,
00051         Q_ARG(QModelIndex, source_parent),
00052         Q_ARG(int, start),
00053         Q_ARG(int, end));
00054    Q_ASSERT(success);
00055   }
00056 
00057   inline void invokeRowsAboutToBeInserted(const QModelIndex &source_parent, int start, int end)
00058   {
00059     Q_Q(KRecursiveFilterProxyModel);
00060     bool success = QMetaObject::invokeMethod(q, "_q_sourceRowsAboutToBeInserted", Qt::DirectConnection,
00061         Q_ARG(QModelIndex, source_parent),
00062         Q_ARG(int, start),
00063         Q_ARG(int, end));
00064     Q_ASSERT(success);
00065   }
00066 
00067   inline void invokeRowsRemoved(const QModelIndex &source_parent, int start, int end)
00068   {
00069     Q_Q(KRecursiveFilterProxyModel);
00070     bool success = QMetaObject::invokeMethod(q, "_q_sourceRowsRemoved", Qt::DirectConnection,
00071         Q_ARG(QModelIndex, source_parent),
00072         Q_ARG(int, start),
00073         Q_ARG(int, end));
00074     Q_ASSERT(success);
00075   }
00076 
00077   inline void invokeRowsAboutToBeRemoved(const QModelIndex &source_parent, int start, int end)
00078   {
00079     Q_Q(KRecursiveFilterProxyModel);
00080     bool success = QMetaObject::invokeMethod(q, "_q_sourceRowsAboutToBeRemoved", Qt::DirectConnection,
00081         Q_ARG(QModelIndex, source_parent),
00082         Q_ARG(int, start),
00083         Q_ARG(int, end));
00084     Q_ASSERT(success);
00085   }
00086 
00087   void sourceDataChanged(const QModelIndex &source_top_left, const QModelIndex &source_bottom_right);
00088   void sourceRowsAboutToBeInserted(const QModelIndex &source_parent, int start, int end);
00089   void sourceRowsInserted(const QModelIndex &source_parent, int start, int end);
00090   void sourceRowsAboutToBeRemoved(const QModelIndex &source_parent, int start, int end);
00091   void sourceRowsRemoved(const QModelIndex &source_parent, int start, int end);
00092 
00099   void refreshAscendantMapping(const QModelIndex &index, bool refreshAll = false);
00100 
00101   bool ignoreRemove;
00102   bool completeInsert;
00103   bool completeRemove;
00104 };
00105 
00106 void KRecursiveFilterProxyModelPrivate::sourceDataChanged(const QModelIndex &source_top_left, const QModelIndex &source_bottom_right)
00107 {
00108   Q_Q(KRecursiveFilterProxyModel);
00109 
00110   QModelIndex source_parent = source_top_left.parent();
00111 
00112   if (!source_parent.isValid() || q->filterAcceptsRow(source_parent.row(), source_parent.parent()))
00113   {
00114     invokeDataChanged(source_top_left, source_bottom_right);
00115     return;
00116   }
00117 
00118   int start = -1;
00119   int end = -1;
00120   bool requireRow = false;
00121   for (int row = source_top_left.row(); row <= source_bottom_right.row(); ++row)
00122     if (q->filterAcceptsRow(row, source_parent))
00123     {
00124       requireRow = true;
00125       break;
00126     }
00127 
00128   if (!requireRow) // None of the changed rows are now required in the model.
00129     return;
00130 
00131   refreshAscendantMapping(source_parent);
00132 }
00133 
00134 void KRecursiveFilterProxyModelPrivate::refreshAscendantMapping(const QModelIndex &index, bool refreshAll)
00135 {
00136   Q_Q(KRecursiveFilterProxyModel);
00137 
00138   Q_ASSERT(index.isValid());
00139   QModelIndex lastAscendant = index;
00140   QModelIndex sourceAscendant = index.parent();
00141   int lastRow;
00142   // We got a matching descendant, so find the right place to insert the row.
00143   // We need to tell the QSortFilterProxyModel that the first child between an existing row in the model
00144   // has changed data so that it will get a mapping.
00145   while(sourceAscendant.isValid() && !q->acceptRow(sourceAscendant.row(), sourceAscendant.parent()))
00146   {
00147     if (refreshAll)
00148       invokeDataChanged(sourceAscendant, sourceAscendant);
00149 
00150     lastAscendant = sourceAscendant;
00151     sourceAscendant = sourceAscendant.parent();
00152   }
00153 
00154   // Inform the model that its data changed so that it creates new mappings and finds the rows which now match the filter.
00155   invokeDataChanged(lastAscendant, lastAscendant);
00156 }
00157 
00158 void KRecursiveFilterProxyModelPrivate::sourceRowsAboutToBeInserted(const QModelIndex &source_parent, int start, int end)
00159 {
00160   Q_Q(KRecursiveFilterProxyModel);
00161 
00162   if (!source_parent.isValid() || q->filterAcceptsRow(source_parent.row(), source_parent.parent()))
00163   {
00164     invokeRowsAboutToBeInserted(source_parent, start, end);
00165     completeInsert = true;
00166   }
00167 }
00168 
00169 void KRecursiveFilterProxyModelPrivate::sourceRowsInserted(const QModelIndex &source_parent, int start, int end)
00170 {
00171   Q_Q(KRecursiveFilterProxyModel);
00172 
00173   if (completeInsert)
00174   {
00175     completeInsert = false;
00176     invokeRowsInserted(source_parent, start, end);
00177     // If the parent is already in the model, we can just pass on the signal.
00178     return;
00179   }
00180 
00181   bool requireRow = false;
00182   for (int row = start; row <= end; ++row)
00183   {
00184     if (q->filterAcceptsRow(row, source_parent))
00185     {
00186       requireRow = true;
00187       break;
00188     }
00189   }
00190 
00191   if (!requireRow)
00192   {
00193     // The row doesn't have descendants that match the filter. Filter it out.
00194     return;
00195   }
00196 
00197   refreshAscendantMapping(source_parent);
00198 }
00199 
00200 void KRecursiveFilterProxyModelPrivate::sourceRowsAboutToBeRemoved(const QModelIndex &source_parent, int start, int end)
00201 {
00202   Q_Q(KRecursiveFilterProxyModel);
00203 
00204   if (q->filterAcceptsRow(source_parent.row(), source_parent.parent()))
00205   {
00206     invokeRowsAboutToBeRemoved(source_parent, start, end);
00207     completeRemove = true;
00208     return;
00209   }
00210 
00211   bool accepted = false;
00212   for (int row = start; row < end; ++row)
00213   {
00214     if (q->filterAcceptsRow(row, source_parent))
00215     {
00216       accepted = true;
00217       break;
00218     }
00219   }
00220   if (!accepted)
00221   {
00222     // All removed rows are already filtered out. We don't care about the signal.
00223     ignoreRemove = true;
00224   }
00225 }
00226 
00227 void KRecursiveFilterProxyModelPrivate::sourceRowsRemoved(const QModelIndex &source_parent, int start, int end)
00228 {
00229   Q_Q(KRecursiveFilterProxyModel);
00230 
00231   if (completeRemove)
00232   {
00233     completeRemove = false;
00234     // Source parent is already in the model.
00235     invokeRowsRemoved(source_parent, start, end);
00236     // fall through. After removing rows, we need to refresh things so that intermediates will be removed too if necessary.
00237   }
00238 
00239   if (ignoreRemove)
00240   {
00241     ignoreRemove = false;
00242     return;
00243   }
00244 
00245   // Refresh intermediate rows too.
00246   // This is needed because QSFPM only invalidates the mapping for the
00247   // index range given to dataChanged, not its children.
00248   refreshAscendantMapping(source_parent, true);
00249 }
00250 
00251 KRecursiveFilterProxyModel::KRecursiveFilterProxyModel(QObject* parent)
00252     : QSortFilterProxyModel(parent), d_ptr(new KRecursiveFilterProxyModelPrivate(this))
00253 {
00254   setDynamicSortFilter(true);
00255 }
00256 
00257 KRecursiveFilterProxyModel::~KRecursiveFilterProxyModel()
00258 {
00259 
00260 }
00261 
00262 bool KRecursiveFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
00263 {
00264   Q_ASSERT(sourceModel()->index(sourceRow, 0, sourceParent).isValid());
00265   QVariant da = sourceModel()->index(sourceRow, 0, sourceParent).data();
00266 
00267   if (acceptRow(sourceRow, sourceParent))
00268     return true;
00269 
00270   QModelIndex source_index = sourceModel()->index(sourceRow, 0, sourceParent);
00271   Q_ASSERT(source_index.isValid());
00272   bool accepted = false;
00273 
00274   for (int row = 0 ; row < sourceModel()->rowCount(source_index); ++row)
00275     if (filterAcceptsRow(row, source_index))
00276       accepted = true; // Need to do this in a loop so that all siblings in a parent get processed, not just the first.
00277 
00278   return accepted;
00279 }
00280 
00281 bool KRecursiveFilterProxyModel::acceptRow(int sourceRow, const QModelIndex& sourceParent) const
00282 {
00283   return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
00284 }
00285 
00286 void KRecursiveFilterProxyModel::setSourceModel(QAbstractItemModel* model)
00287 {
00288   Q_D(KRecursiveFilterProxyModel);
00289 
00290   // Standard disconnect.
00291   disconnect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
00292       this, SLOT(sourceDataChanged(QModelIndex,QModelIndex)));
00293 
00294   disconnect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
00295       this, SLOT(sourceRowsAboutToBeInserted(QModelIndex,int,int)));
00296 
00297   disconnect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
00298       this, SLOT(sourceRowsInserted(QModelIndex,int,int)));
00299 
00300   disconnect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
00301       this, SLOT(sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
00302 
00303   disconnect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
00304       this, SLOT(sourceRowsRemoved(QModelIndex,int,int)));
00305 
00306   QSortFilterProxyModel::setSourceModel(model);
00307 
00308   // Disconnect in the QSortFilterProxyModel. These methods will be invoked manually
00309   disconnect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
00310       this, SLOT(_q_sourceDataChanged(QModelIndex,QModelIndex)));
00311 
00312   disconnect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
00313       this, SLOT(_q_sourceRowsAboutToBeInserted(QModelIndex,int,int)));
00314 
00315   disconnect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
00316       this, SLOT(_q_sourceRowsInserted(QModelIndex,int,int)));
00317 
00318   disconnect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
00319       this, SLOT(_q_sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
00320 
00321   disconnect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
00322       this, SLOT(_q_sourceRowsRemoved(QModelIndex,int,int)));
00323 
00324   // Slots for manual invoking of QSortFilterProxyModel methods.
00325   connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
00326       this, SLOT(sourceDataChanged(QModelIndex,QModelIndex)));
00327 
00328   connect(model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
00329       this, SLOT(sourceRowsAboutToBeInserted(QModelIndex,int,int)));
00330 
00331   connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
00332       this, SLOT(sourceRowsInserted(QModelIndex,int,int)));
00333 
00334   connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
00335       this, SLOT(sourceRowsAboutToBeRemoved(QModelIndex,int,int)));
00336 
00337   connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
00338       this, SLOT(sourceRowsRemoved(QModelIndex,int,int)));
00339 
00340 }
00341 
00342 QModelIndexList KRecursiveFilterProxyModel::match(const QModelIndex& start, int role, const QVariant& value, int hits, Qt::MatchFlags flags) const
00343 {
00344   if (role < Qt::UserRole)
00345     return QSortFilterProxyModel::match(start, role, value, hits, flags);
00346 
00347   QModelIndexList list;
00348   QModelIndex proxyIndex;
00349   foreach(const QModelIndex idx, sourceModel()->match(mapToSource(start), role, value, hits, flags))
00350   {
00351     proxyIndex = mapFromSource(idx);
00352     if (proxyIndex.isValid())
00353       list << proxyIndex;
00354   }
00355   return list;
00356 }
00357 
00358 
00359 #include "krecursivefilterproxymodel.moc"

akonadi

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

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kblog
  • kcal
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal