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 Sat Nov 27 13:45:27 2004 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003