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