~ubuntu-branches/ubuntu/jaunty/kde4libs/jaunty-updates

« back to all changes in this revision

Viewing changes to kio/kio/kdirmodel.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Harald Sitter
  • Date: 2008-12-11 18:26:08 UTC
  • mfrom: (1.1.24 upstream)
  • Revision ID: james.westby@ubuntu.com-20081211182608-tsu6p8ncbw1gnqxt
Tags: 4:4.1.85-0ubuntu1
* New upstream release
* Patches:
  + Removed 15_kfreebsd_support.diff from patches/series (doesn't apply and
    has no use for Ubuntu)
  + Redid 20_use_dejavu_as_default_font.diff
  + Completely removed kubuntu_09_fix_application_menu.diff (applied upstream)
  + Refreshed kubuntu_54_use_xdg_menu_prefix.diff
  + Dropped plasma/widgets/toolbutton.cpp from kubuntu_qt_ftbfs.diff (applied
    upstream)
  + Global quilt refresh

Show diffs side-by-side

added added

removed removed

Lines of Context:
38
38
class KDirModelNode;
39
39
class KDirModelDirNode;
40
40
 
 
41
static KUrl cleanupUrl(const KUrl& url) {
 
42
    KUrl u = url;
 
43
    u.adjustPath(KUrl::RemoveTrailingSlash); // KDirLister does this too, so we remove the slash before comparing with the root node url.
 
44
    u.cleanPath(); // remove double slashes in the path
 
45
    u.setQuery(QString());
 
46
    u.setRef(QString());
 
47
    return u;
 
48
}
 
49
 
41
50
// We create our own tree behind the scenes to have fast lookup from an item to its parent,
42
51
// and also to get the children of an item fast.
43
52
class KDirModelNode
87
96
    bool isPopulated() const { return m_populated; }
88
97
    void setPopulated( bool populated ) { m_populated = populated; }
89
98
 
 
99
    // For removing all child urls from the global hash.
 
100
    void collectAllChildUrls(KUrl::List &urls) const {
 
101
        Q_FOREACH(KDirModelNode* node, m_childNodes) {
 
102
            const KFileItem& item = node->item();
 
103
            urls.append(cleanupUrl(item.url()));
 
104
            if (item.isDir())
 
105
                static_cast<KDirModelDirNode*>(node)->collectAllChildUrls(urls);
 
106
        }
 
107
    }
 
108
 
90
109
private:
91
110
    int m_childCount:31;
92
111
    bool m_populated:1;
113
132
        delete m_rootNode;
114
133
    }
115
134
 
116
 
    void _k_slotNewItems(const KFileItemList&);
 
135
    void _k_slotNewItems(const KUrl& directoryUrl, const KFileItemList&);
117
136
    void _k_slotDeleteItems(const KFileItemList&);
118
137
    void _k_slotRefreshItems(const QList<QPair<KFileItem, KFileItem> >&);
119
138
    void _k_slotClear();
122
141
        delete m_rootNode;
123
142
        m_rootNode = new KDirModelDirNode(0, KFileItem());
124
143
    }
125
 
    // Find the row number and node for a given url.
126
 
    // This has to drill down from the root node.
127
 
    // Returns (0,0) if there is no node for this url.
128
 
    // If expandAndReturnLastParent is set, then we emit expand for each parent and then return the
129
 
    // last known parent if there is no node for this url (special case for expandToUrl)
130
 
    KDirModelNode* nodeForUrl(const KUrl& url, bool expandAndReturnLastParent = false) const;
 
144
    // Emit expand for each parent and then return the
 
145
    // last known parent if there is no node for this url
 
146
    KDirModelNode* expandAllParentsUntil(const KUrl& url) const;
 
147
 
 
148
    // Return the node for a given url, using the hash.
 
149
    KDirModelNode* nodeForUrl(const KUrl& url) const;
131
150
    KDirModelNode* nodeForIndex(const QModelIndex& index) const;
132
151
    QModelIndex indexForNode(KDirModelNode* node, int rowNumber = -1 /*unknown*/) const;
133
152
    bool isDir(KDirModelNode* node) const {
146
165
            url.setQuery(QString());
147
166
            url.setRef(QString()); // kill ref (#171117)
148
167
        }
 
