/* This file is part of the KDE project Copyright (C) 2000 David Faure 2002 Michael Brade Copyright (C) 2003 Waldo Bastian This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dirtree_module.h" #include "dirtree_item.h" #include #include #include #include #include #include #include "konqsidebariface_p.h" KonqSidebarDirTreeModule::KonqSidebarDirTreeModule( KonqSidebarTree * parentTree , bool showHidden) : KonqSidebarTreeModule( parentTree, showHidden ), m_dirLister(0L), m_topLevelItem(0L) { bool universalMode=false; /* Doesn't work reliable :-( KonqSidebarPlugin * plugin = parentTree->part(); // KonqSidebarPlugin::universalMode() is protected :-| if ( plugin->parent() ) { KonqSidebarIface * ksi = static_cast( plugin->parent()->qt_cast( "KonqSidebarIface" ) ); universalMode = ksi ? ksi->universalMode() : false; } */ KConfig * config = new KConfig( universalMode ? "konqsidebartng_kicker.rc" : "konqsidebartng.rc" ); config->setGroup(""); m_showArchivesAsFolders = config->readBoolEntry("ShowArchivesAsFolders",true); delete config; } KonqSidebarDirTreeModule::~KonqSidebarDirTreeModule() { // KDirLister may still emit canceled while being deleted. if (m_dirLister) { disconnect( m_dirLister, SIGNAL( canceled( const KURL & ) ), this, SLOT( slotListingStopped( const KURL & ) ) ); delete m_dirLister; } } KURL::List KonqSidebarDirTreeModule::selectedUrls() { KURL::List lst; KonqSidebarDirTreeItem *selection = static_cast( m_pTree->selectedItem() ); if( !selection ) { kdError() << "KonqSidebarDirTreeModule::selectedUrls: no selection!" << endl; return lst; } lst.append(selection->fileItem()->url()); return lst; } void KonqSidebarDirTreeModule::addTopLevelItem( KonqSidebarTreeTopLevelItem * item ) { if(m_topLevelItem) // We can handle only one at a time ! kdError() << "KonqSidebarDirTreeModule::addTopLevelItem: Impossible, we can have only one toplevel item !" << endl; KDesktopFile cfg( item->path(), true ); cfg.setDollarExpansion(true); KURL targetURL; targetURL.setPath(item->path()); if ( cfg.hasLinkType() ) { targetURL = cfg.readURL(); // some services might want to make their URL configurable in kcontrol QString configured = cfg.readEntry("X-KDE-ConfiguredURL"); if (!configured.isEmpty()) { QStringList list = QStringList::split(':', configured); KConfig config(list[0]); if (list[1] != "noGroup") config.setGroup(list[1]); QString conf_url = config.readEntry(list[2]); if (!conf_url.isEmpty()) { targetURL = conf_url; } } } else if ( cfg.hasDeviceType() ) { // Determine the mountpoint QString mp = cfg.readEntry("MountPoint"); if ( mp.isEmpty() ) return; targetURL.setPath(mp); } else return; bool bListable = KProtocolInfo::supportsListing( targetURL ); //kdDebug(1201) << targetURL.prettyURL() << " listable : " << bListable << endl; if ( !bListable ) { item->setExpandable( false ); item->setListable( false ); } item->setExternalURL( targetURL ); addSubDir( item ); m_topLevelItem = item; } void KonqSidebarDirTreeModule::openTopLevelItem( KonqSidebarTreeTopLevelItem * item ) { if ( !item->childCount() && item->isListable() ) openSubFolder( item ); } void KonqSidebarDirTreeModule::addSubDir( KonqSidebarTreeItem *item ) { QString id = item->externalURL().url(-1); kdDebug(1201) << this << " KonqSidebarDirTreeModule::addSubDir " << id << endl; m_dictSubDirs.insert(id, item ); KonqSidebarDirTreeItem *ditem = dynamic_cast(item); if (ditem) m_ptrdictSubDirs.insert(ditem->fileItem(), item); } // Remove from dict, taking into account that there maybe // other items with the same key. static void remove(QDict &dict, const QString &key, KonqSidebarTreeItem *item) { QPtrList *otherItems = 0; while(true) { KonqSidebarTreeItem *takeItem = dict.take(key); if (!takeItem || (takeItem == item)) { if (!otherItems) return; // Insert the otherItems back in for(KonqSidebarTreeItem *otherItem; (otherItem = otherItems->take(0));) { dict.insert(key, otherItem); } delete otherItems; return; } // Not the item we are looking for if (!otherItems) otherItems = new QPtrList(); otherItems->prepend(takeItem); } } // Looks up key in dict and returns it in item, if there are multiple items // with the same key, additional items are returned in itemList which should // be deleted by the caller. static void lookupItems(QDict &dict, const QString &key, KonqSidebarTreeItem *&item, QPtrList *&itemList) { itemList = 0; item = dict.take(key); if (!item) return; while(true) { KonqSidebarTreeItem *takeItem = dict.take(key); if (!takeItem) { // // Insert itemList back in if (itemList) { for(KonqSidebarTreeItem *otherItem = itemList->first(); otherItem; otherItem = itemList->next()) dict.insert(key, otherItem); } dict.insert(key, item); return; } if (!itemList) itemList = new QPtrList(); itemList->prepend(takeItem); } } // Remove from dict, taking into account that there maybe // other items with the same key. static void remove(QPtrDict &dict, void *key, KonqSidebarTreeItem *item) { QPtrList *otherItems = 0; while(true) { KonqSidebarTreeItem *takeItem = dict.take(key); if (!takeItem || (takeItem == item)) { if (!otherItems) return; // Insert the otherItems back in for(KonqSidebarTreeItem *otherItem; (otherItem = otherItems->take(0));) { dict.insert(key, otherItem); } delete otherItems; return; } // Not the item we are looking for if (!otherItems) otherItems = new QPtrList(); otherItems->prepend(takeItem); } } // Looks up key in dict and returns it in item, if there are multiple items // with the same key, additional items are returned in itemList which should // be deleted by the caller. static void lookupItems(QPtrDict &dict, void *key, KonqSidebarTreeItem *&item, QPtrList *&itemList) { itemList = 0; item = dict.take(key); if (!item) return; while(true) { KonqSidebarTreeItem *takeItem = dict.take(key); if (!takeItem) { // // Insert itemList back in if (itemList) { for(KonqSidebarTreeItem *otherItem = itemList->first(); otherItem; otherItem = itemList->next()) dict.insert(key, otherItem); } dict.insert(key, item); return; } if (!itemList) itemList = new QPtrList(); itemList->prepend(takeItem); } } void KonqSidebarDirTreeModule::removeSubDir( KonqSidebarTreeItem *item, bool childrenOnly ) { kdDebug(1201) << this << " KonqSidebarDirTreeModule::removeSubDir item=" << item << endl; if ( item->firstChild() ) { KonqSidebarTreeItem * it = static_cast(item->firstChild()); KonqSidebarTreeItem * next = 0L; while ( it ) { next = static_cast(it->nextSibling()); removeSubDir( it ); delete it; it = next; } } if ( !childrenOnly ) { QString id = item->externalURL().url(-1); remove(m_dictSubDirs, id, item); while (!(item->alias.isEmpty())) { remove(m_dictSubDirs, item->alias.front(), item); item->alias.pop_front(); } KonqSidebarDirTreeItem *ditem = dynamic_cast(item); if (ditem) remove(m_ptrdictSubDirs, ditem->fileItem(), item); } } void KonqSidebarDirTreeModule::openSubFolder( KonqSidebarTreeItem *item ) { kdDebug(1201) << this << " openSubFolder( " << item->externalURL().prettyURL() << " )" << endl; if ( !m_dirLister ) // created on demand { m_dirLister = new KDirLister( true ); //m_dirLister->setDirOnlyMode( true ); // QStringList mimetypes; // mimetypes<setMimeFilter(mimetypes); connect( m_dirLister, SIGNAL( newItems( const KFileItemList & ) ), this, SLOT( slotNewItems( const KFileItemList & ) ) ); connect( m_dirLister, SIGNAL( refreshItems( const KFileItemList & ) ), this, SLOT( slotRefreshItems( const KFileItemList & ) ) ); connect( m_dirLister, SIGNAL( deleteItem( KFileItem * ) ), this, SLOT( slotDeleteItem( KFileItem * ) ) ); connect( m_dirLister, SIGNAL( completed( const KURL & ) ), this, SLOT( slotListingStopped( const KURL & ) ) ); connect( m_dirLister, SIGNAL( canceled( const KURL & ) ), this, SLOT( slotListingStopped( const KURL & ) ) ); connect( m_dirLister, SIGNAL( redirection( const KURL &, const KURL & ) ), this, SLOT( slotRedirection( const KURL &, const KURL & ) ) ); } if ( !item->isTopLevelItem() && static_cast(item)->hasStandardIcon() ) { int size = KGlobal::iconLoader()->currentSize( KIcon::Small ); QPixmap pix = DesktopIcon( "folder_open", size ); m_pTree->startAnimation( item, "kde", 6, &pix ); } else m_pTree->startAnimation( item ); listDirectory( item ); } void KonqSidebarDirTreeModule::listDirectory( KonqSidebarTreeItem *item ) { // This causes a reparsing, but gets rid of the trailing slash QString strUrl = item->externalURL().url(-1); KURL url( strUrl ); QPtrList *itemList; KonqSidebarTreeItem * openItem; lookupItems(m_dictSubDirs, strUrl, openItem, itemList); while(openItem) { if (openItem->childCount()) break; openItem = itemList ? itemList->take(0) : 0; } delete itemList; if (openItem) { // We have this directory listed already, just copy the entries as we // can't use the dirlister, it would invalidate the old entries int size = KGlobal::iconLoader()->currentSize( KIcon::Small ); KonqSidebarTreeItem * parentItem = item; KonqSidebarDirTreeItem *oldItem = static_cast (openItem->firstChild()); while(oldItem) { KFileItem * fileItem = oldItem->fileItem(); if (! fileItem->isDir() ) { KMimeType::Ptr ptr; if ( fileItem->url().isLocalFile() && (((ptr=fileItem->determineMimeType())!=0) && (ptr->is("inode/directory") || m_showArchivesAsFolders) && ((!ptr->property("X-KDE-LocalProtocol").toString().isEmpty()) ))) { kdDebug()<<"Something not really a directory"<url().prettyURL() << " is not a directory!" << endl; continue; } } KonqSidebarDirTreeItem *dirTreeItem = new KonqSidebarDirTreeItem( parentItem, m_topLevelItem, fileItem ); dirTreeItem->setPixmap( 0, fileItem->pixmap( size ) ); dirTreeItem->setText( 0, KIO::decodeFileName( fileItem->name() ) ); oldItem = static_cast (oldItem->nextSibling()); } m_pTree->stopAnimation( item ); return; } m_dirLister->setShowingDotFiles( showHidden()); if (tree()->isOpeningFirstChild()) m_dirLister->setAutoErrorHandlingEnabled(false,0); else m_dirLister->setAutoErrorHandlingEnabled(true,tree()); m_dirLister->openURL( url, true /*keep*/ ); } void KonqSidebarDirTreeModule::slotNewItems( const KFileItemList& entries ) { kdDebug(1201) << this << " KonqSidebarDirTreeModule::slotNewItems " << entries.count() << endl; Q_ASSERT(entries.count()); KFileItem * firstItem = const_cast(entries).first(); // qlist sucks for constness // Find parent item - it's the same for all the items KURL dir( firstItem->url().url(-1) ); dir.setFileName( "" ); kdDebug(1201) << this << " KonqSidebarDirTreeModule::slotNewItems dir=" << dir.url(-1) << endl; QPtrList *parentItemList; KonqSidebarTreeItem * parentItem; lookupItems(m_dictSubDirs, dir.url(-1), parentItem, parentItemList); if ( !parentItem ) // hack for dnssd://domain/type/service listed in dnssd:/type/ dir { dir.setHost( QString::null ); lookupItems( m_dictSubDirs, dir.url(-1), parentItem, parentItemList ); } if( !parentItem ) { KMessageBox::error( tree(), i18n("Cannot find parent item %1 in the tree. Internal error.").arg( dir.url(-1) ) ); return; } kdDebug()<<"number of additional parent items:"<< (parentItemList?parentItemList->count():0)<currentSize( KIcon::Small ); do { kdDebug()<<"Parent Item URL:"<externalURL()< kit ( entries ); for( ; kit.current(); ++kit ) { KFileItem * fileItem = *kit; if (! fileItem->isDir() ) { KMimeType::Ptr ptr; if ( fileItem->url().isLocalFile() && (( (ptr=fileItem->determineMimeType())!=0) && (ptr->is("inode/directory") || m_showArchivesAsFolders) && ((!ptr->property("X-KDE-LocalProtocol").toString().isEmpty()) ))) { kdDebug()<<"Something really a directory"<url().prettyURL() << " is not a directory!" << endl; continue; } } KonqSidebarDirTreeItem *dirTreeItem = new KonqSidebarDirTreeItem( parentItem, m_topLevelItem, fileItem ); dirTreeItem->setPixmap( 0, fileItem->pixmap( size ) ); dirTreeItem->setText( 0, KIO::decodeFileName( fileItem->name() ) ); } } while ((parentItem = parentItemList ? parentItemList->take(0) : 0)); delete parentItemList; } void KonqSidebarDirTreeModule::slotRefreshItems( const KFileItemList &entries ) { int size = KGlobal::iconLoader()->currentSize( KIcon::Small ); QPtrListIterator kit ( entries ); kdDebug(1201) << "KonqSidebarDirTreeModule::slotRefreshItems " << entries.count() << " entries. First: " << kit.current()->url().url() << endl; for( ; kit.current(); ++kit ) { KFileItem *fileItem = kit.current(); QPtrList *itemList; KonqSidebarTreeItem * item; lookupItems(m_ptrdictSubDirs, fileItem, item, itemList); if (!item) { if ( fileItem->isDir() ) // don't warn for files kdWarning(1201) << "KonqSidebarDirTreeModule::slotRefreshItems can't find old entry for " << kit.current()->url().url(-1) << endl; continue; } do { if ( item->isTopLevelItem() ) // we only have dirs and one toplevel item in the dict { kdWarning(1201) << "KonqSidebarDirTreeModule::slotRefreshItems entry for " << kit.current()->url().url(-1) << " matches against toplevel." << endl; break; } KonqSidebarDirTreeItem * dirTreeItem = static_cast(item); // Item renamed ? if ( dirTreeItem->id != fileItem->url().url( -1 ) ) { // We need to update the URL in m_dictSubDirs, and to get rid of the child items, so remove and add. // Then remove + delete removeSubDir( dirTreeItem, true /*children only*/ ); remove(m_dictSubDirs, dirTreeItem->id, dirTreeItem); dirTreeItem->reset(); // Reset id dirTreeItem->setPixmap( 0, fileItem->pixmap( size ) ); dirTreeItem->setText( 0, KIO::decodeFileName( fileItem->name() ) ); // Make sure the item doesn't get inserted twice! // dirTreeItem->id points to the new name remove(m_dictSubDirs, dirTreeItem->id, dirTreeItem); m_dictSubDirs.insert(dirTreeItem->id, dirTreeItem); } else { dirTreeItem->setPixmap( 0, fileItem->pixmap( size ) ); dirTreeItem->setText( 0, KIO::decodeFileName( fileItem->name() ) ); } } while ((item = itemList ? itemList->take(0) : 0)); delete itemList; } } void KonqSidebarDirTreeModule::slotDeleteItem( KFileItem *fileItem ) { kdDebug(1201) << "KonqSidebarDirTreeModule::slotDeleteItem( " << fileItem->url().url(-1) << " )" << endl; // All items are in m_ptrdictSubDirs, so look it up fast QPtrList *itemList; KonqSidebarTreeItem * item; lookupItems(m_ptrdictSubDirs, fileItem, item, itemList); while(item) { removeSubDir( item ); delete item; item = itemList ? itemList->take(0) : 0; } delete itemList; } void KonqSidebarDirTreeModule::slotRedirection( const KURL & oldUrl, const KURL & newUrl ) { kdDebug(1201) << "******************************KonqSidebarDirTreeModule::slotRedirection(" << newUrl.prettyURL() << ")" << endl; QString oldUrlStr = oldUrl.url(-1); QString newUrlStr = newUrl.url(-1); QPtrList *itemList; KonqSidebarTreeItem * item; lookupItems(m_dictSubDirs, oldUrlStr, item, itemList); if (!item) { kdWarning(1201) << "NOT FOUND oldUrl=" << oldUrlStr << endl; return; } do { if (item->alias.contains(newUrlStr)) continue; kdDebug()<<"Redirectiong element"<alias << newUrlStr; kdDebug(1201) << "Updating url of " << item << " to " << newUrlStr << endl; } while ((item = itemList ? itemList->take(0) : 0)); delete itemList; } void KonqSidebarDirTreeModule::slotListingStopped( const KURL & url ) { kdDebug(1201) << "KonqSidebarDirTree::slotListingStopped " << url.url(-1) << endl; QPtrList *itemList; KonqSidebarTreeItem * item; lookupItems(m_dictSubDirs, url.url(-1), item, itemList); while(item) { if ( item->childCount() == 0 ) { item->setExpandable( false ); item->repaint(); } m_pTree->stopAnimation( item ); item = itemList ? itemList->take(0) : 0; } delete itemList; kdDebug(1201) << "m_selectAfterOpening " << m_selectAfterOpening.prettyURL() << endl; if ( !m_selectAfterOpening.isEmpty() && url.isParentOf( m_selectAfterOpening ) ) { KURL theURL( m_selectAfterOpening ); m_selectAfterOpening = KURL(); followURL( theURL ); } } void KonqSidebarDirTreeModule::followURL( const KURL & url ) { // Check if we already know this URL KonqSidebarTreeItem * item = m_dictSubDirs[ url.url(-1) ]; if (item) // found it -> ensure visible, select, return. { m_pTree->ensureItemVisible( item ); m_pTree->setSelected( item, true ); return; } KURL uParent( url ); KonqSidebarTreeItem * parentItem = 0L; // Go up to the first known parent do { uParent = uParent.upURL(); parentItem = m_dictSubDirs[ uParent.url(-1) ]; } while ( !parentItem && !uParent.path().isEmpty() && uParent.path() != "/" ); // Not found !?! if (!parentItem) { kdDebug() << "No parent found for url " << url.prettyURL() << endl; return; } kdDebug(1202) << "Found parent " << uParent.prettyURL() << endl; // That's the parent directory we found. Open if not open... if ( !parentItem->isOpen() ) { parentItem->setOpen( true ); if ( parentItem->childCount() && m_dictSubDirs[ url.url(-1) ] ) { // Immediate opening, if the dir was already listed followURL( url ); // equivalent to a goto-beginning-of-method } else { m_selectAfterOpening = url; kdDebug(1202) << "KonqSidebarDirTreeModule::followURL: m_selectAfterOpening=" << m_selectAfterOpening.url() << endl; } } } extern "C" { KDE_EXPORT KonqSidebarTreeModule *create_konq_sidebartree_dirtree(KonqSidebarTree* par,const bool showHidden) { return new KonqSidebarDirTreeModule(par,showHidden); } } #include "dirtree_module.moc"