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