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