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