kio Library API Documentation

kfiletreebranch.cpp

00001 /* This file is part of the KDEproject
00002    Copyright (C) 2000 David Faure <faure@kde.org>
00003                  2000 Carsten Pfeiffer <pfeiffer@kde.org>
00004                  2002 Klaas Freitag <freitag@suse.de>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License version 2 as published by the Free Software Foundation.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018    Boston, MA 02111-1307, USA.
00019 */
00020 
00021 #include <qfile.h>
00022 
00023 #include <kfileitem.h>
00024 #include <kdebug.h>
00025 
00026 #include <sys/types.h>
00027 #include <sys/stat.h>
00028 #include <unistd.h>
00029 
00030 #include "kfiletreeviewitem.h"
00031 #include "kfiletreebranch.h"
00032 
00033 class KFileTreeView;
00034 
00035 
00036 /* --- KFileTreeViewToplevelItem --- */
00037 KFileTreeBranch::KFileTreeBranch( KFileTreeView *parent, const KURL& url,
00038                                   const QString& name,
00039                   const QPixmap& pix, bool showHidden,
00040                   KFileTreeViewItem *branchRoot )
00041 
00042     : KDirLister( false ),
00043       m_root( branchRoot ),
00044       m_startURL( url ),
00045       m_name ( name ),
00046       m_rootIcon( pix ),
00047       m_openRootIcon( pix ),
00048       m_recurseChildren(true),
00049       m_showExtensions(true)
00050 {
00051    kdDebug( 250) << "Creating branch for url " << url.prettyURL() << endl;
00052 
00053    /* if non exists, create one */
00054    if( ! branchRoot )
00055    {
00056       m_root =  new KFileTreeViewItem( parent,
00057                        new KFileItem( url, "inode/directory",
00058                               S_IFDIR  ),
00059                        this );
00060    }
00061 
00062    m_root->setExpandable( true );
00063    m_root->setPixmap( 0, pix );
00064    m_root->setText( 0, name );
00065 
00066    setShowingDotFiles( showHidden );
00067 
00068    connect( this, SIGNAL( newItems(const KFileItemList&)),
00069         this, SLOT  ( addItems( const KFileItemList& )));
00070 
00071    connect( this, SIGNAL( completed(const KURL& )),
00072         this,   SLOT(slCompleted(const KURL&)));
00073 
00074    connect( this, SIGNAL( started( const KURL& )),
00075         this,   SLOT( slotListerStarted( const KURL& )));
00076 
00077    connect( this, SIGNAL( deleteItem( KFileItem* )),
00078         this,   SLOT( slotDeleteItem( KFileItem* )));
00079 
00080    connect( this, SIGNAL( canceled(const KURL&) ),
00081             this,   SLOT( slotCanceled(const KURL&) ));
00082 
00083    connect( this, SIGNAL( clear()),
00084         this, SLOT( slotDirlisterClear()));
00085 
00086    connect( this, SIGNAL( clear(const KURL&)),
00087         this, SLOT( slotDirlisterClearURL(const KURL&)));
00088 
00089    connect( this, SIGNAL( redirection( const KURL& , const KURL& ) ),
00090         this, SLOT( slotRedirect( const KURL&, const KURL& )));
00091 
00092    m_openChildrenURLs.append( url );
00093 }
00094 
00095 void KFileTreeBranch::setOpenPixmap( const QPixmap& pix )
00096 {
00097    m_openRootIcon = pix;
00098 
00099    if( root()->isOpen())
00100    {
00101       root()->setPixmap( 0, pix );
00102    }
00103 }
00104 
00105 void KFileTreeBranch::slotListerStarted( const KURL &url )
00106 {
00107    /* set the parent correct if it is zero. */
00108    kdDebug( 250) << "Starting to list " << url.prettyURL() << endl;
00109 }
00110 
00111 
00112 KFileTreeViewItem *KFileTreeBranch::parentKFTVItem( KFileItem *item )
00113 {
00114    KFileTreeViewItem *parent = 0;
00115 
00116    if( ! item ) return 0;
00117 
00118    /* If it is a directory, check, if it exists in the dict. If not, go one up
00119     * and check again.
00120     */
00121    KURL url = item->url();
00122    // kdDebug(250) << "Item's url is " << url.prettyURL() << endl;
00123    KURL dirUrl( url );
00124     dirUrl.setFileName( QString::null );
00125    // kdDebug(250) << "Directory url is " << dirUrl.prettyURL() << endl;
00126 
00127    parent  = findTVIByURL( dirUrl );
00128    // kdDebug(250) << "Returning as parent item <" << parent <<  ">" << endl;
00129    return( parent );
00130 }
00131 
00132 
00133 void KFileTreeBranch::addItems( const KFileItemList& list )
00134 {
00135    KFileItemListIterator it( list );
00136    kdDebug(250) << "Adding " << list.count() << " items !" << endl;
00137    KFileItem *currItem;
00138    KFileTreeViewItemList treeViewItList;
00139    KFileTreeViewItem *parentItem = 0;
00140 
00141    while ( (currItem = it.current()) != 0 )
00142    {
00143       parentItem = parentKFTVItem( currItem );
00144 
00145       /* Only create a new KFileTreeViewItem if it does not yet exist */
00146       KFileTreeViewItem *newKFTVI =
00147      static_cast<KFileTreeViewItem *>(currItem->extraData( this ));
00148 
00149       if( ! newKFTVI )
00150       {
00151          newKFTVI = createTreeViewItem( parentItem, currItem );
00152          currItem->setExtraData( this, newKFTVI );
00153 
00154 
00155      /* Cut off the file extension in case it is not a directory */
00156      if( !m_showExtensions && !currItem->isDir() )  /* Need to cut the extension */
00157      {
00158         QString n = currItem->text();
00159         int mPoint = n.findRev( '.' );
00160                 if( mPoint > 0 )
00161                     n = n.left( mPoint );
00162         newKFTVI->setText( 0, n );
00163      }
00164       }
00165 
00166       /* Now try to find out if there are children for dirs in the treeview */
00167       /* This stats a directory on the local file system and checks the */
00168       /* hardlink entry in the stat-buf. This works only for local directories. */
00169       if( dirOnlyMode() && !m_recurseChildren && currItem->isLocalFile( ) && currItem->isDir() )
00170       {
00171      KURL url = currItem->url();
00172      QString filename = url.directory( false, true ) + url.filename();
00173      /* do the stat trick of Carsten. The problem is, that the hardlink
00174      *  count only contains directory links. Thus, this method only seem
00175          * to work in dir-only mode */
00176      kdDebug(250) << "Doing stat on " << filename << endl;
00177      struct stat statBuf;
00178      if( stat( QFile::encodeName( filename ), &statBuf ) == 0 )
00179      {
00180         int hardLinks = statBuf.st_nlink;  /* Count of dirs */
00181         kdDebug(250) << "stat succeeded, hardlinks: " << hardLinks << endl;
00182         // If the link count is > 2, the directory likely has subdirs. If it's < 2
00183         // it's something weird like a mounted SMB share. In that case we don't know
00184         // if there are subdirs, thus show it as expandable.
00185         if( hardLinks != 2 )
00186         {
00187            newKFTVI->setExpandable(true);
00188         }
00189         else
00190         {
00191            newKFTVI->setExpandable(false);
00192         }
00193         if( hardLinks >= 2 ) // "Normal" directory with subdirs
00194         {
00195            kdDebug(250) << "Emitting for " << url.prettyURL() << endl;
00196            emit( directoryChildCount( newKFTVI, hardLinks-2)); // parentItem, hardLinks-1 ));
00197         }
00198      }
00199      else
00200      {
00201         kdDebug(250) << "stat of " << filename << " failed !" << endl;
00202      }
00203       }
00204       ++it;
00205 
00206       treeViewItList.append( newKFTVI );
00207    }
00208 
00209    emit( newTreeViewItems( this, treeViewItList ));
00210 }
00211 
00212 KFileTreeViewItem* KFileTreeBranch::createTreeViewItem( KFileTreeViewItem *parent,
00213                             KFileItem *fileItem )
00214 {
00215    KFileTreeViewItem  *tvi = 0;
00216 
00217    if( parent && fileItem )
00218    {
00219       tvi = new KFileTreeViewItem( parent,
00220                    fileItem,
00221                    this );
00222    }
00223    else
00224    {
00225       kdDebug(250) << "createTreeViewItem: Have no parent" << endl;
00226    }
00227    return( tvi );
00228 }
00229 
00230 void KFileTreeBranch::setChildRecurse( bool t )
00231 {
00232    m_recurseChildren = t;
00233    if( t == false )
00234       m_openChildrenURLs.clear();
00235 }
00236 
00237 
00238 void KFileTreeBranch::setShowExtensions( bool visible )
00239 {
00240    m_showExtensions = visible;
00241 }
00242 
00243 bool KFileTreeBranch::showExtensions( ) const
00244 {
00245    return( m_showExtensions );
00246 }
00247 
00248 /*
00249  * The signal that tells that a directory was deleted may arrive before the signal
00250  * for its children arrive. Thus, we must walk through the children of a dir and
00251  * remove them before removing the dir itself.
00252  */
00253 void KFileTreeBranch::slotDeleteItem( KFileItem *it )
00254 {
00255 
00256    if( !it ) return;
00257    kdDebug(250) << "Slot Delete Item hitted for " << it->url().prettyURL() << endl;
00258 
00259    KFileTreeViewItem *kfti = static_cast<KFileTreeViewItem*>(it->extraData(this));
00260 
00261    if( kfti )
00262    {
00263       kdDebug( 250 ) << "Child count: " << kfti->childCount() << endl;
00264       if( kfti->childCount() > 0 )
00265       {
00266      KFileTreeViewItem *child = static_cast<KFileTreeViewItem*>(kfti->firstChild());
00267 
00268      while( child )
00269      {
00270         kdDebug(250) << "Calling child to be deleted !" << endl;
00271         KFileTreeViewItem *nextChild = static_cast<KFileTreeViewItem*>(child->nextSibling());
00272         slotDeleteItem( child->fileItem());
00273         child = nextChild;
00274      }
00275       }
00276 
00277       kdDebug(250) << "Found corresponding KFileTreeViewItem" << endl;
00278       delete( kfti );
00279    }
00280    else
00281    {
00282       kdDebug(250) << "Error: kfiletreeviewitem: "<< kfti << endl;
00283    }
00284 }
00285 
00286 
00287 void KFileTreeBranch::slotCanceled( const KURL& url )
00288 {
00289     // ### anything else to do?
00290    // remove the url from the childrento-recurse-list
00291    m_openChildrenURLs.remove( url);
00292 
00293    // stop animations etc.
00294    emit populateFinished( findTVIByURL(url));
00295 }
00296 
00297 void KFileTreeBranch::slotDirlisterClear()
00298 {
00299    kdDebug(250)<< "*** Clear all !" << endl;
00300    /* this slots needs to clear all listed items, but NOT the root item */
00301    if( ! m_root ) return;
00302 
00303    QListViewItem *child = m_root->firstChild();
00304    QListViewItem *next = child;
00305 
00306    while( child )
00307    {
00308       next = child->nextSibling();
00309       delete child;
00310       child  = next;
00311    }
00312 }
00313 
00314 void KFileTreeBranch::slotRedirect( const KURL& oldUrl, const KURL&newUrl )
00315 {
00316     if( oldUrl.equals( m_startURL, true ))
00317    {
00318       m_startURL = newUrl;
00319    }
00320 
00321 }
00322 
00323 void KFileTreeBranch::slotDirlisterClearURL( const KURL& url )
00324 {
00325    kdDebug(250)<< "*** Clear for URL !" << url.prettyURL() << endl;
00326    KFileItem *item = find( url );
00327    if( item )
00328    {
00329       KFileTreeViewItem *ftvi =
00330      static_cast<KFileTreeViewItem *>(item->extraData( this ));
00331       delete ftvi;
00332    }
00333 }
00334 
00335 
00336 KFileTreeViewItem* KFileTreeBranch::findTVIByURL( const KURL& url )
00337 {
00338    KFileTreeViewItem *resultItem = 0;
00339 
00340     if( m_startURL.equals(url, true) )
00341    {
00342       kdDebug(250) << "findByURL: Returning root as a parent !" << endl;
00343       resultItem = m_root;
00344    }
00345     else if( m_lastFoundURL.equals( url, true ))
00346    {
00347       kdDebug(250) << "findByURL: Returning from lastFoundURL!" << endl;
00348       resultItem = m_lastFoundItem;
00349    }
00350    else
00351    {
00352         kdDebug(250) << "findByURL: searching by dirlister: " << url.url() << endl;
00353 
00354       KFileItem *it = findByURL( url );
00355 
00356       if( it )
00357       {
00358      resultItem = static_cast<KFileTreeViewItem*>(it->extraData(this));
00359      m_lastFoundItem = resultItem;
00360      m_lastFoundURL = url;
00361       }
00362    }
00363 
00364    return( resultItem );
00365 }
00366 
00367 
00368 void KFileTreeBranch::slCompleted( const KURL& url )
00369 {
00370    kdDebug(250) << "SlotCompleted hit for " << url.prettyURL() << endl;
00371    KFileTreeViewItem *currParent = findTVIByURL( url );
00372    if( ! currParent ) return;
00373 
00374    kdDebug(250) << "current parent " << currParent << " is already listed: "
00375         << currParent->alreadyListed() << endl;
00376 
00377    emit( populateFinished(currParent));
00378    emit( directoryChildCount(currParent, currParent->childCount()));
00379 
00380    /* This is a walk through the children of the last populated directory.
00381     * Here we start the dirlister on every child of the dir and wait for its
00382     * finish. When it has finished, we go to the next child.
00383     * This must be done for non local file systems in dirOnly- and Full-Mode
00384     * and for local file systems only in full mode, because the stat trick
00385     * (see addItem-Method) does only work for dirs, not for files in the directory.
00386     */
00387    /* Set bit that the parent dir was listed completely */
00388    currParent->setListed(true);
00389 
00390    kdDebug(250) << "recurseChildren: " << m_recurseChildren << endl;
00391    kdDebug(250) << "isLocalFile: " << m_startURL.isLocalFile() << endl;
00392    kdDebug(250) << "dirOnlyMode: " << dirOnlyMode() << endl;
00393 
00394 
00395    if( m_recurseChildren && (!m_startURL.isLocalFile() || ! dirOnlyMode()) )
00396    {
00397       bool wantRecurseUrl = false;
00398       /* look if the url is in the list for url to recurse */
00399       for ( KURL::List::Iterator it = m_openChildrenURLs.begin();
00400         it != m_openChildrenURLs.end(); ++it )
00401       {
00402      /* it is only interesting that the url _is_in_ the list. */
00403             if( (*it).equals( url, true ) )
00404         wantRecurseUrl = true;
00405       }
00406 
00407       KFileTreeViewItem    *nextChild = 0;
00408       kdDebug(250) << "Recursing " << url.prettyURL() << "? " << wantRecurseUrl << endl;
00409 
00410       if( wantRecurseUrl && currParent )
00411       {
00412     
00413      /* now walk again through the tree and populate the children to get +-signs */
00414      /* This is the starting point. The visible folder has finished,
00415             processing the children has not yet started */
00416      nextChild = static_cast<KFileTreeViewItem*>
00417         (static_cast<QListViewItem*>(currParent)->firstChild());
00418 
00419      if( ! nextChild )
00420      {
00421         /* This happens if there is no child at all */
00422         kdDebug( 250 ) << "No children to recuse" << endl;
00423      }
00424 
00425      /* Since we have listed the children to recurse, we can remove the entry
00426       * in the list of the URLs to see the children.
00427       */
00428      m_openChildrenURLs.remove(url);
00429       }
00430 
00431       if( nextChild ) /* This implies that idx > -1 */
00432       {
00433      /* Next child is defined. We start a dirlister job on every child item
00434       * which is a directory to find out how much children are in the child
00435       * of the last opened dir
00436       */
00437 
00438      /* Skip non directory entries */
00439      while( nextChild )
00440      {
00441         if( nextChild->isDir() && ! nextChild->alreadyListed())
00442         {
00443            KFileItem *kfi = nextChild->fileItem();
00444            if( kfi && kfi->isReadable())
00445            {
00446           KURL recurseUrl = kfi->url();
00447           kdDebug(250) << "Starting to recurse NOW " << recurseUrl.prettyURL() << endl;
00448           openURL( recurseUrl, true );
00449            }
00450         }
00451         nextChild = static_cast<KFileTreeViewItem*>(static_cast<QListViewItem*>(nextChild->nextSibling()));
00452         // kdDebug(250) << "Next child " << m_nextChild << endl;
00453      }
00454       }
00455    }
00456    else
00457    {
00458       kdDebug(250) << "skipping to recurse in complete-slot" << endl;
00459    }
00460 }
00461 
00462 /* This slot is called when a treeviewitem is expanded in the gui */
00463 bool KFileTreeBranch::populate( const KURL& url,  KFileTreeViewItem *currItem )
00464 {
00465    bool ret = false;
00466    if( ! currItem )
00467       return ret;
00468 
00469    kdDebug(250) << "Populating <" << url.prettyURL() << ">" << endl;
00470 
00471    /* Add this url to the list of urls to recurse for children */
00472    if( m_recurseChildren )
00473    {
00474       m_openChildrenURLs.append( url );
00475       kdDebug(250) << "Appending to list " << url.prettyURL() << endl;
00476    }
00477 
00478    if( ! currItem->alreadyListed() )
00479    {
00480       /* start the lister */
00481       ret = openURL( url, true );
00482    }
00483    else
00484    {
00485       kdDebug(250) << "Children already existing in treeview!" << endl;
00486       slCompleted( url );
00487       ret = true;
00488    }
00489    return ret;
00490 }
00491 
00492 void KFileTreeBranch::virtual_hook( int id, void* data )
00493 { KDirLister::virtual_hook( id, data ); }
00494 
00495 #include "kfiletreebranch.moc"
00496 
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.0.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Oct 8 12:21:30 2003 by doxygen 1.2.18 written by Dimitri van Heesch, © 1997-2001