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