[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    KDE/kdelibs/kio
From:       David Faure <faure () kde ! org>
Date:       2006-10-31 23:18:37
Message-ID: 1162336717.080717.30992.nullmailer () svn ! kde ! org
[Download RAW message or body]

SVN commit 600836 by dfaure:

Support for previews in KDirModel, which can store the pixmaps set with \
setData(Qt::DecorationRole) Performance improvements in KDirModel, documented the \
complexity of the fooForBar methods.


 M  +91 -37    kio/kdirmodel.cpp  
 M  +6 -1      kio/kdirmodel.h  
 M  +25 -2     tests/kdirmodeltest.cpp  
 M  +1 -0      tests/kdirmodeltest.h  
 M  +1 -0      tests/kfileitemtest.cpp  


--- trunk/KDE/kdelibs/kio/kio/kdirmodel.cpp #600835:600836
@@ -16,6 +16,7 @@
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
 */
+
 #include "kdirmodel.h"
 #include "kdirlister.h"
 #include "kfileitem.h"
@@ -32,10 +33,11 @@
 class KDirModelNode
 {
 public:
-    KDirModelNode( KDirModelNode* parent = 0 ) :
-        m_item(0),
+    KDirModelNode( KDirModelNode* parent, KFileItem* item ) :
         m_childNodes(),
-        m_parent(parent)
+        m_item(item),
+        m_parent(parent),
+        m_preview()
     {
     }
     ~KDirModelNode() {
@@ -46,34 +48,39 @@
     KFileItem* item() const { return m_item; }
     KDirModelNode* parent() const { return m_parent; }
     // linear search
-    int rowNumber() const { if (!m_parent) return 0;
-        return m_parent->m_childNodes.indexOf(const_cast<KDirModelNode*>(this)); }
+    int rowNumber() const {
+        if (!m_parent) return 0;
+        return m_parent->m_childNodes.indexOf(const_cast<KDirModelNode*>(this));
+    }
+    QIcon preview() const { return m_preview; }
+    void addPreview( const QPixmap& pix ) { m_preview.addPixmap(pix); }
+    void setPreview( const QIcon& icn ) { m_preview = icn; }
 
-    KFileItem* m_item;
-
     // TODO maybe move this field to a DirNode subclass to save 4 bytes per file?
     QList<KDirModelNode *> m_childNodes;
 private:
+    KFileItem* m_item;
     KDirModelNode* const m_parent;
+    QIcon m_preview;
 };
 
 class KDirModelPrivate
 {
 public:
     KDirModelPrivate( KDirModel* model )
-        : q(model), m_dirLister(0), m_rootNode(new KDirModelNode) {
+        : q(model), m_dirLister(0), m_rootNode(new KDirModelNode(0, 0)) {
     }
     ~KDirModelPrivate() {
         delete m_rootNode;
     }
     void clear() {
         delete m_rootNode;
-        m_rootNode = new KDirModelNode;
+        m_rootNode = new KDirModelNode(0, 0);
     }
-    //KDirModelNode* nodeForFileItem(KFileItem* item) const;
-    KDirModelNode* nodeForUrl(const KUrl& _url) const;
+    QPair<int /*row*/, KDirModelNode*> nodeForUrl(const KUrl& url) const;
     KDirModelNode* nodeForIndex(const QModelIndex& index) const;
-    QModelIndex indexForNode(KDirModelNode* node) const;
+    QModelIndex indexForNode(KDirModelNode* node, int rowNumber = -1 /*unknown*/) \
const; +    QModelIndex indexForUrl(const KUrl& url) const;
 
     KDirModel* q;
     KDirLister* m_dirLister;
@@ -83,14 +90,13 @@
 // If we want to support arbitrary trees like "home:/ as a child of system:/" then,
 // we need to get the parent KFileItem in slotNewItems, and then we can use a \
QHash<KFileItem*,KDirModelNode*> cache.  // For now we'll assume "child url = parent \
                url + filename"
-
-KDirModelNode* KDirModelPrivate::nodeForUrl(const KUrl& _url) const
+QPair<int /*row*/, KDirModelNode*> KDirModelPrivate::nodeForUrl(const KUrl& _url) \
const // O(n*m)  {
     KUrl url(_url);
     url.adjustPath(KUrl::RemoveTrailingSlash);
     //kDebug() << k_funcinfo << url << endl;
     if (url == m_dirLister->url())
-        return m_rootNode;
+        return qMakePair(0, m_rootNode);
 
     const QString urlStr = url.url();
     KDirModelNode* node = m_rootNode;
@@ -100,11 +106,12 @@
         bool foundChild = false;
         QList<KDirModelNode *>::const_iterator it = node->m_childNodes.begin();
         const QList<KDirModelNode *>::const_iterator end = node->m_childNodes.end();
-        for ( ; it != end ; ++it ) {
+        int row = 0;
+        for ( ; it != end ; ++it, ++row ) {
             const KUrl u = (*it)->item()->url();
             if ( u == url ) {
                 //kDebug() << "Found! " << u << endl;
-                return *it;
+                return qMakePair(row, *it);
             }
             if ( urlStr.startsWith(u.url()) ) {
                 node = *it;
@@ -115,25 +122,25 @@
         }
         if (!foundChild) {
             //kDebug() << "child equal or starting with " << url << " not found" << \
                endl;
-            return false;
+            return qMakePair(0, static_cast<KDirModelNode*>(0));
         }
         nodeUrl = node->item()->url();
         //kDebug() << " " << nodeUrl << endl;
     }
-    return 0;
+    return qMakePair(0, static_cast<KDirModelNode*>(0));
 }
 
-QModelIndex KDirModelPrivate::indexForNode(KDirModelNode* node) const
+// node -> index. If rowNumber is set (or node is root): O(1). Otherwise: O(n).
+QModelIndex KDirModelPrivate::indexForNode(KDirModelNode* node, int rowNumber) const
 {
     if (node == m_rootNode)
         return QModelIndex();
 
-    KDirModelNode* const parentNode = node->parent();
-    Q_ASSERT(parentNode);
-    const int row = parentNode->m_childNodes.indexOf(node);
-    return q->createIndex(row, 0, node);
+    Q_ASSERT(node->parent());
+    return q->createIndex(rowNumber == -1 ? node->rowNumber() : rowNumber, 0, node);
 }
 
+// index -> node. O(1)
 KDirModelNode* KDirModelPrivate::nodeForIndex(const QModelIndex& index) const
 {
     return index.isValid()
@@ -141,7 +148,19 @@
         : m_rootNode;
 }
 
+// url -> index. O(n*m)
+QModelIndex KDirModelPrivate::indexForUrl(const KUrl& url) const
+{
+    const QPair<int, KDirModelNode*> result = nodeForUrl(url); // O(n*m) (m is the \
depth from the root) +    if (!result.second) {
+        kWarning() << "KDirModelPrivate::indexForUrl: " << url << " not found" << \
endl; +        return QModelIndex();
+    }
+    return indexForNode(result.second, result.first); // O(1)
+}
 
+
+// We don't use QHash<KUrl,...> anymore, it's too slow.
 // Idea from George, to make QHash<KUrl,...> fast: - cache hash value into QUrl or \
KUrl  // This also helps making operator== fast [which means operator== has to call \
qHash if cached value isn't there]  // But it means invalidating the cached hash \
value when the url is modified, @@ -218,18 +237,18 @@
 
     //kDebug() << k_funcinfo << "dir=" << dir << endl;
 
-    KDirModelNode* dirNode = d->nodeForUrl(dir);
+    const QPair<int, KDirModelNode*> result = d->nodeForUrl(dir); // O(n*m)
+    KDirModelNode* dirNode = result.second;
 
     Q_ASSERT(dirNode);
     KFileItemList::const_iterator it = items.begin();
     const KFileItemList::const_iterator end = items.end();
     for ( ; it != end ; ++it ) {
-        KDirModelNode* node = new KDirModelNode( dirNode );
-        node->m_item = *it;
+        KDirModelNode* node = new KDirModelNode( dirNode, *it );
         dirNode->m_childNodes.append( node );
     }
 
-    const QModelIndex index = d->indexForNode(dirNode);
+    const QModelIndex index = d->indexForNode(dirNode, result.first); // O(1)
     const int newRowCount = dirNode->m_childNodes.count();
     kDebug() << k_funcinfo << items.count() << " in " << dir
              << " index=" << debugIndex(index) << " newRowCount=" << newRowCount << \
endl; @@ -244,19 +263,18 @@
     //KUrl dir( item->url().upUrl() );
     //dir.adjustPath(KUrl::RemoveTrailingSlash);
 
-    KDirModelNode* node = d->nodeForUrl(item->url());
+    const QPair<int, KDirModelNode*> result = d->nodeForUrl(item->url()); // O(n*m)
+    const int rowNumber = result.first;
+    KDirModelNode* node = result.second;
     Q_ASSERT(node);
     if (!node)
         return;
 
-    const QModelIndex parentIndex = d->indexForNode(node->parent());
-    const int rowNumber = node->rowNumber();
-    //kDebug() << k_funcinfo << debugIndex(parentIndex) << " " << rowNumber << endl;
-
-    KDirModelNode* dirNode = d->nodeForIndex(parentIndex);
+    KDirModelNode* dirNode = node->parent();
     Q_ASSERT(dirNode);
     dirNode->m_childNodes.removeAt(rowNumber);
 
+    QModelIndex parentIndex = d->indexForNode(dirNode); // O(n)
     beginRemoveRows( parentIndex, rowNumber, rowNumber );
     endRemoveRows();
 }
@@ -268,7 +286,7 @@
     // Solution 1: we could emit dataChanged for one row (if items.size()==1) or all \
                rows
     // Solution 2: more fine-grained, actually figure out the beginning and end \
                rows.
     for ( KFileItemList::const_iterator fit = items.begin(), fend = items.end() ; \
                fit != fend ; ++fit ) {
-        const QModelIndex index = d->indexForNode(d->nodeForUrl( (*fit)->url() ));
+        const QModelIndex index = d->indexForUrl( (*fit)->url() ); // O(n*m); maybe \
we could look up to the parent only once  if (!topLeft.isValid() || index.row() < \
topLeft.row()) {  topLeft = index;
         }
@@ -300,7 +318,8 @@
 QVariant KDirModel::data( const QModelIndex & index, int role ) const
 {
     if (index.isValid()) {
-        KFileItem* item = \
static_cast<KDirModelNode*>(index.internalPointer())->item(); +        KDirModelNode* \
node = static_cast<KDirModelNode*>(index.internalPointer()); +        KFileItem* item \
= node->item();  switch (role) {
         case Qt::DisplayRole:
             switch (index.column()) {
@@ -326,6 +345,10 @@
             break;
         case Qt::DecorationRole:
             if (index.column() == Name) {
+                if (!node->preview().isNull()) {
+                    //kDebug() << item->url() << " preview found" << endl;
+                    return node->preview();
+                }
                 Q_ASSERT(item);
                 const int overlays = item->overlays();
                 //kDebug() << item->url() << " overlays=" << overlays << endl;
@@ -339,12 +362,38 @@
 
 void KDirModel::sort( int column, Qt::SortOrder order )
 {
+    // Not implemented - we should probably use QSortFilterProxyModel instead.
     return QAbstractItemModel::sort(column, order);
 }
 
 bool KDirModel::setData( const QModelIndex & index, const QVariant & value, int role \
)  {
-    return QAbstractItemModel::setData(index, value, role);
+    switch (role) {
+    case Qt::DisplayRole:
+        // TODO handle renaming here?
+        break;
+    case Qt::DecorationRole:
+        if (index.column() == Name) {
+            Q_ASSERT(index.isValid());
+            // Set new pixmap - e.g. preview
+            KDirModelNode* node = \
static_cast<KDirModelNode*>(index.internalPointer()); +            //kDebug() << \
"setting icon for " << node->item()->url() << endl; +            Q_ASSERT(node);
+            if (value.type() == QVariant::Icon) {
+                const QIcon icon(qvariant_cast<QIcon>(value));
+                Q_ASSERT(!icon.isNull());
+                node->setPreview(icon);
+            } else if (value.type() == QVariant::Pixmap) {
+                node->addPreview(qvariant_cast<QPixmap>(value));
+            }
+            emit dataChanged(index, index);
+            return true;
+        }
+        break;
+    default:
+        break;
+    }
+    return false;
 }
 
 int KDirModel::rowCount( const QModelIndex & parent ) const
@@ -396,6 +445,11 @@
     }
 }
 
+QModelIndex KDirModel::indexForItem( const KFileItem* item ) const
+{
+    return d->indexForUrl(item->url()); // O(n*m)
+}
+
 QModelIndex KDirModel::index( int row, int column, const QModelIndex & parent ) \
const  {
     KDirModelNode* parentNode = d->nodeForIndex(parent);
--- trunk/KDE/kdelibs/kio/kio/kdirmodel.h #600835:600836
@@ -56,10 +56,15 @@
     KDirLister* dirLister() const;
 
     /**
-     * Return the fileitem for a given index.
+     * Return the fileitem for a given index. This is O(1), i.e. fast.
      */
     KFileItem* itemForIndex( const QModelIndex& index ) const;
 
+    /**
+     * Return the index for a given kfileitem. This is slow.
+     */
+    QModelIndex indexForItem( const KFileItem* ) const;
+
     /***
      * Useful "default" columns. Views can use a proxy to have more control over \
                this.
      */
--- trunk/KDE/kdelibs/kio/tests/kdirmodeltest.cpp #600835:600836
@@ -23,11 +23,13 @@
 #include <kdirlister.h>
 
 #include <qtest_kde.h>
-#include <kdebug.h>
+
 #ifdef Q_OS_UNIX
 #include <utime.h>
-#include <kdirlister.h>
 #endif
+#include <kdebug.h>
+#include <kio/deletejob.h>
+#include <kio/netaccess.h>
 
 QTEST_KDEMAIN( KDirModelTest, NoGUI )
 
@@ -249,3 +251,24 @@
     fillModel( true );
     testItemForIndex();
 }
+
+void KDirModelTest::testDeleteFile()
+{
+    const QString file = m_tempDir.name() + "toplevelfile_1";
+    const KUrl url(file);
+
+    qRegisterMetaType<QModelIndex>("QModelIndex"); // beats me why Qt doesn't do \
that +    connect( &m_dirModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+             this, SIGNAL(exitLoop()) );
+
+    KIO::DeleteJob* job = KIO::del(url);
+    bool ok = KIO::NetAccess::synchronousRun(job, 0);
+    QVERIFY(ok);
+
+    // Wait for the DBUS signal from KDirNotify, it's the one the triggers \
rowsRemoved +    enterLoop();
+
+    // If we come here, then rowsRemoved() was emitted - all good.
+    const int topLevelRowCount = m_dirModel.rowCount();
+    QCOMPARE(topLevelRowCount, 3); // one less than before
+}
--- trunk/KDE/kdelibs/kio/tests/kdirmodeltest.h #600835:600836
@@ -35,6 +35,7 @@
     void testItemForIndex();
     void testData();
     void testReload();
+    void testDeleteFile();
 
 Q_SIGNALS:
     void exitLoop();
--- trunk/KDE/kdelibs/kio/tests/kfileitemtest.cpp #600835:600836
@@ -129,3 +129,4 @@
     QVERIFY(fileItem.cmp(fileItem2));
 }
 
+// TODO test KFileItem(UDSEntry), for instance doing a synchronous kio listing or \
stat.


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic