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