1 /* This file is part of the KDE project
2     SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
3     SPDX-FileCopyrightText: 2002 Michael Brade <brade@kde.org>
4     SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
5 
6     SPDX-License-Identifier: LGPL-2.0-only
7 */
8 
9 #include "dirtree_module.h"
10 #include "dirtree_item.h"
11 
12 #include <kconfiggroup.h>
13 #include <kio_version.h>
14 #include <kprotocolmanager.h>
15 #include <kdesktopfile.h>
16 #include <kmessagebox.h>
17 #include <kiconloader.h>
18 #include <kdirlister.h>
19 #include <KLocalizedString>
20 
KonqSidebarDirTreeModule(KonqSidebarTree * parentTree,bool showHidden)21 KonqSidebarDirTreeModule::KonqSidebarDirTreeModule(KonqSidebarTree *parentTree, bool showHidden)
22     : KonqSidebarTreeModule(parentTree, showHidden), m_dirLister(0L), m_topLevelItem(0L)
23 {
24     // SLOW! Get the KConfigGroup from the plugin.
25     KConfig config("konqsidebartngrc");
26     KConfigGroup generalGroup(&config, "General");
27     m_showArchivesAsFolders = generalGroup.readEntry("ShowArchivesAsFolders", true);
28 }
29 
~KonqSidebarDirTreeModule()30 KonqSidebarDirTreeModule::~KonqSidebarDirTreeModule()
31 {
32     // KDirLister may still emit canceled while being deleted.
33     if (m_dirLister) {
34 #if KIO_VERSION < QT_VERSION_CHECK(5, 79, 0)
35         disconnect(m_dirLister, SIGNAL(canceled(QUrl)),
36                    this, SLOT(slotListingStopped(QUrl)));
37 #else
38         disconnect(m_dirLister, &KCoreDirLister::listingDirCanceled, this, &KonqSidebarDirTreeModule::slotListingStopped);
39 #endif
40         delete m_dirLister;
41     }
42 }
43 
selectedUrls()44 QList<QUrl> KonqSidebarDirTreeModule::selectedUrls()
45 {
46     QList<QUrl> lst;
47     KonqSidebarDirTreeItem *selection = static_cast<KonqSidebarDirTreeItem *>(m_pTree->selectedItem());
48     if (!selection) {
49         kError() << "no selection!" << endl;
50         return lst;
51     }
52     lst.append(selection->fileItem().url());
53     return lst;
54 }
55 
addTopLevelItem(KonqSidebarTreeTopLevelItem * item)56 void KonqSidebarDirTreeModule::addTopLevelItem(KonqSidebarTreeTopLevelItem *item)
57 {
58     if (m_topLevelItem) { // We can handle only one at a time !
59         kError() << "Impossible, we can have only one toplevel item !" << endl;
60     }
61 
62     KDesktopFile cfg(item->path());
63     KConfigGroup desktopGroup = cfg.desktopGroup();
64 
65     QUrl targetURL;
66     targetURL.setPath(item->path());
67 
68     if (cfg.hasLinkType()) {
69         targetURL = cfg.readUrl();
70         // some services might want to make their URL configurable in kcontrol
71         QString configured = desktopGroup.readPathEntry("X-KDE-ConfiguredURL", QString());
72         if (!configured.isEmpty()) {
73             QStringList list = configured.split(':');
74             KConfig config(list[0]);
75             KConfigGroup urlGroup(&config, list[1] != "noGroup" ? list[1] : "General");
76             QString conf_url = urlGroup.readEntry(list[2], QString());
77             if (!conf_url.isEmpty()) {
78                 targetURL = conf_url;
79             }
80         }
81     } else if (cfg.hasDeviceType()) {
82         // Determine the mountpoint
83         QString mp = desktopGroup.readPathEntry("MountPoint", QString());
84         if (mp.isEmpty()) {
85             return;
86         }
87 
88         targetURL.setPath(mp);
89     } else {
90         return;
91     }
92 
93     bool bListable = KProtocolManager::supportsListing(targetURL);
94     //qCDebug(SIDEBAR_LOG) << targetURL.toDisplayString() << " listable : " << bListable;
95 
96     if (!bListable) {
97         item->setExpandable(false);
98         item->setListable(false);
99     }
100 
101     item->setExternalURL(targetURL);
102     addSubDir(item);
103 
104     m_topLevelItem = item;
105 }
106 
openTopLevelItem(KonqSidebarTreeTopLevelItem * item)107 void KonqSidebarDirTreeModule::openTopLevelItem(KonqSidebarTreeTopLevelItem *item)
108 {
109     if (!item->childCount() && item->isListable()) {
110         openSubFolder(item);
111     }
112 }
113 
addSubDir(KonqSidebarTreeItem * item)114 void KonqSidebarDirTreeModule::addSubDir(KonqSidebarTreeItem *item)
115 {
116     QString id = item->externalURL().adjusted(QUrl::StripTrailingSlash).toString();
117     qCDebug(SIDEBAR_LOG) << this << id;
118     m_dictSubDirs.insert(id, item);
119 
120     KonqSidebarDirTreeItem *ditem = dynamic_cast<KonqSidebarDirTreeItem *>(item);
121     if (ditem) {
122         m_ptrdictSubDirs.insert(ditem->fileItem(), item);
123     }
124 }
125 
126 // Remove <key, item> from dict, taking into account that there maybe
127 // other items with the same key.
remove(Q3Dict<KonqSidebarTreeItem> & dict,const QString & key,KonqSidebarTreeItem * item)128 static void remove(Q3Dict<KonqSidebarTreeItem> &dict, const QString &key, KonqSidebarTreeItem *item)
129 {
130     Q3PtrList<KonqSidebarTreeItem> *otherItems = 0;
131     while (true) {
132         KonqSidebarTreeItem *takeItem = dict.take(key);
133         if (!takeItem || (takeItem == item)) {
134             if (!otherItems) {
135                 return;
136             }
137 
138             // Insert the otherItems back in
139             for (KonqSidebarTreeItem * otherItem; (otherItem = otherItems->take(0));) {
140                 dict.insert(key, otherItem);
141             }
142             delete otherItems;
143             return;
144         }
145         // Not the item we are looking for
146         if (!otherItems) {
147             otherItems = new Q3PtrList<KonqSidebarTreeItem>();
148         }
149 
150         otherItems->prepend(takeItem);
151     }
152 }
153 
154 // Looks up key in dict and returns it in item, if there are multiple items
155 // with the same key, additional items are returned in itemList which should
156 // be deleted by the caller.
lookupItems(Q3Dict<KonqSidebarTreeItem> & dict,const QString & key,KonqSidebarTreeItem * & item,Q3PtrList<KonqSidebarTreeItem> * & itemList)157 static void lookupItems(Q3Dict<KonqSidebarTreeItem> &dict, const QString &key, KonqSidebarTreeItem *&item, Q3PtrList<KonqSidebarTreeItem> *&itemList)
158 {
159     itemList = 0;
160     item = dict.take(key);
161     if (!item) {
162         return;
163     }
164 
165     while (true) {
166         KonqSidebarTreeItem *takeItem = dict.take(key);
167         if (!takeItem) {
168             //
169             // Insert itemList back in
170             if (itemList) {
171                 for (KonqSidebarTreeItem *otherItem = itemList->first(); otherItem; otherItem = itemList->next()) {
172                     dict.insert(key, otherItem);
173                 }
174             }
175             dict.insert(key, item);
176             return;
177         }
178         if (!itemList) {
179             itemList = new Q3PtrList<KonqSidebarTreeItem>();
180         }
181 
182         itemList->prepend(takeItem);
183     }
184 }
185 
186 // Remove <key, item> from dict, taking into account that there maybe
187 // other items with the same key.
remove(QHash<KFileItem,KonqSidebarTreeItem * > & dict,const KFileItem & key,KonqSidebarTreeItem * item)188 static void remove(QHash<KFileItem, KonqSidebarTreeItem *> &dict, const KFileItem &key, KonqSidebarTreeItem *item)
189 {
190     Q3PtrList<KonqSidebarTreeItem> *otherItems = 0;
191     while (true) {
192         KonqSidebarTreeItem *takeItem = dict.take(key);
193         if (!takeItem || (takeItem == item)) {
194             if (!otherItems) {
195                 return;
196             }
197 
198             // Insert the otherItems back in
199             for (KonqSidebarTreeItem * otherItem; (otherItem = otherItems->take(0));) {
200                 dict.insert(key, otherItem);
201             }
202             delete otherItems;
203             return;
204         }
205         // Not the item we are looking for
206         if (!otherItems) {
207             otherItems = new Q3PtrList<KonqSidebarTreeItem>();
208         }
209 
210         otherItems->prepend(takeItem);
211     }
212 }
213 
214 // Looks up key in dict and returns it in item, if there are multiple items
215 // with the same key, additional items are returned in itemList which should
216 // be deleted by the caller.
lookupItems(QHash<KFileItem,KonqSidebarTreeItem * > & dict,const KFileItem & key,KonqSidebarTreeItem * & item,Q3PtrList<KonqSidebarTreeItem> * & itemList)217 static void lookupItems(QHash<KFileItem, KonqSidebarTreeItem *> &dict, const KFileItem &key, KonqSidebarTreeItem *&item, Q3PtrList<KonqSidebarTreeItem> *&itemList)
218 {
219     itemList = 0;
220     item = dict.take(key);
221     if (!item) {
222         return;
223     }
224 
225     while (true) {
226         KonqSidebarTreeItem *takeItem = dict.take(key);
227         if (!takeItem) {
228             //
229             // Insert itemList back in
230             if (itemList) {
231                 for (KonqSidebarTreeItem *otherItem = itemList->first(); otherItem; otherItem = itemList->next()) {
232                     dict.insert(key, otherItem);
233                 }
234             }
235             dict.insert(key, item);
236             return;
237         }
238         if (!itemList) {
239             itemList = new Q3PtrList<KonqSidebarTreeItem>();
240         }
241 
242         itemList->prepend(takeItem);
243     }
244 }
245 
removeSubDir(KonqSidebarTreeItem * item,bool childrenOnly)246 void KonqSidebarDirTreeModule::removeSubDir(KonqSidebarTreeItem *item, bool childrenOnly)
247 {
248     qCDebug(SIDEBAR_LOG) << this << "item=" << item;
249     if (item->firstChild()) {
250         KonqSidebarTreeItem *it = static_cast<KonqSidebarTreeItem *>(item->firstChild());
251         KonqSidebarTreeItem *next = 0L;
252         while (it) {
253             next = static_cast<KonqSidebarTreeItem *>(it->nextSibling());
254             removeSubDir(it);
255             delete it;
256             it = next;
257         }
258     }
259 
260     if (!childrenOnly) {
261         QString id = item->externalURL().adjusted(QUrl::StripTrailingSlash).toString();
262         remove(m_dictSubDirs, id, item);
263         while (!(item->alias.isEmpty())) {
264             remove(m_dictSubDirs, item->alias.front(), item);
265             item->alias.pop_front();
266         }
267 
268         KonqSidebarDirTreeItem *ditem = dynamic_cast<KonqSidebarDirTreeItem *>(item);
269         if (ditem) {
270             remove(m_ptrdictSubDirs, ditem->fileItem(), item);
271         }
272     }
273 }
274 
openSubFolder(KonqSidebarTreeItem * item)275 void KonqSidebarDirTreeModule::openSubFolder(KonqSidebarTreeItem *item)
276 {
277     qCDebug(SIDEBAR_LOG) << this << "openSubFolder(" << item->externalURL().prettyUrl() << ")";
278 
279     if (!m_dirLister) { // created on demand
280         m_dirLister = new KDirLister();
281         //m_dirLister->setDelayedMimeTypes( true ); // this was set, but it's wrong, without a KMimeTypeResolver...
282         //m_dirLister->setDirOnlyMode( true );
283 //  QStringList mimetypes;
284 //  mimetypes<<QString("inode/directory");
285 //  m_dirLister->setMimeFilter(mimetypes);
286 
287         connect(m_dirLister, SIGNAL(newItems(KFileItemList)),
288                 this, SLOT(slotNewItems(KFileItemList)));
289         connect(m_dirLister, SIGNAL(refreshItems(QList<QPair<KFileItem,KFileItem> >)),
290                 this, SLOT(slotRefreshItems(QList<QPair<KFileItem,KFileItem> >)));
291         connect(m_dirLister, SIGNAL(deleteItem(KFileItem)),
292                 this, SLOT(slotDeleteItem(KFileItem)));
293 
294 #if KIO_VERSION < QT_VERSION_CHECK(5, 79, 0)
295         connect(m_dirLister, SIGNAL(completed(QUrl)),
296                 this, SLOT(slotListingStopped(QUrl)));
297         connect(m_dirLister, SIGNAL(canceled(QUrl)),
298                 this, SLOT(slotListingStopped(QUrl)));
299 #else
300         connect(m_dirLister, &KCoreDirLister::listingDirCompleted, this, &KonqSidebarDirTreeModule::slotListingStopped);
301         connect(m_dirLister, &KCoreDirLister::listingDirCanceled, this, &KonqSidebarDirTreeModule::slotListingStopped);
302 #endif
303 
304         connect(m_dirLister, SIGNAL(redirection(QUrl,QUrl)),
305                 this, SLOT(slotRedirection(QUrl,QUrl)));
306     }
307 
308     if (!item->isTopLevelItem() &&
309             static_cast<KonqSidebarDirTreeItem *>(item)->hasStandardIcon()) {
310         int size = KIconLoader::global()->currentSize(KIconLoader::Small);
311         QPixmap pix = DesktopIcon("folder-open", size);
312         m_pTree->startAnimation(item, "kde", 6, &pix);
313     } else {
314         m_pTree->startAnimation(item);
315     }
316 
317     listDirectory(item);
318 }
319 
listDirectory(KonqSidebarTreeItem * item)320 void KonqSidebarDirTreeModule::listDirectory(KonqSidebarTreeItem *item)
321 {
322     // This causes a reparsing, but gets rid of the trailing slash
323     QString strUrl = item->externalURL().adjusted(QUrl::StripTrailingSlash).toString();
324     QUrl url(strUrl);
325 
326     Q3PtrList<KonqSidebarTreeItem> *itemList;
327     KonqSidebarTreeItem *openItem;
328     lookupItems(m_dictSubDirs, strUrl, openItem, itemList);
329 
330     while (openItem) {
331         if (openItem->childCount()) {
332             break;
333         }
334 
335         openItem = itemList ? itemList->take(0) : 0;
336     }
337     delete itemList;
338 
339     if (openItem) {
340         // We have this directory listed already, just copy the entries as we
341         // can't use the dirlister, it would invalidate the old entries
342         int size = KIconLoader::global()->currentSize(KIconLoader::Small);
343         KonqSidebarTreeItem *parentItem = item;
344         KonqSidebarDirTreeItem *oldItem = static_cast<KonqSidebarDirTreeItem *>(openItem->firstChild());
345         while (oldItem) {
346             const KFileItem fileItem = oldItem->fileItem();
347             if (! fileItem.isDir()) {
348                 if (!fileItem.url().isLocalFile()) {
349                     continue;
350                 }
351                 KMimeType::Ptr ptr = fileItem.determineMimeType();
352                 if (ptr && (ptr->is("inode/directory") || m_showArchivesAsFolders)
353                         && ((!ptr->property("X-KDE-LocalProtocol").toString().isEmpty()))) {
354                     qCDebug(SIDEBAR_LOG) << "Something not really a directory";
355                 } else {
356 //                kError() << "Item " << fileItem->url().prettyUrl() << " is not a directory!" << endl;
357                     continue;
358                 }
359             }
360 
361             KonqSidebarDirTreeItem *dirTreeItem = new KonqSidebarDirTreeItem(parentItem, m_topLevelItem, fileItem);
362             dirTreeItem->setPixmap(0, fileItem.pixmap(size));
363             dirTreeItem->setText(0, KIO::decodeFileName(fileItem.name()));
364 
365             oldItem = static_cast<KonqSidebarDirTreeItem *>(oldItem->nextSibling());
366         }
367         m_pTree->stopAnimation(item);
368 
369         return;
370     }
371 
372     m_dirLister->setShowingDotFiles(showHidden());
373 
374     if (tree()->isOpeningFirstChild()) {
375         m_dirLister->setAutoErrorHandlingEnabled(false, 0);
376     } else {
377         m_dirLister->setAutoErrorHandlingEnabled(true, tree());
378     }
379 
380     m_dirLister->openUrl(url, KDirLister::Keep);
381 }
382 
slotNewItems(const KFileItemList & entries)383 void KonqSidebarDirTreeModule::slotNewItems(const KFileItemList &entries)
384 {
385     qCDebug(SIDEBAR_LOG) << this << entries.count();
386 
387     Q_ASSERT(entries.count());
388     const KFileItem firstItem = entries.first();
389 
390     // Find parent item - it's the same for all the items
391     QUrl dir(firstItem.url().adjusted(QUrl::StripTrailingSlash).toString());
392     dir = dir.adjusted(QUrl::RemoveFilename);
393     dir.setPath(dir.path() + "");
394     qCDebug(SIDEBAR_LOG) << this << "dir=" << dir.adjusted(QUrl::StripTrailingSlash).toString();
395 
396     Q3PtrList<KonqSidebarTreeItem> *parentItemList;
397     KonqSidebarTreeItem *parentItem;
398     lookupItems(m_dictSubDirs, dir.adjusted(QUrl::StripTrailingSlash).toString(), parentItem, parentItemList);
399 
400     if (!parentItem) {   // hack for dnssd://domain/type/service listed in dnssd:/type/ dir
401         dir.setHost(QString());
402         lookupItems(m_dictSubDirs, dir.adjusted(QUrl::StripTrailingSlash).toString(), parentItem, parentItemList);
403     }
404 
405     if (!parentItem) {
406         KMessageBox::error(tree(), i18n("Cannot find parent item %1 in the tree. Internal error.", dir.adjusted(QUrl::StripTrailingSlash).toString()));
407         return;
408     }
409 
410     qCDebug(SIDEBAR_LOG) << "number of additional parent items:" << (parentItemList ? parentItemList->count() : 0);
411     int size = KIconLoader::global()->currentSize(KIconLoader::Small);
412     do {
413         qCDebug(SIDEBAR_LOG) << "Parent Item URL:" << parentItem->externalURL();
414         KFileItemList::const_iterator kit = entries.begin();
415         const KFileItemList::const_iterator kend = entries.end();
416         for (; kit != kend; ++kit) {
417             const KFileItem fileItem = *kit;
418 
419             if (! fileItem.isDir()) {
420                 if (!fileItem.url().isLocalFile()) {
421                     continue;
422                 }
423                 KMimeType::Ptr ptr = fileItem.determineMimeType();
424 
425                 if (ptr && (ptr->is("inode/directory") || m_showArchivesAsFolders)
426                         && ((!ptr->property("X-KDE-LocalProtocol").toString().isEmpty()))) {
427                     qCDebug(SIDEBAR_LOG) << "Something really a directory";
428                 } else {
429                     //kError() << "Item " << fileItem->url().prettyUrl() << " is not a directory!" << endl;
430                     continue;
431                 }
432             }
433 
434             KonqSidebarDirTreeItem *dirTreeItem = new KonqSidebarDirTreeItem(parentItem, m_topLevelItem, fileItem);
435             dirTreeItem->setPixmap(0, fileItem.pixmap(size));
436             dirTreeItem->setText(0, KIO::decodeFileName(fileItem.name()));
437         }
438 
439     } while ((parentItem = parentItemList ? parentItemList->take(0) : 0));
440     delete parentItemList;
441 }
442 
slotRefreshItems(const QList<QPair<KFileItem,KFileItem>> & entries)443 void KonqSidebarDirTreeModule::slotRefreshItems(const QList<QPair<KFileItem, KFileItem> > &entries)
444 {
445     int size = KIconLoader::global()->currentSize(KIconLoader::Small);
446 
447     qCDebug(SIDEBAR_LOG) << "# of items to refresh:" << entries.count();
448 
449     for (int i = 0; i < entries.count(); ++i) {
450         const KFileItem fileItem(entries.at(i).second);
451         const KFileItem oldFileItem(entries.at(i).first);
452 
453         Q3PtrList<KonqSidebarTreeItem> *itemList;
454         KonqSidebarTreeItem *item;
455         lookupItems(m_ptrdictSubDirs, oldFileItem, item, itemList);
456 
457         if (!item) {
458             kWarning(1201) << "can't find old entry for " << oldFileItem.url().adjusted(QUrl::StripTrailingSlash).toString();
459             continue;
460         }
461 
462         do {
463             if (item->isTopLevelItem()) { // we only have dirs and one toplevel item in the dict
464                 kWarning(1201) << "entry for " << oldFileItem.url().adjusted(QUrl::StripTrailingSlash).toString() << "matches against toplevel.";
465                 break;
466             }
467 
468             KonqSidebarDirTreeItem *dirTreeItem = static_cast<KonqSidebarDirTreeItem *>(item);
469             // Item renamed ?
470             if (dirTreeItem->id != fileItem.url().adjusted(QUrl::StripTrailingSlash).toString()) {
471                 qCDebug(SIDEBAR_LOG) << "renaming" << oldFileItem << "->" << fileItem;
472                 // We need to update the URL in m_dictSubDirs, and to get rid of the child items, so remove and add.
473                 // Then remove + delete
474                 removeSubDir(dirTreeItem, true /*children only*/);
475                 remove(m_dictSubDirs, dirTreeItem->id, dirTreeItem);
476                 remove(m_ptrdictSubDirs, oldFileItem, dirTreeItem);
477 
478                 dirTreeItem->reset(); // Reset id
479                 dirTreeItem->setPixmap(0, fileItem.pixmap(size));
480                 dirTreeItem->setText(0, KIO::decodeFileName(fileItem.name()));
481 
482                 // Make sure the item doesn't get inserted twice!
483                 // dirTreeItem->id points to the new name
484                 remove(m_dictSubDirs, dirTreeItem->id, dirTreeItem);
485                 remove(m_ptrdictSubDirs, fileItem, dirTreeItem);
486                 m_dictSubDirs.insert(dirTreeItem->id, dirTreeItem);
487                 m_ptrdictSubDirs.insert(fileItem, dirTreeItem);
488             } else {
489                 dirTreeItem->setPixmap(0, fileItem.pixmap(size));
490                 dirTreeItem->setText(0, KIO::decodeFileName(fileItem.name()));
491             }
492 
493         } while ((item = itemList ? itemList->take(0) : 0));
494         delete itemList;
495     }
496 }
497 
slotDeleteItem(const KFileItem & fileItem)498 void KonqSidebarDirTreeModule::slotDeleteItem(const KFileItem &fileItem)
499 {
500     qCDebug(SIDEBAR_LOG) << fileItem.url().adjusted(QUrl::StripTrailingSlash).toString();
501 
502     // All items are in m_ptrdictSubDirs, so look it up fast
503     Q3PtrList<KonqSidebarTreeItem> *itemList;
504     KonqSidebarTreeItem *item;
505     lookupItems(m_dictSubDirs, fileItem.url().adjusted(QUrl::StripTrailingSlash).toString(), item, itemList);
506     while (item) {
507         removeSubDir(item);
508         delete item;
509 
510         item = itemList ? itemList->take(0) : 0;
511     }
512     delete itemList;
513 }
514 
slotRedirection(const QUrl & oldUrl,const QUrl & newUrl)515 void KonqSidebarDirTreeModule::slotRedirection(const QUrl &oldUrl, const QUrl &newUrl)
516 {
517     qCDebug(SIDEBAR_LOG) << newUrl;
518 
519     QString oldUrlStr = oldUrl.adjusted(QUrl::StripTrailingSlash).toString();
520     QString newUrlStr = newUrl.adjusted(QUrl::StripTrailingSlash).toString();
521 
522     Q3PtrList<KonqSidebarTreeItem> *itemList;
523     KonqSidebarTreeItem *item;
524     lookupItems(m_dictSubDirs, oldUrlStr, item, itemList);
525 
526     if (!item) {
527         kWarning(1201) << "NOT FOUND   oldUrl=" << oldUrlStr;
528         return;
529     }
530 
531     do {
532         if (item->alias.contains(newUrlStr)) {
533             continue;
534         }
535         qCDebug(SIDEBAR_LOG) << "Redirectiong element";
536         // We need to update the URL in m_dictSubDirs
537         m_dictSubDirs.insert(newUrlStr, item);
538         item->alias << newUrlStr;
539 
540         qCDebug(SIDEBAR_LOG) << "Updating url of " << item << " to " << newUrlStr;
541 
542     } while ((item = itemList ? itemList->take(0) : 0));
543     delete itemList;
544 }
545 
slotListingStopped(const QUrl & url)546 void KonqSidebarDirTreeModule::slotListingStopped(const QUrl &url)
547 {
548     //qCDebug(SIDEBAR_LOG) << url;
549 
550     Q3PtrList<KonqSidebarTreeItem> *itemList;
551     KonqSidebarTreeItem *item;
552     lookupItems(m_dictSubDirs, url.adjusted(QUrl::StripTrailingSlash).toString(), item, itemList);
553 
554     while (item) {
555         if (item->childCount() == 0) {
556             item->setExpandable(false);
557             item->repaint();
558         }
559         m_pTree->stopAnimation(item);
560 
561         item = itemList ? itemList->take(0) : 0;
562     }
563     delete itemList;
564 
565     //qCDebug(SIDEBAR_LOG) << "m_selectAfterOpening " << m_selectAfterOpening.prettyUrl();
566     if (!m_selectAfterOpening.isEmpty() && url.isParentOf(m_selectAfterOpening)) {
567         QUrl theURL(m_selectAfterOpening);
568         m_selectAfterOpening = QUrl();
569         followURL(theURL);
570     }
571 }
572 
followURL(const QUrl & url)573 void KonqSidebarDirTreeModule::followURL(const QUrl &url)
574 {
575     // Check if we already know this URL
576     KonqSidebarTreeItem *item = m_dictSubDirs[ url.adjusted(QUrl::StripTrailingSlash).toString() ];
577     if (item) { // found it  -> ensure visible, select, return.
578         m_pTree->ensureItemVisible(item);
579         m_pTree->setSelected(item, true);
580         return;
581     }
582 
583     QUrl uParent(url);
584     KonqSidebarTreeItem *parentItem = 0L;
585     // Go up to the first known parent
586     do {
587         uParent = uParent.upUrl();
588         parentItem = m_dictSubDirs[ uParent.adjusted(QUrl::StripTrailingSlash).toString() ];
589     } while (!parentItem && !uParent.path().isEmpty() && uParent.path() != "/");
590 
591     // Not found !?!
592     if (!parentItem) {
593         qCDebug(SIDEBAR_LOG) << "No parent found for url " << url.toDisplayString();
594         return;
595     }
596     qCDebug(SIDEBAR_LOG) << "Found parent " << uParent.toDisplayString();
597 
598     // That's the parent directory we found. Open if not open...
599     if (!parentItem->isOpen()) {
600         parentItem->setOpen(true);
601         if (parentItem->childCount() && m_dictSubDirs[ url.adjusted(QUrl::StripTrailingSlash).toString() ]) {
602             // Immediate opening, if the dir was already listed
603             followURL(url);   // equivalent to a goto-beginning-of-method
604         } else {
605             m_selectAfterOpening = url;
606             //qCDebug(SIDEBAR_LOG) << "m_selectAfterOpening=" << m_selectAfterOpening.url();
607         }
608     }
609 }
610 
611 extern "C"
612 {
create_konq_sidebartree_dirtree(KonqSidebarTree * par,const bool showHidden)613     KDE_EXPORT KonqSidebarTreeModule *create_konq_sidebartree_dirtree(KonqSidebarTree *par, const bool showHidden)
614     {
615         return new KonqSidebarDirTreeModule(par, showHidden);
616     }
617 }
618 
619