kio Library API Documentation

kfiletreeview.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 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License version 2 as published by the Free Software Foundation. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00017 Boston, MA 02111-1307, USA. 00018 */ 00019 00020 #include <qapplication.h> 00021 #include <qheader.h> 00022 #include <qtimer.h> 00023 #include <kdebug.h> 00024 #include <kdirnotify_stub.h> 00025 #include <kglobalsettings.h> 00026 #include <kfileitem.h> 00027 #include <kfileview.h> 00028 #include <kmimetype.h> 00029 #include <kstandarddirs.h> 00030 #include <stdlib.h> 00031 #include <assert.h> 00032 #include <kio/job.h> 00033 #include <kio/global.h> 00034 #include <kurldrag.h> 00035 #include <kiconloader.h> 00036 00037 00038 #include "kfiletreeview.h" 00039 #include "kfiletreebranch.h" 00040 #include "kfiletreeviewitem.h" 00041 00042 KFileTreeView::KFileTreeView( QWidget *parent, const char *name ) 00043 : KListView( parent, name ), 00044 m_wantOpenFolderPixmaps( true ), 00045 m_toolTip( this ) 00046 { 00047 setSelectionModeExt( KListView::Single ); 00048 00049 m_animationTimer = new QTimer( this ); 00050 connect( m_animationTimer, SIGNAL( timeout() ), 00051 this, SLOT( slotAnimation() ) ); 00052 00053 m_currentBeforeDropItem = 0; 00054 m_dropItem = 0; 00055 00056 m_autoOpenTimer = new QTimer( this ); 00057 connect( m_autoOpenTimer, SIGNAL( timeout() ), 00058 this, SLOT( slotAutoOpenFolder() ) ); 00059 00060 /* The executed-Slot only opens a path, while the expanded-Slot populates it */ 00061 connect( this, SIGNAL( executed( QListViewItem * ) ), 00062 this, SLOT( slotExecuted( QListViewItem * ) ) ); 00063 connect( this, SIGNAL( expanded ( QListViewItem *) ), 00064 this, SLOT( slotExpanded( QListViewItem *) )); 00065 connect( this, SIGNAL( collapsed( QListViewItem *) ), 00066 this, SLOT( slotCollapsed( QListViewItem* ))); 00067 00068 00069 /* connections from the konqtree widget */ 00070 connect( this, SIGNAL( selectionChanged() ), 00071 this, SLOT( slotSelectionChanged() ) ); 00072 connect( this, SIGNAL( onItem( QListViewItem * )), 00073 this, SLOT( slotOnItem( QListViewItem * ) ) ); 00074 connect( this, SIGNAL(itemRenamed(QListViewItem*, const QString &, int)), 00075 this, SLOT(slotItemRenamed(QListViewItem*, const QString &, int))); 00076 00077 00078 m_bDrag = false; 00079 m_branches.setAutoDelete( true ); 00080 00081 m_openFolderPixmap = SmallIcon( "folder_open" ); 00082 } 00083 00084 KFileTreeView::~KFileTreeView() 00085 { 00086 // we must make sure that the KFileTreeViewItems are deleted _before_ the 00087 // branches are deleted. Otherwise, the KFileItems would be destroyed 00088 // and the KFileTreeViewItems had dangling pointers to them. 00089 hide(); 00090 clear(); 00091 m_branches.clear(); // finally delete the branches and KFileItems 00092 } 00093 00094 00095 bool KFileTreeView::isValidItem( QListViewItem *item) 00096 { 00097 if (!item) 00098 return false; 00099 QPtrList<QListViewItem> lst; 00100 QListViewItemIterator it( this ); 00101 while ( it.current() ) 00102 { 00103 if ( it.current() == item ) 00104 return true; 00105 ++it; 00106 } 00107 return false; 00108 } 00109 00110 void KFileTreeView::contentsDragEnterEvent( QDragEnterEvent *ev ) 00111 { 00112 if ( ! acceptDrag( ev ) ) 00113 { 00114 ev->ignore(); 00115 return; 00116 } 00117 ev->acceptAction(); 00118 m_currentBeforeDropItem = selectedItem(); 00119 00120 QListViewItem *item = itemAt( contentsToViewport( ev->pos() ) ); 00121 if( item ) 00122 { 00123 m_dropItem = item; 00124 m_autoOpenTimer->start( KFileView::autoOpenDelay() ); 00125 } 00126 else 00127 { 00128 m_dropItem = 0; 00129 } 00130 } 00131 00132 void KFileTreeView::contentsDragMoveEvent( QDragMoveEvent *e ) 00133 { 00134 if( ! acceptDrag( e ) ) 00135 { 00136 e->ignore(); 00137 return; 00138 } 00139 e->acceptAction(); 00140 00141 00142 QListViewItem *afterme; 00143 QListViewItem *parent; 00144 00145 findDrop( e->pos(), parent, afterme ); 00146 00147 // "afterme" is 0 when aiming at a directory itself 00148 QListViewItem *item = afterme ? afterme : parent; 00149 00150 if( item && item->isSelectable() ) 00151 { 00152 setSelected( item, true ); 00153 if( item != m_dropItem ) { 00154 m_autoOpenTimer->stop(); 00155 m_dropItem = item; 00156 m_autoOpenTimer->start( KFileView::autoOpenDelay() ); 00157 } 00158 } 00159 else 00160 { 00161 m_autoOpenTimer->stop(); 00162 m_dropItem = 0; 00163 } 00164 } 00165 00166 void KFileTreeView::contentsDragLeaveEvent( QDragLeaveEvent * ) 00167 { 00168 // Restore the current item to what it was before the dragging (#17070) 00169 if ( isValidItem(m_currentBeforeDropItem) ) 00170 { 00171 setSelected( m_currentBeforeDropItem, true ); 00172 ensureItemVisible( m_currentBeforeDropItem ); 00173 } 00174 else if ( isValidItem(m_dropItem) ) 00175 setSelected( m_dropItem, false ); // no item selected 00176 m_currentBeforeDropItem = 0; 00177 m_dropItem = 0; 00178 00179 } 00180 00181 void KFileTreeView::contentsDropEvent( QDropEvent *e ) 00182 { 00183 00184 m_autoOpenTimer->stop(); 00185 m_dropItem = 0; 00186 00187 kdDebug(250) << "contentsDropEvent !" << endl; 00188 if( ! acceptDrag( e ) ) { 00189 e->ignore(); 00190 return; 00191 } 00192 00193 e->acceptAction(); 00194 QListViewItem *afterme; 00195 QListViewItem *parent; 00196 findDrop(e->pos(), parent, afterme); 00197 00198 //kdDebug(250) << " parent=" << (parent?parent->text(0):QString::null) 00199 // << " afterme=" << (afterme?afterme->text(0):QString::null) << endl; 00200 00201 if (e->source() == viewport() && itemsMovable()) 00202 movableDropEvent(parent, afterme); 00203 else 00204 { 00205 emit dropped(e, afterme); 00206 emit dropped(this, e, afterme); 00207 emit dropped(e, parent, afterme); 00208 emit dropped(this, e, parent, afterme); 00209 00210 KURL::List urls; 00211 KURLDrag::decode( e, urls ); 00212 emit dropped( this, e, urls ); 00213 00214 KURL parentURL; 00215 if( parent ) 00216 parentURL = static_cast<KFileTreeViewItem*>(parent)->url(); 00217 else 00218 // can happen when dropping above the root item 00219 // Should we choose the first branch in such a case ?? 00220 return; 00221 00222 emit dropped( urls, parentURL ); 00223 emit dropped( this , e, urls, parentURL ); 00224 } 00225 } 00226 00227 bool KFileTreeView::acceptDrag(QDropEvent* e ) const 00228 { 00229 00230 bool ancestOK= acceptDrops(); 00231 // kdDebug(250) << "Do accept drops: " << ancestOK << endl; 00232 ancestOK = ancestOK && itemsMovable(); 00233 // kdDebug(250) << "acceptDrag: " << ancestOK << endl; 00234 // kdDebug(250) << "canDecode: " << KURLDrag::canDecode(e) << endl; 00235 // kdDebug(250) << "action: " << e->action() << endl; 00236 00237 /* KListView::acceptDrag(e); */ 00238 /* this is what KListView does: 00239 * acceptDrops() && itemsMovable() && (e->source()==viewport()); 00240 * ask acceptDrops and itemsMovable, but not the third 00241 */ 00242 return ancestOK && KURLDrag::canDecode( e ) && 00243 // Why this test? All DnDs are one of those AFAIK (DF) 00244 ( e->action() == QDropEvent::Copy 00245 || e->action() == QDropEvent::Move 00246 || e->action() == QDropEvent::Link ); 00247 } 00248 00249 00250 00251 QDragObject * KFileTreeView::dragObject() 00252 { 00253 00254 KURL::List urls; 00255 const QPtrList<QListViewItem> fileList = selectedItems(); 00256 QPtrListIterator<QListViewItem> it( fileList ); 00257 for ( ; it.current(); ++it ) 00258 { 00259 urls.append( static_cast<KFileTreeViewItem*>(it.current())->url() ); 00260 } 00261 QPoint hotspot; 00262 QPixmap pixmap; 00263 if( urls.count() > 1 ){ 00264 pixmap = DesktopIcon( "kmultiple", 16 ); 00265 } 00266 if( pixmap.isNull() ) 00267 pixmap = currentKFileTreeViewItem()->fileItem()->pixmap( 16 ); 00268 hotspot.setX( pixmap.width() / 2 ); 00269 hotspot.setY( pixmap.height() / 2 ); 00270 QDragObject* dragObject = new KURLDrag( urls, this ); 00271 if( dragObject ) 00272 dragObject->setPixmap( pixmap, hotspot ); 00273 return dragObject; 00274 } 00275 00276 00277 00278 void KFileTreeView::slotCollapsed( QListViewItem *item ) 00279 { 00280 KFileTreeViewItem *kftvi = static_cast<KFileTreeViewItem*>(item); 00281 kdDebug(250) << "hit slotCollapsed" << endl; 00282 if( kftvi && kftvi->isDir()) 00283 { 00284 item->setPixmap( 0, itemIcon(kftvi)); 00285 } 00286 } 00287 00288 void KFileTreeView::slotExpanded( QListViewItem *item ) 00289 { 00290 kdDebug(250) << "slotExpanded here !" << endl; 00291 00292 if( ! item ) return; 00293 00294 KFileTreeViewItem *it = static_cast<KFileTreeViewItem*>(item); 00295 KFileTreeBranch *branch = it->branch(); 00296 00297 /* Start the animation for the branch object */ 00298 if( it->isDir() && branch && item->childCount() == 0 ) 00299 { 00300 /* check here if the branch really needs to be populated again */ 00301 kdDebug(250 ) << "starting to open " << it->url().prettyURL() << endl; 00302 startAnimation( it ); 00303 bool branchAnswer = branch->populate( it->url(), it ); 00304 kdDebug(250) << "Branches answer: " << branchAnswer << endl; 00305 if( ! branchAnswer ) 00306 { 00307 kdDebug(250) << "ERR: Could not populate!" << endl; 00308 stopAnimation( it ); 00309 } 00310 } 00311 00312 /* set a pixmap 'open folder' */ 00313 if( it->isDir() && isOpen( item ) ) 00314 { 00315 kdDebug(250)<< "Setting open Pixmap" << endl; 00316 item->setPixmap( 0, itemIcon( it )); // 0, m_openFolderPixmap ); 00317 } 00318 } 00319 00320 00321 00322 void KFileTreeView::slotExecuted( QListViewItem *item ) 00323 { 00324 if ( !item ) 00325 return; 00326 /* This opens the dir and causes the Expanded-slot to be called, 00327 * which strolls through the children. 00328 */ 00329 if( static_cast<KFileTreeViewItem*>(item)->isDir()) 00330 { 00331 item->setOpen( !item->isOpen() ); 00332 } 00333 } 00334 00335 00336 void KFileTreeView::slotAutoOpenFolder() 00337 { 00338 m_autoOpenTimer->stop(); 00339 00340 if ( !isValidItem(m_dropItem) || m_dropItem->isOpen() ) 00341 return; 00342 00343 m_dropItem->setOpen( true ); 00344 m_dropItem->repaint(); 00345 } 00346 00347 00348 void KFileTreeView::slotSelectionChanged() 00349 { 00350 if ( !m_dropItem ) // don't do this while the dragmove thing 00351 { 00352 } 00353 } 00354 00355 00356 KFileTreeBranch* KFileTreeView::addBranch( const KURL &path, const QString& name, 00357 bool showHidden ) 00358 { 00359 const QPixmap& folderPix = KMimeType::mimeType("inode/directory")->pixmap( KIcon::Small ); 00360 00361 return addBranch( path, name, folderPix, showHidden); 00362 } 00363 00364 KFileTreeBranch* KFileTreeView::addBranch( const KURL &path, const QString& name, 00365 const QPixmap& pix, bool showHidden ) 00366 { 00367 kdDebug(250) << "adding another root " << path.prettyURL() << endl; 00368 00369 /* Open a new branch */ 00370 KFileTreeBranch *newBranch = new KFileTreeBranch( this, path, name, pix, 00371 showHidden ); 00372 return addBranch(newBranch); 00373 } 00374 00375 KFileTreeBranch *KFileTreeView::addBranch(KFileTreeBranch *newBranch) 00376 { 00377 connect( newBranch, SIGNAL(populateFinished( KFileTreeViewItem* )), 00378 this, SLOT( slotPopulateFinished( KFileTreeViewItem* ))); 00379 00380 connect( newBranch, SIGNAL( newTreeViewItems( KFileTreeBranch*, 00381 const KFileTreeViewItemList& )), 00382 this, SLOT( slotNewTreeViewItems( KFileTreeBranch*, 00383 const KFileTreeViewItemList& ))); 00384 00385 m_branches.append( newBranch ); 00386 return( newBranch ); 00387 } 00388 00389 KFileTreeBranch *KFileTreeView::branch( const QString& searchName ) 00390 { 00391 KFileTreeBranch *branch = 0; 00392 QPtrListIterator<KFileTreeBranch> it( m_branches ); 00393 00394 while ( (branch = it.current()) != 0 ) { 00395 ++it; 00396 QString bname = branch->name(); 00397 kdDebug(250) << "This is the branches name: " << bname << endl; 00398 if( bname == searchName ) 00399 { 00400 kdDebug(250) << "Found branch " << bname << " and return ptr" << endl; 00401 return( branch ); 00402 } 00403 } 00404 return ( 0L ); 00405 } 00406 00407 KFileTreeBranchList& KFileTreeView::branches() 00408 { 00409 return( m_branches ); 00410 } 00411 00412 00413 bool KFileTreeView::removeBranch( KFileTreeBranch *branch ) 00414 { 00415 if(m_branches.contains(branch)) 00416 { 00417 delete (branch->root()); 00418 m_branches.remove( branch ); 00419 return true; 00420 } 00421 else 00422 { 00423 return false; 00424 } 00425 } 00426 00427 void KFileTreeView::setDirOnlyMode( KFileTreeBranch* branch, bool bom ) 00428 { 00429 if( branch ) 00430 { 00431 branch->setDirOnlyMode( bom ); 00432 } 00433 } 00434 00435 00436 void KFileTreeView::slotPopulateFinished( KFileTreeViewItem *it ) 00437 { 00438 if( it && it->isDir()) 00439 stopAnimation( it ); 00440 } 00441 00442 void KFileTreeView::slotNewTreeViewItems( KFileTreeBranch* branch, const KFileTreeViewItemList& itemList ) 00443 { 00444 if( ! branch ) return; 00445 kdDebug(250) << "hitting slotNewTreeViewItems" << endl; 00446 00447 /* Sometimes it happens that new items should become selected, i.e. if the user 00448 * creates a new dir, he probably wants it to be selected. This can not be done 00449 * right after creating the directory or file, because it takes some time until 00450 * the item appears here in the treeview. Thus, the creation code sets the member 00451 * m_neUrlToSelect to the required url. If this url appears here, the item becomes 00452 * selected and the member nextUrlToSelect will be cleared. 00453 */ 00454 if( ! m_nextUrlToSelect.isEmpty() ) 00455 { 00456 KFileTreeViewItemListIterator it( itemList ); 00457 00458 bool end = false; 00459 for( ; !end && it.current(); ++it ) 00460 { 00461 KURL url = (*it)->url(); 00462 00463 if( m_nextUrlToSelect.equals(url, true )) // ignore trailing / on dirs 00464 { 00465 setCurrentItem( static_cast<QListViewItem*>(*it) ); 00466 m_nextUrlToSelect = KURL(); 00467 end = true; 00468 } 00469 } 00470 } 00471 } 00472 00473 QPixmap KFileTreeView::itemIcon( KFileTreeViewItem *item, int gap ) const 00474 { 00475 QPixmap pix; 00476 kdDebug(250) << "Setting icon for column " << gap << endl; 00477 00478 if( item ) 00479 { 00480 /* Check if it is a branch root */ 00481 KFileTreeBranch *brnch = item->branch(); 00482 if( item == brnch->root() ) 00483 { 00484 pix = brnch->pixmap(); 00485 if( m_wantOpenFolderPixmaps && brnch->root()->isOpen() ) 00486 { 00487 pix = brnch->openPixmap(); 00488 } 00489 } 00490 else 00491 { 00492 // TODO: different modes, user Pixmaps ? 00493 pix = item->fileItem()->pixmap( KIcon::SizeSmall ); // , KIcon::DefaultState); 00494 00495 /* Only if it is a dir and the user wants open dir pixmap and it is open, 00496 * change the fileitem's pixmap to the open folder pixmap. */ 00497 if( item->isDir() && m_wantOpenFolderPixmaps ) 00498 { 00499 if( isOpen( static_cast<QListViewItem*>(item))) 00500 pix = m_openFolderPixmap; 00501 } 00502 } 00503 } 00504 00505 return pix; 00506 } 00507 00508 00509 void KFileTreeView::slotAnimation() 00510 { 00511 MapCurrentOpeningFolders::Iterator it = m_mapCurrentOpeningFolders.begin(); 00512 MapCurrentOpeningFolders::Iterator end = m_mapCurrentOpeningFolders.end(); 00513 for (; it != end;) 00514 { 00515 KFileTreeViewItem *item = it.key(); 00516 if (!isValidItem(item)) 00517 { 00518 MapCurrentOpeningFolders::Iterator deleteIt = it; 00519 ++it; 00520 m_mapCurrentOpeningFolders.remove(item); 00521 continue; 00522 } 00523 00524 uint & iconNumber = it.data().iconNumber; 00525 QString icon = QString::fromLatin1( it.data().iconBaseName ).append( QString::number( iconNumber ) ); 00526 // kdDebug(250) << "Loading icon " << icon << endl; 00527 item->setPixmap( 0, SmallIcon( icon )); // KFileTreeViewFactory::instance() ) ); 00528 00529 iconNumber++; 00530 if ( iconNumber > it.data().iconCount ) 00531 iconNumber = 1; 00532 00533 ++it; 00534 } 00535 } 00536 00537 00538 void KFileTreeView::startAnimation( KFileTreeViewItem * item, const char * iconBaseName, uint iconCount ) 00539 { 00540 /* TODO: allow specific icons */ 00541 if( ! item ) 00542 { 00543 kdDebug(250) << " startAnimation Got called without valid item !" << endl; 00544 return; 00545 } 00546 00547 m_mapCurrentOpeningFolders.insert( item, 00548 AnimationInfo( iconBaseName, 00549 iconCount, 00550 itemIcon(item, 0) ) ); 00551 if ( !m_animationTimer->isActive() ) 00552 m_animationTimer->start( 50 ); 00553 } 00554 00555 void KFileTreeView::stopAnimation( KFileTreeViewItem * item ) 00556 { 00557 if( ! item ) return; 00558 00559 kdDebug(250) << "Stoping Animation !" << endl; 00560 00561 MapCurrentOpeningFolders::Iterator it = m_mapCurrentOpeningFolders.find(item); 00562 if ( it != m_mapCurrentOpeningFolders.end() ) 00563 { 00564 if( item->isDir() && isOpen( item) ) 00565 { 00566 kdDebug(250) << "Setting folder open pixmap !" << endl; 00567 item->setPixmap( 0, itemIcon( item )); 00568 } 00569 else 00570 { 00571 item->setPixmap( 0, it.data().originalPixmap ); 00572 } 00573 m_mapCurrentOpeningFolders.remove( item ); 00574 } 00575 else 00576 { 00577 if( item ) 00578 kdDebug(250)<< "StopAnimation - could not find item " << item->url().prettyURL()<< endl; 00579 else 00580 kdDebug(250)<< "StopAnimation - item is zero !" << endl; 00581 } 00582 if (m_mapCurrentOpeningFolders.isEmpty()) 00583 m_animationTimer->stop(); 00584 } 00585 00586 KFileTreeViewItem * KFileTreeView::currentKFileTreeViewItem() const 00587 { 00588 return static_cast<KFileTreeViewItem *>( selectedItem() ); 00589 } 00590 00591 KURL KFileTreeView::currentURL() const 00592 { 00593 KFileTreeViewItem *item = currentKFileTreeViewItem(); 00594 if ( item ) 00595 return currentKFileTreeViewItem()->url(); 00596 else 00597 return KURL(); 00598 } 00599 00600 void KFileTreeView::slotOnItem( QListViewItem *item ) 00601 { 00602 KFileTreeViewItem *i = static_cast<KFileTreeViewItem *>( item ); 00603 if( i ) 00604 { 00605 const KURL url = i->url(); 00606 if ( url.isLocalFile() ) 00607 emit onItem( url.path() ); 00608 else 00609 emit onItem( url.prettyURL() ); 00610 } 00611 } 00612 00613 void KFileTreeView::slotItemRenamed(QListViewItem* item, const QString &name, int col) 00614 { 00615 (void) item; 00616 kdDebug(250) << "Do not bother: " << name << col << endl; 00617 } 00618 00619 KFileTreeViewItem *KFileTreeView::findItem( const QString& branchName, const QString& relUrl ) 00620 { 00621 KFileTreeBranch *br = branch( branchName ); 00622 return( findItem( br, relUrl )); 00623 } 00624 00625 KFileTreeViewItem *KFileTreeView::findItem( KFileTreeBranch* brnch, const QString& relUrl ) 00626 { 00627 KFileTreeViewItem *ret = 0; 00628 if( brnch ) 00629 { 00630 KURL url = brnch->rootUrl(); 00631 00632 if( ! relUrl.isEmpty() && relUrl != QString::fromLatin1("/") ) 00633 { 00634 QString partUrl( relUrl ); 00635 00636 if( partUrl.endsWith("/")) 00637 partUrl.truncate( relUrl.length()-1 ); 00638 00639 url.addPath( partUrl ); 00640 00641 kdDebug(250) << "assembled complete dir string " << url.prettyURL() << endl; 00642 00643 KFileItem *fi = brnch->findByURL( url ); 00644 if( fi ) 00645 { 00646 ret = static_cast<KFileTreeViewItem*>( fi->extraData( brnch )); 00647 kdDebug(250) << "Found item !" <<ret << endl; 00648 } 00649 } 00650 else 00651 { 00652 ret = brnch->root(); 00653 } 00654 } 00655 return( ret ); 00656 } 00657 00660 00661 00662 void KFileTreeViewToolTip::maybeTip( const QPoint & ) 00663 { 00664 #if 0 00665 QListViewItem *item = m_view->itemAt( point ); 00666 if ( item ) { 00667 QString text = static_cast<KFileViewItem*>( item )->toolTipText(); 00668 if ( !text.isEmpty() ) 00669 tip ( m_view->itemRect( item ), text ); 00670 } 00671 #endif 00672 } 00673 00674 void KFileTreeView::virtual_hook( int id, void* data ) 00675 { KListView::virtual_hook( id, data ); } 00676 00677 #include "kfiletreeview.moc"
KDE Logo
This file is part of the documentation for kio Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:41:05 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003