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