168
        if (node == m_rootNode) {
 
169
            // For a URL without a path, like "applications:" or "settings://",
 
170
            // we want to resolve here "no path" to "/ assumed".
 
171
            // We don't do it before (e.g. in KDirLister) because we want to
 
172
            // give the ioslave a chance for a redirect (e.g. kio_ftp redirects "no path"
 
173
            // to the user's home dir)
 
174
            if (url.path().isEmpty())
 
175
                url.setPath("/");
 
176
        }
149
177
        return url;
150
178
    }
 
179
    void removeFromNodeHash(KDirModelNode* node, const KUrl& url);
 
180
#ifndef NDEBUG
 
181
    void dump();
 
182
#endif
151
183
 
152
184
    KDirModel* q;
153
185
    KDirLister* m_dirLister;
156
188
    // key = current known parent node (always a KDirModelDirNode but KDirModelNode is more convenient),
157
189
    // value = final url[s] being fetched
158
190
    QMap<KDirModelNode*, KUrl::List> m_urlsBeingFetched;
 
191
    QHash<KUrl, KDirModelNode *> m_nodeHash; // global node hash: url -> node
159
192
};
160
193
 
161
 
// If we want to support arbitrary trees like "home:/ as a child of system:/" then,
162
 
// we need to get the parent KFileItem in _k_slotNewItems, and then we can use a QHash<KFileItem,KDirModelNode*> cache.
163
 
// (well there isn't a parent kfileitem, rather a parent url... hmm, back to square one with hashes-of-urls..)
164
 
// For now we'll assume "child url = parent url + filename"
165
 
KDirModelNode* KDirModelPrivate::nodeForUrl(const KUrl& _url, bool expandAndReturnLastParent) const // O(depth)
166
 
