diff options
Diffstat (limited to 'kio/kfile/kfiletreebranch.cpp')
-rw-r--r-- | kio/kfile/kfiletreebranch.cpp | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/kio/kfile/kfiletreebranch.cpp b/kio/kfile/kfiletreebranch.cpp new file mode 100644 index 000000000..b37662f89 --- /dev/null +++ b/kio/kfile/kfiletreebranch.cpp @@ -0,0 +1,528 @@ +/* This file is part of the KDEproject + Copyright (C) 2000 David Faure <faure@kde.org> + 2000 Carsten Pfeiffer <pfeiffer@kde.org> + 2002 Klaas Freitag <freitag@suse.de> + + 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 <qfile.h> + +#include <kfileitem.h> +#include <kdebug.h> +#include <kde_file.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "kfiletreeviewitem.h" +#include "kfiletreebranch.h" + + +/* --- KFileTreeViewToplevelItem --- */ +KFileTreeBranch::KFileTreeBranch( KFileTreeView *parent, const KURL& url, + const QString& name, + const QPixmap& pix, bool showHidden, + KFileTreeViewItem *branchRoot ) + + : KDirLister( false ), + m_root( branchRoot ), + m_startURL( url ), + m_name ( name ), + m_rootIcon( pix ), + m_openRootIcon( pix ), + m_recurseChildren(true), + m_showExtensions(true) +{ + kdDebug( 250) << "Creating branch for url " << url.prettyURL() << endl; + + /* if non exists, create one */ + if( ! branchRoot ) + { + m_root = new KFileTreeViewItem( parent, + new KFileItem( url, "inode/directory", + S_IFDIR ), + this ); + } + + m_root->setExpandable( true ); + m_root->setPixmap( 0, pix ); + m_root->setText( 0, name ); + + setShowingDotFiles( showHidden ); + + connect( this, SIGNAL( refreshItems(const KFileItemList&)), + this, SLOT ( slotRefreshItems( const KFileItemList& ))); + + connect( this, SIGNAL( newItems(const KFileItemList&)), + this, SLOT ( addItems( const KFileItemList& ))); + + connect( this, SIGNAL( completed(const KURL& )), + this, SLOT(slCompleted(const KURL&))); + + connect( this, SIGNAL( started( const KURL& )), + this, SLOT( slotListerStarted( const KURL& ))); + + connect( this, SIGNAL( deleteItem( KFileItem* )), + this, SLOT( slotDeleteItem( KFileItem* ))); + + connect( this, SIGNAL( canceled(const KURL&) ), + this, SLOT( slotCanceled(const KURL&) )); + + connect( this, SIGNAL( clear()), + this, SLOT( slotDirlisterClear())); + + connect( this, SIGNAL( clear(const KURL&)), + this, SLOT( slotDirlisterClearURL(const KURL&))); + + connect( this, SIGNAL( redirection( const KURL& , const KURL& ) ), + this, SLOT( slotRedirect( const KURL&, const KURL& ))); + + m_openChildrenURLs.append( url ); +} + +void KFileTreeBranch::setOpenPixmap( const QPixmap& pix ) +{ + m_openRootIcon = pix; + + if( root()->isOpen()) + { + root()->setPixmap( 0, pix ); + } +} + +void KFileTreeBranch::slotListerStarted( const KURL &url ) +{ + /* set the parent correct if it is zero. */ + kdDebug( 250) << "Starting to list " << url.prettyURL() << endl; +} + + +KFileTreeViewItem *KFileTreeBranch::parentKFTVItem( KFileItem *item ) +{ + KFileTreeViewItem *parent = 0; + + if( ! item ) return 0; + + /* If it is a directory, check, if it exists in the dict. If not, go one up + * and check again. + */ + KURL url = item->url(); + // kdDebug(250) << "Item's url is " << url.prettyURL() << endl; + KURL dirUrl( url ); + dirUrl.setFileName( QString::null ); + // kdDebug(250) << "Directory url is " << dirUrl.prettyURL() << endl; + + parent = findTVIByURL( dirUrl ); + // kdDebug(250) << "Returning as parent item <" << parent << ">" << endl; + return( parent ); +} + + +void KFileTreeBranch::slotRefreshItems( const KFileItemList& list ) +{ + KFileItemListIterator it( list ); + kdDebug(250) << "Refreshing " << list.count() << " items !" << endl; + KFileItem *currItem; + KFileTreeViewItem *item = 0; + + while ( (currItem = it.current()) != 0 ) + { + item = findTVIByURL(currItem->url()); + if (item) { + item->setPixmap(0, item->fileItem()->pixmap( KIcon::SizeSmall )); + item->setText( 0, item->fileItem()->text()); + } + ++it; + } +} + +void KFileTreeBranch::addItems( const KFileItemList& list ) +{ + KFileItemListIterator it( list ); + kdDebug(250) << "Adding " << list.count() << " items !" << endl; + KFileItem *currItem; + KFileTreeViewItemList treeViewItList; + KFileTreeViewItem *parentItem = 0; + + while ( (currItem = it.current()) != 0 ) + { + parentItem = parentKFTVItem( currItem ); + + + /* Only create a new KFileTreeViewItem if it does not yet exist */ + KFileTreeViewItem *newKFTVI = + static_cast<KFileTreeViewItem *>(currItem->extraData( this )); + + if( ! newKFTVI ) + { + newKFTVI = createTreeViewItem( parentItem, currItem ); + if (!newKFTVI) + { + // TODO: Don't fail if parentItem == 0 + ++it; + continue; + } + currItem->setExtraData( this, newKFTVI ); + + /* Cut off the file extension in case it is not a directory */ + if( !m_showExtensions && !currItem->isDir() ) /* Need to cut the extension */ + { + QString name = currItem->text(); + int mPoint = name.findRev( '.' ); + if( mPoint > 0 ) + name = name.left( mPoint ); + newKFTVI->setText( 0, name ); + } + } + + /* Now try to find out if there are children for dirs in the treeview */ + /* This stats a directory on the local file system and checks the */ + /* hardlink entry in the stat-buf. This works only for local directories. */ + if( dirOnlyMode() && !m_recurseChildren && currItem->isLocalFile( ) && currItem->isDir() ) + { + KURL url = currItem->url(); + QString filename = url.directory( false, true ) + url.fileName(); + /* do the stat trick of Carsten. The problem is, that the hardlink + * count only contains directory links. Thus, this method only seem + * to work in dir-only mode */ + kdDebug(250) << "Doing stat on " << filename << endl; + KDE_struct_stat statBuf; + if( KDE_stat( QFile::encodeName( filename ), &statBuf ) == 0 ) + { + int hardLinks = statBuf.st_nlink; /* Count of dirs */ + kdDebug(250) << "stat succeeded, hardlinks: " << hardLinks << endl; + // If the link count is > 2, the directory likely has subdirs. If it's < 2 + // it's something weird like a mounted SMB share. In that case we don't know + // if there are subdirs, thus show it as expandable. + + if( hardLinks != 2 ) + { + newKFTVI->setExpandable(true); + } + else + { + newKFTVI->setExpandable(false); + } + if( hardLinks >= 2 ) // "Normal" directory with subdirs + { + kdDebug(250) << "Emitting for " << url.prettyURL() << endl; + emit( directoryChildCount( newKFTVI, hardLinks-2)); // parentItem, hardLinks-1 )); + } + } + else + { + kdDebug(250) << "stat of " << filename << " failed !" << endl; + } + } + ++it; + + treeViewItList.append( newKFTVI ); + } + + emit newTreeViewItems( this, treeViewItList ); +} + +KFileTreeViewItem* KFileTreeBranch::createTreeViewItem( KFileTreeViewItem *parent, + KFileItem *fileItem ) +{ + KFileTreeViewItem *tvi = 0; + if( parent && fileItem ) + { + tvi = new KFileTreeViewItem( parent, + fileItem, + this ); + } + else + { + kdDebug(250) << "createTreeViewItem: Have no parent" << endl; + } + return( tvi ); +} + +void KFileTreeBranch::setChildRecurse( bool t ) +{ + m_recurseChildren = t; + if( t == false ) + m_openChildrenURLs.clear(); +} + + +void KFileTreeBranch::setShowExtensions( bool visible ) +{ + m_showExtensions = visible; +} + +bool KFileTreeBranch::showExtensions( ) const +{ + return( m_showExtensions ); +} + +/* + * The signal that tells that a directory was deleted may arrive before the signal + * for its children arrive. Thus, we must walk through the children of a dir and + * remove them before removing the dir itself. + */ +void KFileTreeBranch::slotDeleteItem( KFileItem *it ) +{ + if( !it ) return; + kdDebug(250) << "Slot Delete Item hitted for " << it->url().prettyURL() << endl; + + KFileTreeViewItem *kfti = static_cast<KFileTreeViewItem*>(it->extraData(this)); + + if( kfti ) + { + kdDebug( 250 ) << "Child count: " << kfti->childCount() << endl; + if( kfti->childCount() > 0 ) + { + KFileTreeViewItem *child = static_cast<KFileTreeViewItem*>(kfti->firstChild()); + + while( child ) + { + kdDebug(250) << "Calling child to be deleted !" << endl; + KFileTreeViewItem *nextChild = static_cast<KFileTreeViewItem*>(child->nextSibling()); + slotDeleteItem( child->fileItem()); + child = nextChild; + } + } + + kdDebug(250) << "Found corresponding KFileTreeViewItem" << endl; + if( m_lastFoundURL.equals(it->url(), true )) + { + m_lastFoundURL = KURL(); + m_lastFoundItem = 0L; + } + delete( kfti ); + } + else + { + kdDebug(250) << "Error: kfiletreeviewitem: "<< kfti << endl; + } +} + + +void KFileTreeBranch::slotCanceled( const KURL& url ) +{ + // ### anything else to do? + // remove the url from the childrento-recurse-list + m_openChildrenURLs.remove( url); + + // stop animations etc. + KFileTreeViewItem *item = findTVIByURL(url); + if (!item) return; // Uh oh... + emit populateFinished(item); +} + +void KFileTreeBranch::slotDirlisterClear() +{ + kdDebug(250)<< "*** Clear all !" << endl; + /* this slots needs to clear all listed items, but NOT the root item */ + if( m_root ) + deleteChildrenOf( m_root ); +} + +void KFileTreeBranch::slotDirlisterClearURL( const KURL& url ) +{ + kdDebug(250)<< "*** Clear for URL !" << url.prettyURL() << endl; + KFileItem *item = findByURL( url ); + if( item ) + { + KFileTreeViewItem *ftvi = + static_cast<KFileTreeViewItem *>(item->extraData( this )); + deleteChildrenOf( ftvi ); + } +} + +void KFileTreeBranch::deleteChildrenOf( QListViewItem *parent ) +{ + // for some strange reason, slotDirlisterClearURL() sometimes calls us + // with a 0L parent. + if ( !parent ) + return; + + while ( parent->firstChild() ) + delete parent->firstChild(); +} + +void KFileTreeBranch::slotRedirect( const KURL& oldUrl, const KURL&newUrl ) +{ + if( oldUrl.equals( m_startURL, true )) + { + m_startURL = newUrl; + } +} + +KFileTreeViewItem* KFileTreeBranch::findTVIByURL( const KURL& url ) +{ + KFileTreeViewItem *resultItem = 0; + + if( m_startURL.equals(url, true) ) + { + kdDebug(250) << "findByURL: Returning root as a parent !" << endl; + resultItem = m_root; + } + else if( m_lastFoundURL.equals( url, true )) + { + kdDebug(250) << "findByURL: Returning from lastFoundURL!" << endl; + resultItem = m_lastFoundItem; + } + else + { + kdDebug(250) << "findByURL: searching by dirlister: " << url.url() << endl; + + KFileItem *it = findByURL( url ); + + if( it ) + { + resultItem = static_cast<KFileTreeViewItem*>(it->extraData(this)); + m_lastFoundItem = resultItem; + m_lastFoundURL = url; + } + } + + return( resultItem ); +} + + +void KFileTreeBranch::slCompleted( const KURL& url ) +{ + kdDebug(250) << "SlotCompleted hit for " << url.prettyURL() << endl; + KFileTreeViewItem *currParent = findTVIByURL( url ); + if( ! currParent ) return; + + kdDebug(250) << "current parent " << currParent << " is already listed: " + << currParent->alreadyListed() << endl; + + emit( populateFinished(currParent)); + emit( directoryChildCount(currParent, currParent->childCount())); + + /* This is a walk through the children of the last populated directory. + * Here we start the dirlister on every child of the dir and wait for its + * finish. When it has finished, we go to the next child. + * This must be done for non local file systems in dirOnly- and Full-Mode + * and for local file systems only in full mode, because the stat trick + * (see addItem-Method) does only work for dirs, not for files in the directory. + */ + /* Set bit that the parent dir was listed completely */ + currParent->setListed(true); + + kdDebug(250) << "recurseChildren: " << m_recurseChildren << endl; + kdDebug(250) << "isLocalFile: " << m_startURL.isLocalFile() << endl; + kdDebug(250) << "dirOnlyMode: " << dirOnlyMode() << endl; + + + if( m_recurseChildren && (!m_startURL.isLocalFile() || ! dirOnlyMode()) ) + { + bool wantRecurseUrl = false; + /* look if the url is in the list for url to recurse */ + for ( KURL::List::Iterator it = m_openChildrenURLs.begin(); + it != m_openChildrenURLs.end(); ++it ) + { + /* it is only interesting that the url _is_in_ the list. */ + if( (*it).equals( url, true ) ) + wantRecurseUrl = true; + } + + KFileTreeViewItem *nextChild = 0; + kdDebug(250) << "Recursing " << url.prettyURL() << "? " << wantRecurseUrl << endl; + + if( wantRecurseUrl && currParent ) + { + + /* now walk again through the tree and populate the children to get +-signs */ + /* This is the starting point. The visible folder has finished, + processing the children has not yet started */ + nextChild = static_cast<KFileTreeViewItem*> + (static_cast<QListViewItem*>(currParent)->firstChild()); + + if( ! nextChild ) + { + /* This happens if there is no child at all */ + kdDebug( 250 ) << "No children to recuse" << endl; + } + + /* Since we have listed the children to recurse, we can remove the entry + * in the list of the URLs to see the children. + */ + m_openChildrenURLs.remove(url); + } + + if( nextChild ) /* This implies that idx > -1 */ + { + /* Next child is defined. We start a dirlister job on every child item + * which is a directory to find out how much children are in the child + * of the last opened dir + */ + + /* Skip non directory entries */ + while( nextChild ) + { + if( nextChild->isDir() && ! nextChild->alreadyListed()) + { + KFileItem *kfi = nextChild->fileItem(); + if( kfi && kfi->isReadable()) + { + KURL recurseUrl = kfi->url(); + kdDebug(250) << "Starting to recurse NOW " << recurseUrl.prettyURL() << endl; + openURL( recurseUrl, true ); + } + } + nextChild = static_cast<KFileTreeViewItem*>(static_cast<QListViewItem*>(nextChild->nextSibling())); + // kdDebug(250) << "Next child " << m_nextChild << endl; + } + } + } + else + { + kdDebug(250) << "skipping to recurse in complete-slot" << endl; + } +} + +/* This slot is called when a treeviewitem is expanded in the gui */ +bool KFileTreeBranch::populate( const KURL& url, KFileTreeViewItem *currItem ) +{ + bool ret = false; + if( ! currItem ) + return ret; + + kdDebug(250) << "Populating <" << url.prettyURL() << ">" << endl; + + /* Add this url to the list of urls to recurse for children */ + if( m_recurseChildren ) + { + m_openChildrenURLs.append( url ); + kdDebug(250) << "Appending to list " << url.prettyURL() << endl; + } + + if( ! currItem->alreadyListed() ) + { + /* start the lister */ + ret = openURL( url, true ); + } + else + { + kdDebug(250) << "Children already existing in treeview!" << endl; + slCompleted( url ); + ret = true; + } + return ret; +} + +void KFileTreeBranch::virtual_hook( int id, void* data ) +{ KDirLister::virtual_hook( id, data ); } + +#include "kfiletreebranch.moc" + |