summaryrefslogtreecommitdiffstats
path: root/kio/kfile/kfiletreeview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kio/kfile/kfiletreeview.cpp')
-rw-r--r--kio/kfile/kfiletreeview.cpp677
1 files changed, 677 insertions, 0 deletions
diff --git a/kio/kfile/kfiletreeview.cpp b/kio/kfile/kfiletreeview.cpp
new file mode 100644
index 000000000..542e80d5b
--- /dev/null
+++ b/kio/kfile/kfiletreeview.cpp
@@ -0,0 +1,677 @@
+/* This file is part of the KDEproject
+ Copyright (C) 2000 David Faure <[email protected]>
+ 2000 Carsten Pfeiffer <[email protected]>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <qapplication.h>
+#include <qheader.h>
+#include <qtimer.h>
+#include <kdebug.h>
+#include <kdirnotify_stub.h>
+#include <kglobalsettings.h>
+#include <kfileitem.h>
+#include <kfileview.h>
+#include <kmimetype.h>
+#include <kstandarddirs.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <kio/job.h>
+#include <kio/global.h>
+#include <kurldrag.h>
+#include <kiconloader.h>
+
+
+#include "kfiletreeview.h"
+#include "kfiletreebranch.h"
+#include "kfiletreeviewitem.h"
+
+KFileTreeView::KFileTreeView( QWidget *parent, const char *name )
+ : KListView( parent, name ),
+ m_wantOpenFolderPixmaps( true ),
+ m_toolTip( this )
+{
+ setDragEnabled(true);
+ setSelectionModeExt( KListView::Single );
+
+ m_animationTimer = new QTimer( this );
+ connect( m_animationTimer, SIGNAL( timeout() ),
+ this, SLOT( slotAnimation() ) );
+
+ m_currentBeforeDropItem = 0;
+ m_dropItem = 0;
+
+ m_autoOpenTimer = new QTimer( this );
+ connect( m_autoOpenTimer, SIGNAL( timeout() ),
+ this, SLOT( slotAutoOpenFolder() ) );
+
+ /* The executed-Slot only opens a path, while the expanded-Slot populates it */
+ connect( this, SIGNAL( executed( QListViewItem * ) ),
+ this, SLOT( slotExecuted( QListViewItem * ) ) );
+ connect( this, SIGNAL( expanded ( QListViewItem *) ),
+ this, SLOT( slotExpanded( QListViewItem *) ));
+ connect( this, SIGNAL( collapsed( QListViewItem *) ),
+ this, SLOT( slotCollapsed( QListViewItem* )));
+
+
+ /* connections from the konqtree widget */
+ connect( this, SIGNAL( selectionChanged() ),
+ this, SLOT( slotSelectionChanged() ) );
+ connect( this, SIGNAL( onItem( QListViewItem * )),
+ this, SLOT( slotOnItem( QListViewItem * ) ) );
+ connect( this, SIGNAL(itemRenamed(QListViewItem*, const QString &, int)),
+ this, SLOT(slotItemRenamed(QListViewItem*, const QString &, int)));
+
+
+ m_bDrag = false;
+ m_branches.setAutoDelete( true );
+
+ m_openFolderPixmap = DesktopIcon( "folder_open",KIcon::SizeSmall,KIcon::ActiveState );
+}
+
+KFileTreeView::~KFileTreeView()
+{
+ // we must make sure that the KFileTreeViewItems are deleted _before_ the
+ // branches are deleted. Otherwise, the KFileItems would be destroyed
+ // and the KFileTreeViewItems had dangling pointers to them.
+ hide();
+ clear();
+ m_branches.clear(); // finally delete the branches and KFileItems
+}
+
+
+bool KFileTreeView::isValidItem( QListViewItem *item)
+{
+ if (!item)
+ return false;
+ QPtrList<QListViewItem> lst;
+ QListViewItemIterator it( this );
+ while ( it.current() )
+ {
+ if ( it.current() == item )
+ return true;
+ ++it;
+ }
+ return false;
+}
+
+void KFileTreeView::contentsDragEnterEvent( QDragEnterEvent *ev )
+{
+ if ( ! acceptDrag( ev ) )
+ {
+ ev->ignore();
+ return;
+ }
+ ev->acceptAction();
+ m_currentBeforeDropItem = selectedItem();
+
+ QListViewItem *item = itemAt( contentsToViewport( ev->pos() ) );
+ if( item )
+ {
+ m_dropItem = item;
+ m_autoOpenTimer->start( KFileView::autoOpenDelay() );
+ }
+ else
+ {
+ m_dropItem = 0;
+}
+}
+
+void KFileTreeView::contentsDragMoveEvent( QDragMoveEvent *e )
+{
+ if( ! acceptDrag( e ) )
+ {
+ e->ignore();
+ return;
+ }
+ e->acceptAction();
+
+
+ QListViewItem *afterme;
+ QListViewItem *parent;
+
+ findDrop( e->pos(), parent, afterme );
+
+ // "afterme" is 0 when aiming at a directory itself
+ QListViewItem *item = afterme ? afterme : parent;
+
+ if( item && item->isSelectable() )
+ {
+ setSelected( item, true );
+ if( item != m_dropItem ) {
+ m_autoOpenTimer->stop();
+ m_dropItem = item;
+ m_autoOpenTimer->start( KFileView::autoOpenDelay() );
+ }
+ }
+ else
+ {
+ m_autoOpenTimer->stop();
+ m_dropItem = 0;
+ }
+}
+
+void KFileTreeView::contentsDragLeaveEvent( QDragLeaveEvent * )
+{
+ // Restore the current item to what it was before the dragging (#17070)
+ if ( isValidItem(m_currentBeforeDropItem) )
+ {
+ setSelected( m_currentBeforeDropItem, true );
+ ensureItemVisible( m_currentBeforeDropItem );
+ }
+ else if ( isValidItem(m_dropItem) )
+ setSelected( m_dropItem, false ); // no item selected
+ m_currentBeforeDropItem = 0;
+ m_dropItem = 0;
+
+}
+
+void KFileTreeView::contentsDropEvent( QDropEvent *e )
+{
+
+ m_autoOpenTimer->stop();
+ m_dropItem = 0;
+
+ kdDebug(250) << "contentsDropEvent !" << endl;
+ if( ! acceptDrag( e ) ) {
+ e->ignore();
+ return;
+ }
+
+ e->acceptAction();
+ QListViewItem *afterme;
+ QListViewItem *parent;
+ findDrop(e->pos(), parent, afterme);
+
+ //kdDebug(250) << " parent=" << (parent?parent->text(0):QString::null)
+ // << " afterme=" << (afterme?afterme->text(0):QString::null) << endl;
+
+ if (e->source() == viewport() && itemsMovable())
+ movableDropEvent(parent, afterme);
+ else
+ {
+ emit dropped(e, afterme);
+ emit dropped(this, e, afterme);
+ emit dropped(e, parent, afterme);
+ emit dropped(this, e, parent, afterme);
+
+ KURL::List urls;
+ KURLDrag::decode( e, urls );
+ emit dropped( this, e, urls );
+
+ KURL parentURL;
+ if( parent )
+ parentURL = static_cast<KFileTreeViewItem*>(parent)->url();
+ else
+ // can happen when dropping above the root item
+ // Should we choose the first branch in such a case ??
+ return;
+
+ emit dropped( urls, parentURL );
+ emit dropped( this , e, urls, parentURL );
+ }
+}
+
+bool KFileTreeView::acceptDrag(QDropEvent* e ) const
+{
+
+ bool ancestOK= acceptDrops();
+ // kdDebug(250) << "Do accept drops: " << ancestOK << endl;
+ ancestOK = ancestOK && itemsMovable();
+ // kdDebug(250) << "acceptDrag: " << ancestOK << endl;
+ // kdDebug(250) << "canDecode: " << KURLDrag::canDecode(e) << endl;
+ // kdDebug(250) << "action: " << e->action() << endl;
+
+ /* KListView::acceptDrag(e); */
+ /* this is what KListView does:
+ * acceptDrops() && itemsMovable() && (e->source()==viewport());
+ * ask acceptDrops and itemsMovable, but not the third
+ */
+ return ancestOK && KURLDrag::canDecode( e ) &&
+ // Why this test? All DnDs are one of those AFAIK (DF)
+ ( e->action() == QDropEvent::Copy
+ || e->action() == QDropEvent::Move
+ || e->action() == QDropEvent::Link );
+}
+
+
+
+QDragObject * KFileTreeView::dragObject()
+{
+
+ KURL::List urls;
+ const QPtrList<QListViewItem> fileList = selectedItems();
+ QPtrListIterator<QListViewItem> it( fileList );
+ for ( ; it.current(); ++it )
+ {
+ urls.append( static_cast<KFileTreeViewItem*>(it.current())->url() );
+ }
+ QPoint hotspot;
+ QPixmap pixmap;
+ if( urls.count() > 1 ){
+ pixmap = DesktopIcon( "kmultiple", 16 );
+ }
+ if( pixmap.isNull() )
+ pixmap = currentKFileTreeViewItem()->fileItem()->pixmap( 16 );
+ hotspot.setX( pixmap.width() / 2 );
+ hotspot.setY( pixmap.height() / 2 );
+ QDragObject* dragObject = new KURLDrag( urls, this );
+ if( dragObject )
+ dragObject->setPixmap( pixmap, hotspot );
+ return dragObject;
+}
+
+
+
+void KFileTreeView::slotCollapsed( QListViewItem *item )
+{
+ KFileTreeViewItem *kftvi = static_cast<KFileTreeViewItem*>(item);
+ kdDebug(250) << "hit slotCollapsed" << endl;
+ if( kftvi && kftvi->isDir())
+ {
+ item->setPixmap( 0, itemIcon(kftvi));
+ }
+}
+
+void KFileTreeView::slotExpanded( QListViewItem *item )
+{
+ kdDebug(250) << "slotExpanded here !" << endl;
+
+ if( ! item ) return;
+
+ KFileTreeViewItem *it = static_cast<KFileTreeViewItem*>(item);
+ KFileTreeBranch *branch = it->branch();
+
+ /* Start the animation for the branch object */
+ if( it->isDir() && branch && item->childCount() == 0 )
+ {
+ /* check here if the branch really needs to be populated again */
+ kdDebug(250 ) << "starting to open " << it->url().prettyURL() << endl;
+ startAnimation( it );
+ bool branchAnswer = branch->populate( it->url(), it );
+ kdDebug(250) << "Branches answer: " << branchAnswer << endl;
+ if( ! branchAnswer )
+ {
+ kdDebug(250) << "ERR: Could not populate!" << endl;
+ stopAnimation( it );
+ }
+ }
+
+ /* set a pixmap 'open folder' */
+ if( it->isDir() && isOpen( item ) )
+ {
+ kdDebug(250)<< "Setting open Pixmap" << endl;
+ item->setPixmap( 0, itemIcon( it )); // 0, m_openFolderPixmap );
+ }
+}
+
+
+
+void KFileTreeView::slotExecuted( QListViewItem *item )
+{
+ if ( !item )
+ return;
+ /* This opens the dir and causes the Expanded-slot to be called,
+ * which strolls through the children.
+ */
+ if( static_cast<KFileTreeViewItem*>(item)->isDir())
+ {
+ item->setOpen( !item->isOpen() );
+ }
+}
+
+
+void KFileTreeView::slotAutoOpenFolder()
+{
+ m_autoOpenTimer->stop();
+
+ if ( !isValidItem(m_dropItem) || m_dropItem->isOpen() )
+ return;
+
+ m_dropItem->setOpen( true );
+ m_dropItem->repaint();
+}
+
+
+void KFileTreeView::slotSelectionChanged()
+{
+ if ( !m_dropItem ) // don't do this while the dragmove thing
+ {
+ }
+}
+
+
+KFileTreeBranch* KFileTreeView::addBranch( const KURL &path, const QString& name,
+ bool showHidden )
+{
+ const QPixmap& folderPix = KMimeType::mimeType("inode/directory")->pixmap( KIcon::Desktop,KIcon::SizeSmall );
+
+ return addBranch( path, name, folderPix, showHidden);
+}
+
+KFileTreeBranch* KFileTreeView::addBranch( const KURL &path, const QString& name,
+ const QPixmap& pix, bool showHidden )
+{
+ kdDebug(250) << "adding another root " << path.prettyURL() << endl;
+
+ /* Open a new branch */
+ KFileTreeBranch *newBranch = new KFileTreeBranch( this, path, name, pix,
+ showHidden );
+ return addBranch(newBranch);
+}
+
+KFileTreeBranch *KFileTreeView::addBranch(KFileTreeBranch *newBranch)
+{
+ connect( newBranch, SIGNAL(populateFinished( KFileTreeViewItem* )),
+ this, SLOT( slotPopulateFinished( KFileTreeViewItem* )));
+
+ connect( newBranch, SIGNAL( newTreeViewItems( KFileTreeBranch*,
+ const KFileTreeViewItemList& )),
+ this, SLOT( slotNewTreeViewItems( KFileTreeBranch*,
+ const KFileTreeViewItemList& )));
+
+ m_branches.append( newBranch );
+ return( newBranch );
+}
+
+KFileTreeBranch *KFileTreeView::branch( const QString& searchName )
+{
+ KFileTreeBranch *branch = 0;
+ QPtrListIterator<KFileTreeBranch> it( m_branches );
+
+ while ( (branch = it.current()) != 0 ) {
+ ++it;
+ QString bname = branch->name();
+ kdDebug(250) << "This is the branches name: " << bname << endl;
+ if( bname == searchName )
+ {
+ kdDebug(250) << "Found branch " << bname << " and return ptr" << endl;
+ return( branch );
+ }
+ }
+ return ( 0L );
+}
+
+KFileTreeBranchList& KFileTreeView::branches()
+{
+ return( m_branches );
+}
+
+
+bool KFileTreeView::removeBranch( KFileTreeBranch *branch )
+{
+ if(m_branches.contains(branch))
+ {
+ delete (branch->root());
+ m_branches.remove( branch );
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void KFileTreeView::setDirOnlyMode( KFileTreeBranch* branch, bool bom )
+{
+ if( branch )
+ {
+ branch->setDirOnlyMode( bom );
+ }
+}
+
+
+void KFileTreeView::slotPopulateFinished( KFileTreeViewItem *it )
+{
+ if( it && it->isDir())
+ stopAnimation( it );
+}
+
+void KFileTreeView::slotNewTreeViewItems( KFileTreeBranch* branch, const KFileTreeViewItemList& itemList )
+{
+ if( ! branch ) return;
+ kdDebug(250) << "hitting slotNewTreeViewItems" << endl;
+
+ /* Sometimes it happens that new items should become selected, i.e. if the user
+ * creates a new dir, he probably wants it to be selected. This can not be done
+ * right after creating the directory or file, because it takes some time until
+ * the item appears here in the treeview. Thus, the creation code sets the member
+ * m_neUrlToSelect to the required url. If this url appears here, the item becomes
+ * selected and the member nextUrlToSelect will be cleared.
+ */
+ if( ! m_nextUrlToSelect.isEmpty() )
+ {
+ KFileTreeViewItemListIterator it( itemList );
+
+ bool end = false;
+ for( ; !end && it.current(); ++it )
+ {
+ KURL url = (*it)->url();
+
+ if( m_nextUrlToSelect.equals(url, true )) // ignore trailing / on dirs
+ {
+ setCurrentItem( static_cast<QListViewItem*>(*it) );
+ m_nextUrlToSelect = KURL();
+ end = true;
+ }
+ }
+ }
+}
+
+QPixmap KFileTreeView::itemIcon( KFileTreeViewItem *item, int gap ) const
+{
+ QPixmap pix;
+ kdDebug(250) << "Setting icon for column " << gap << endl;
+
+ if( item )
+ {
+ /* Check if it is a branch root */
+ KFileTreeBranch *brnch = item->branch();
+ if( item == brnch->root() )
+ {
+ pix = brnch->pixmap();
+ if( m_wantOpenFolderPixmaps && brnch->root()->isOpen() )
+ {
+ pix = brnch->openPixmap();
+ }
+ }
+ else
+ {
+ // TODO: different modes, user Pixmaps ?
+ pix = item->fileItem()->pixmap( KIcon::SizeSmall ); // , KIcon::DefaultState);
+
+ /* Only if it is a dir and the user wants open dir pixmap and it is open,
+ * change the fileitem's pixmap to the open folder pixmap. */
+ if( item->isDir() && m_wantOpenFolderPixmaps )
+ {
+ if( isOpen( static_cast<QListViewItem*>(item)))
+ pix = m_openFolderPixmap;
+ }
+ }
+ }
+
+ return pix;
+}
+
+
+void KFileTreeView::slotAnimation()
+{
+ MapCurrentOpeningFolders::Iterator it = m_mapCurrentOpeningFolders.begin();
+ MapCurrentOpeningFolders::Iterator end = m_mapCurrentOpeningFolders.end();
+ for (; it != end;)
+ {
+ KFileTreeViewItem *item = it.key();
+ if (!isValidItem(item))
+ {
+ ++it;
+ m_mapCurrentOpeningFolders.remove(item);
+ continue;
+ }
+
+ uint & iconNumber = it.data().iconNumber;
+ QString icon = QString::fromLatin1( it.data().iconBaseName ).append( QString::number( iconNumber ) );
+ // kdDebug(250) << "Loading icon " << icon << endl;
+ item->setPixmap( 0, DesktopIcon( icon,KIcon::SizeSmall,KIcon::ActiveState )); // KFileTreeViewFactory::instance() ) );
+
+ iconNumber++;
+ if ( iconNumber > it.data().iconCount )
+ iconNumber = 1;
+
+ ++it;
+ }
+}
+
+
+void KFileTreeView::startAnimation( KFileTreeViewItem * item, const char * iconBaseName, uint iconCount )
+{
+ /* TODO: allow specific icons */
+ if( ! item )
+ {
+ kdDebug(250) << " startAnimation Got called without valid item !" << endl;
+ return;
+ }
+
+ m_mapCurrentOpeningFolders.insert( item,
+ AnimationInfo( iconBaseName,
+ iconCount,
+ itemIcon(item, 0) ) );
+ if ( !m_animationTimer->isActive() )
+ m_animationTimer->start( 50 );
+}
+
+void KFileTreeView::stopAnimation( KFileTreeViewItem * item )
+{
+ if( ! item ) return;
+
+ kdDebug(250) << "Stoping Animation !" << endl;
+
+ MapCurrentOpeningFolders::Iterator it = m_mapCurrentOpeningFolders.find(item);
+ if ( it != m_mapCurrentOpeningFolders.end() )
+ {
+ if( item->isDir() && isOpen( item) )
+ {
+ kdDebug(250) << "Setting folder open pixmap !" << endl;
+ item->setPixmap( 0, itemIcon( item ));
+ }
+ else
+ {
+ item->setPixmap( 0, it.data().originalPixmap );
+ }
+ m_mapCurrentOpeningFolders.remove( item );
+ }
+ else
+ {
+ if( item )
+ kdDebug(250)<< "StopAnimation - could not find item " << item->url().prettyURL()<< endl;
+ else
+ kdDebug(250)<< "StopAnimation - item is zero !" << endl;
+ }
+ if (m_mapCurrentOpeningFolders.isEmpty())
+ m_animationTimer->stop();
+}
+
+KFileTreeViewItem * KFileTreeView::currentKFileTreeViewItem() const
+{
+ return static_cast<KFileTreeViewItem *>( selectedItem() );
+}
+
+KURL KFileTreeView::currentURL() const
+{
+ KFileTreeViewItem *item = currentKFileTreeViewItem();
+ if ( item )
+ return currentKFileTreeViewItem()->url();
+ else
+ return KURL();
+}
+
+void KFileTreeView::slotOnItem( QListViewItem *item )
+{
+ KFileTreeViewItem *i = static_cast<KFileTreeViewItem *>( item );
+ if( i )
+ {
+ const KURL url = i->url();
+ if ( url.isLocalFile() )
+ emit onItem( url.path() );
+ else
+ emit onItem( url.prettyURL() );
+ }
+}
+
+void KFileTreeView::slotItemRenamed(QListViewItem* item, const QString &name, int col)
+{
+ (void) item;
+ kdDebug(250) << "Do not bother: " << name << col << endl;
+}
+
+KFileTreeViewItem *KFileTreeView::findItem( const QString& branchName, const QString& relUrl )
+{
+ KFileTreeBranch *br = branch( branchName );
+ return( findItem( br, relUrl ));
+}
+
+KFileTreeViewItem *KFileTreeView::findItem( KFileTreeBranch* brnch, const QString& relUrl )
+{
+ KFileTreeViewItem *ret = 0;
+ if( brnch )
+ {
+ KURL url = brnch->rootUrl();
+
+ if( ! relUrl.isEmpty() && QDir::isRelativePath(relUrl) )
+ {
+ QString partUrl( relUrl );
+
+ if( partUrl.endsWith("/"))
+ partUrl.truncate( relUrl.length()-1 );
+
+ url.addPath( partUrl );
+
+ kdDebug(250) << "assembled complete dir string " << url.prettyURL() << endl;
+
+ KFileItem *fi = brnch->findByURL( url );
+ if( fi )
+ {
+ ret = static_cast<KFileTreeViewItem*>( fi->extraData( brnch ));
+ kdDebug(250) << "Found item !" <<ret << endl;
+ }
+ }
+ else
+ {
+ ret = brnch->root();
+ }
+ }
+ return( ret );
+}
+
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+
+
+void KFileTreeViewToolTip::maybeTip( const QPoint & )
+{
+#if 0
+ QListViewItem *item = m_view->itemAt( point );
+ if ( item ) {
+ QString text = static_cast<KFileViewItem*>( item )->toolTipText();
+ if ( !text.isEmpty() )
+ tip ( m_view->itemRect( item ), text );
+ }
+#endif
+}
+
+void KFileTreeView::virtual_hook( int id, void* data )
+{ KListView::virtual_hook( id, data ); }
+
+#include "kfiletreeview.moc"