{
167
 
    KUrl url(_url);
168
 
    url.adjustPath(KUrl::RemoveTrailingSlash); // KDirLister does this too, so we remove the slash before comparing with the root node url.
169
 
    url.cleanPath(); // remove double slashes in the path
170
 
    url.setQuery(QString());
171
 
    url.setRef(QString());
 
194
KDirModelNode* KDirModelPrivate::nodeForUrl(const KUrl& _url) const // O(1), well, O(length of url as a string)
 
195
{
 
196
    KUrl url = cleanupUrl(_url);
 
197
    if (url == urlForNode(m_rootNode))
 
198
        return m_rootNode;
 
199
    return m_nodeHash.value(url);
 
200
}
 
201
 
 
202
void KDirModelPrivate::removeFromNodeHash(KDirModelNode* node, const KUrl& url)
 
203
{
 
204
    if (node->item().isDir()) {
 
205
        KUrl::List urls;
 
206
        static_cast<KDirModelDirNode *>(node)->collectAllChildUrls(urls);
 
207
        Q_FOREACH(const KUrl& u, urls) {
 
208
            m_nodeHash.remove(u);
 
209
        }
 
210
    } else {
 
211
        m_nodeHash.remove(cleanupUrl(url));
 
212
    }
 
213
}
 
214
 
 
215
KDirModelNode* KDirModelPrivate::expandAllParentsUntil(const KUrl& _url) const // O(depth)
 
216
{
 
217
    KUrl url = cleanupUrl(_url);
172
218
 
173
219
    //kDebug(7008) << url;
174
220
    KUrl nodeUrl = urlForNode(m_rootNode);
175
 
    // For a URL without a path, like "applications:" or "settings://",
176
 
    // we want to resolve here "no path" to "/ assumed".
177
 
    // We don't do it before (e.g. in KDirLister) because we want to
178
 
    // give the ioslave a chance for a redirect (e.g. kio_ftp redirects "no path"
179
 
    // to the user's home dir)
180
 
    if (nodeUrl.path().isEmpty())
181
 
        nodeUrl.setPath("/");
182
 
 
183
221
    if (url == nodeUrl)
184
222
        return m_rootNode;
185
223
 
210
248
        KDirModelNode* node = dirNode->m_childNodesByName.value(fileName);
211
249
        if (!node) {
212
250
            //kDebug(7008) << "child equal or starting with" << url << "not found";
213
 
            if (expandAndReturnLastParent)
214
 
                return dirNode;
215
 
            else
216
 
                return 0;
 
251
            // return last parent found:
 
252
            return dirNode;
217
253
        }
218
254
 
219
 
        if (expandAndReturnLastParent)
220
 
            emit q->expand(indexForNode(node));
 
255
        emit q->expand(indexForNode(node));
221
256
 
222
257
        nodeUrl = urlForNode(node);
223
258
        nodeUrl.adjustPath(KUrl::RemoveTrailingSlash); // #172508
234
269
    //return 0;
235
270
}
236
271
 
 
272
#ifndef NDEBUG
 
273
void KDirModelPrivate::dump()
 
274
{
 
275
    kDebug() << "Dumping contents of KDirModel" << q << "dirLister url:" << m_dirLister->url();
 
276
    QHashIterator<KUrl, KDirModelNode *> it(m_nodeHash);
 
277
    while (it.hasNext()) {
 
278
        it.next();
 
279
        kDebug() << it.key() << it.value();
 
280
    }
 
281
}
 
282
#endif
 
283
 
237
284
// node -> index. If rowNumber is set (or node is root): O(1). Otherwise: O(n).
238
285
QModelIndex KDirModelPrivate::indexForNode(KDirModelNode* node, int rowNumber) const
239
286
{
252
299
        : m_rootNode;
253
300
}
254
301
 
255
 
// We don't use QHash<KUrl,...> anymore, it's too slow.
256
 
// Idea from George, to make QHash<KUrl,...> fast: - cache hash value into QUrl or KUrl
257
 
// This also helps making operator== fast [which means operator== has to call qHash if cached value isn't there]
258
 
// But it means invalidating the cached hash value when the url is modified,
259
 
// so it can't be done in the current KUrl due to the public inheritance from QUrl.
260
 
 
261
 
 
262
302
/*
263
303
 * This model wraps the data held by KDirLister.
264
304
 *
305
345
    }
306
346
    d->m_dirLister = dirLister;
307
347
    d->m_dirLister->setParent(this);
308
 
    connect( d->m_dirLister, SIGNAL(newItems(KFileItemList)),
309
 
             this, SLOT(_k_slotNewItems(KFileItemList)) );
 
348
    connect( d->m_dirLister, SIGNAL(itemsAdded(KUrl,KFileItemList)),
 
349
             this, SLOT(_k_slotNewItems(KUrl,KFileItemList)) );
310
350
    connect( d->m_dirLister, SIGNAL(itemsDeleted(KFileItemList)),
311
351
             this, SLOT(_k_slotDeleteItems(KFileItemList)) );
312
352
    connect( d->m_dirLister, SIGNAL(refreshItems(QList<QPair<KFileItem, KFileItem> >)),
320
360
    return d->m_dirLister;
321
361
}
322
362
 
323
 
void KDirModelPrivate::_k_slotNewItems(const KFileItemList& items)
 
363
void KDirModelPrivate::_k_slotNewItems(const KUrl& directoryUrl, const KFileItemList& items)
324
364
{
325
 
    // Find parent item - it's the same for all the items
326
 
    // TODO: add parent url to the newItems signal
327
 
    //
328
 
    // This way we can finally support properly trees where the urls are using different protocols.
329
 
    // Well, it's not that simple - nodeForUrl still needs to know where to drill down...
330
 
 
331
 
    KUrl firstItemUrl = items.first().url();
332
 
    firstItemUrl.setQuery(QString());
333
 
    firstItemUrl.setRef(QString());
334
 
    KUrl dir(firstItemUrl);
335
 
    dir.setPath(dir.directory());
336
 
 
337
 
    //kDebug(7008) << "dir=" << dir;
338
 
 
339
 
    KDirModelNode* result = nodeForUrl(dir); // O(depth)
 
365
    //kDebug(7008) << "directoryUrl=" << directoryUrl;
 
366
 
 
367
    KDirModelNode* result = nodeForUrl(directoryUrl); // O(depth)
340
368
    // If the directory containing the items wasn't found, then we have a big problem.
341
369
    // Are you calling KDirLister::openUrl(url,true,false)? Please use expandToUrl() instead.
342
370
    if (!result) {
343
 
        kError(7008) << "First item has URL" << firstItemUrl
344
 
                     << "-> parent directory would be" << dir
 
371
        kError(7008) << "Items emitted in directory" << directoryUrl
345
372
                     << "but that directory isn't in KDirModel!"
346
373
                     << "Root directory:" << urlForNode(m_rootNode);
 
374
#ifndef NDEBUG
 
375
        dump();
 
376
#endif
347
377
        Q_ASSERT(result);
348
378
    }
349
379
    Q_ASSERT(isDir(result));
361
391
    q->beginInsertRows( index, newRowCount - newItemsCount, newRowCount - 1 ); // parent, first, last
362
392
 
363
393
    const KUrl::List urlsBeingFetched = m_urlsBeingFetched.value(dirNode);
364
 
    //kDebug(7008) << "urlsBeingFetched for dir" << dirNode << dir << ":" << urlsBeingFetched;
 
394
    //kDebug(7008) << "urlsBeingFetched for dir" << dirNode << directoryUrl << ":" << urlsBeingFetched;
365
395
 
366
396
    QList<QModelIndex> emitExpandFor;
367
397
 
375
405
        dirNode->m_childNodes.append(node);
376
406
        const KUrl url = it->url();
377
407
        dirNode->m_childNodesByName.insert(url.fileName(), node);
 
408
        m_nodeHash.insert(cleanupUrl(url), node);
378
409
        //kDebug(7008) << url;
379
410
 
380
411
        if (!urlsBeingFetched.isEmpty()) {
415
446
    Q_ASSERT(!item.isNull());
416
447
    KUrl url = item.url();
417
448
    KDirModelNode* node = nodeForUrl(url); // O(depth)
418
 
    if (!node)
 
449
    if (!node) {
 
450
        kWarning(7008) << "No node found for item that was just removed:" << url;
419
451
        return;
 
452
    }
420
453
 
421
454
    KDirModelDirNode* dirNode = node->parent();
422
455
    if (!dirNode)
428
461
    if (items.count() == 1) {
429
462
        const int r = node->rowNumber();
430
463
        q->beginRemoveRows(parentIndex, r, r);
 
464
        removeFromNodeHash(node, url);
431
465
        delete dirNode->m_childNodes.takeAt(r);
432
466
        q->endRemoveRows();
433
467
        Q_ASSERT(dirNode->m_childNodesByName.contains(url.fileName()));
443
477
        if (!node) { // don't lookup the first item twice
444
478
            url = item.url();
445
479
            node = nodeForUrl(url);
 
480
            if (!node) {
 
481
                kWarning(7008) << "No node found for item that was just removed:" << url;
 
482
            }
446
483
            Q_ASSERT(node);
447
484
        }
448
485
        rowNumbers.setBit(node->rowNumber(), 1); // O(n)
449
486
        Q_ASSERT(dirNode->m_childNodesByName.contains(url.fileName()));
450
487
        dirNode->m_childNodesByName.remove(url.fileName());
 
488
        removeFromNodeHash(node, url);
451
489
        node = 0;
452
490
    }
453
491
 
491
529
        if (node != m_rootNode) { // we never set an item in the rootnode, we use m_dirLister->rootItem instead.
492
530
            node->setItem(fit->second);
493
531
 
494
 
            if (oldUrl.fileName() != newUrl.fileName()) {
495
 
                KDirModelDirNode* parentNode = node->parent();
496
 
                Q_ASSERT(parentNode);
497
 
                parentNode->m_childNodesByName.remove(oldUrl.fileName());
498
 
                parentNode->m_childNodesByName.insert(newUrl.fileName(), node);
 
532
            if (oldUrl != newUrl) {
 
533
                if (oldUrl.fileName() != newUrl.fileName()) {
 
534
                    KDirModelDirNode* parentNode = node->parent();
 
535
                    Q_ASSERT(parentNode);
 
536
                    parentNode->m_childNodesByName.remove(oldUrl.fileName());
 
537
                    parentNode->m_childNodesByName.insert(newUrl.fileName(), node);
 
538
                }
 
539
                m_nodeHash.remove(cleanupUrl(oldUrl));
 
540
                m_nodeHash.insert(cleanupUrl(newUrl), node);
499
541
            }
500
542
            if (!topLeft.isValid() || index.row() < topLeft.row()) {
501
543
                topLeft = index;
518
560
    q->beginRemoveRows( QModelIndex(), 0, numRows );
519
561
    q->endRemoveRows();
520
562
 
 
563
    m_nodeHash.clear();
521
564
    //emit layoutAboutToBeChanged();
522
565
    clear();
523
566
    //emit layoutChanged();
681
724
 
682
725
int KDirModel::rowCount( const QModelIndex & parent ) const
683
726
{
684
 
    KDirModelDirNode* parentNode = static_cast<KDirModelDirNode *>(d->nodeForIndex(parent));
 
727
    KDirModelNode* node = d->nodeForIndex(parent);
 
728
    if (!node || !d->isDir(node)) // #176555
 
729
        return 0;
 
730
 
 
731
    KDirModelDirNode* parentNode = static_cast<KDirModelDirNode *>(node);
685
732
    Q_ASSERT(parentNode);
686
733
    const int count = parentNode->m_childNodes.count();
687
734
#if 0
706
753
    return d->indexForNode(parentNode); // O(n)
707
754
}
708
755
 
 
756
static bool lessThan(const KUrl &left, const KUrl &right)
 
757
{
 
758
    return left.url().compare(right.url()) < 0;
 
759
}
 
760
 
 
761
KUrl::List KDirModel::simplifiedUrlList(const KUrl::List &urls)
 
762
{
 
763
    if (!urls.count()) {
 
764
        return urls;
 
765
    }
 
766
 
 
767
    KUrl::List ret(urls);
 
768
    qSort(ret.begin(), ret.end(), lessThan);
 
769
 
 
770
    KUrl::List::iterator it = ret.begin();
 
771
    KUrl url = *it;
 
772
    ++it;
 
773
    while (it != ret.end()) {
 
774
        if (url.isParentOf(*it)) {
 
775
            it = ret.erase(it);
 
776
        } else {
 
777
            url = *it;
 
778
            ++it;
 
779
        }
 
780
    }
 
781
 
 
782
    return ret;
 
783
}
 
784
 
709
785
QStringList KDirModel::mimeTypes( ) const
710
786
{
711
 
    return QStringList() << QLatin1String("text/uri-list")
712
 
                         << QLatin1String( "application/x-kde-cutselection" ) // TODO
713
 
                         << QLatin1String( "text/plain" )
714
 
                         << QLatin1String( "application/x-kde-urilist" );
 
787
    return KUrl::List::mimeDataTypes();
715
788
}
716
789
 
717
790
QMimeData * KDirModel::mimeData( const QModelIndexList & indexes ) const
718
791
{
719
 
    KUrl::List urls;
720
 
    foreach ( const QModelIndex &index, indexes ) {
721
 
        urls << d->nodeForIndex( index )->item().url();
 
792
    KUrl::List urls, mostLocalUrls;
 
793
    foreach (const QModelIndex &index, indexes) {
 
794
        const KFileItem& item = d->nodeForIndex(index)->item();
 
795
        urls << item.url();
 
796
        bool dummy;
 
797
        mostLocalUrls << item.mostLocalUrl(dummy);
722
798
    }
723
799
    QMimeData *data = new QMimeData();
724
 
    urls.populateMimeData( data );
 
800
    const bool different = mostLocalUrls != urls;
 
801
    urls = simplifiedUrlList(urls);
 
802
    if (different) {
 
803
        mostLocalUrls = simplifiedUrlList(mostLocalUrls);
 
804
        urls.populateMimeData(mostLocalUrls, data);
 
805
    } else {
 
806
        urls.populateMimeData(data);
 
807
    }
 
808
 
 
809
    // for compatibility reasons (when dropping or pasting into kde3 applications)
 
810
    QString application_x_qiconlist;
 
811
    const int items = urls.count();
 
812
    for (int i = 0; i < items; i++) {
 
813
        const int offset = i*16;
 
814
        QString tmp("%1$@@$%2$@@$32$@@$32$@@$%3$@@$%4$@@$32$@@$16$@@$no data$@@$");
 
815
        application_x_qiconlist += tmp.arg(offset).arg(offset).arg(offset).arg(offset+40);
 
816
    }
 
817
    data->setData("application/x-qiconlist", application_x_qiconlist.toLatin1());
 
818
 
725
819
    return data;
726
820
}
727
821
 
903
997
 
904
998
void KDirModel::expandToUrl(const KUrl& url)
905
999
{
906
 
    KDirModelNode* result = d->nodeForUrl(url, true /*emit expand for each parent and return last parent*/); // O(depth)
907
 
    kDebug(7008) << url << result;
 
1000
    // emit expand for each parent and return last parent
 
1001
    KDirModelNode* result = d->expandAllParentsUntil(url); // O(depth)
 
1002
    //kDebug(7008) << url << result;
908
1003
 
909
1004
    if (!result) // doesn't seem related to our base url?
910
1005
        return;