You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdevelop/parts/fileview/filetreewidget.cpp

388 lines
14 KiB

/***************************************************************************
* Copyright (C) 2001-2002 by Bernd Gehrmann *
* bernd@kdevelop.org *
* Copyright (C) 2003 by Mario Scalas (VCS Support) *
* mario.scalas@libero.it *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "filetreewidget.h"
#include <qheader.h>
#include <qpainter.h>
#include <qregexp.h>
#include <qstringlist.h>
#include <qcolor.h>
#include <kdebug.h>
#include <klocale.h>
#include <kpopupmenu.h>
#include <kfileitem.h>
#include <kurl.h>
#include <kaction.h>
#include "kdevcore.h"
#include "kdevproject.h"
#include "kdevpartcontroller.h"
#include "kdevmainwindow.h"
#include "kdevversioncontrol.h"
#include "domutil.h"
#include "urlutil.h"
#include "fileviewpart.h"
#include "fileitemfactory.h"
#include "vcsfiletreewidgetimpl.h"
#include "stdfiletreewidgetimpl.h"
using namespace filetreeview;
///////////////////////////////////////////////////////////////////////////////
// class FileTreeViewItem
///////////////////////////////////////////////////////////////////////////////
#include <kdeversion.h>
///////////////////////////////////////////////////////////////////////////////
// class FileTreeWidget
///////////////////////////////////////////////////////////////////////////////
FileTreeWidget::FileTreeWidget( FileViewPart *part, QWidget *parent, KDevVCSFileInfoProvider *infoProvider )
: KFileTreeView( parent, "filetreewidget" ), m_part( part ), m_rootBranch( 0 )
{
kdDebug(9017) << "Requested FileTree for: " << projectDirectory() << endl;
if (versionControl() && infoProvider)
kdDebug(9017) << "Valid VCS directory: " << versionControl()->isValidDirectory( projectDirectory() ) << endl;
if (infoProvider && versionControl() && versionControl()->isValidDirectory( projectDirectory() ))
m_impl = new VCSFileTreeWidgetImpl( this, infoProvider );
else
m_impl = new StdFileTreeWidgetImpl( this );
//setResizeMode( QListView::LastColumn );
setSorting( 0 );
setAllColumnsShowFocus( true );
setSelectionMode( QListView::Extended ); // Enable multiple items selection by use of Ctrl/Shift
setDragEnabled( false );
// Slot connections
connect( this, SIGNAL(executed(QListViewItem*)), this, SLOT(slotItemExecuted(QListViewItem*)) );
connect( this, SIGNAL(returnPressed(QListViewItem*)), this, SLOT(slotItemExecuted(QListViewItem*)) );
connect( this, SIGNAL(contextMenu(KListView*, QListViewItem*, const QPoint&)),
this, SLOT(slotContextMenu(KListView*, QListViewItem*, const QPoint&)) );
// Intercepts KDevelop core signals and VCS notifications (if available)
connect( m_part->project(), SIGNAL( activeDirectoryChanged( const QString&, const QString& ) ),
this, SLOT( changeActiveDirectory( const QString&, const QString& ) ) );
connect( m_part->project(), SIGNAL( addedFilesToProject( const QStringList & ) ),
this, SLOT( addProjectFiles( const QStringList & ) ) );
connect( m_part->project(), SIGNAL( removedFilesFromProject( const QStringList & ) ),
this, SLOT( removeProjectFiles( const QStringList & ) ) );
// Safeguard against VCS plug-in unloading at run-time
connect( m_impl, SIGNAL(implementationInvalidated()), this, SLOT(slotImplementationInvalidated()) );
// Hide pattern for files
QDomDocument &dom = *m_part->projectDom();
QString defaultHidePattern = "*.o,*.lo,CVS";
QString hidePattern = DomUtil::readEntry( dom, "/kdevfileview/tree/hidepatterns", defaultHidePattern );
m_hidePatterns = QStringList::split( ",", hidePattern );
}
///////////////////////////////////////////////////////////////////////////////
FileTreeWidget::~FileTreeWidget()
{
kdDebug(9017) << "FileTreeWidget::~FileTreeWidget()" << endl;
QDomDocument &dom = *m_part->projectDom();
DomUtil::writeEntry( dom, "/kdevfileview/tree/hidepatterns", hidePatterns() );
// delete m_impl;
}
///////////////////////////////////////////////////////////////////////////////
void FileTreeWidget::openDirectory( const QString& dirName )
{
kdDebug(9017) << "FileTreeWidget::openDirectory(): " + dirName << endl;
// if we're reloading
if (m_rootBranch)
{
disconnect( m_rootBranch, SIGNAL(populateFinished(KFileTreeViewItem*)), this, SLOT(finishPopulate(KFileTreeViewItem*)) );
removeBranch( m_rootBranch );
m_projectFiles.clear();
}
addProjectFiles( m_part->project()->allFiles(), true );
KURL url = KURL::fromPathOrURL( dirName );
const QPixmap& pix = KMimeType::mimeType("inode/directory")->pixmap( KIcon::Small );
// this is a bit odd, but the order of these calls seems to be important
//FileTreeBranch *b = new FileTreeBranch( this, url, url.prettyURL(), pix );
FileTreeBranchItem *b = m_impl->branchItemFactory()->makeBranchItem( this, url, url.prettyURL(), pix );
b->setChildRecurse( false );
m_rootBranch = addBranch( b );
m_rootBranch->setOpen( true );
connect( m_rootBranch, SIGNAL(populateFinished(KFileTreeViewItem*)), this, SLOT(finishPopulate(KFileTreeViewItem*)) );
}
///////////////////////////////////////////////////////////////////////////////
bool FileTreeWidget::shouldBeShown( KFileTreeViewItem* item )
{
FileTreeViewItem * i = static_cast<FileTreeViewItem *>( item );
return ( i->isDir() || ( (m_impl->showNonProjectFiles() || i->isProjectFile() )
&& !matchesHidePattern( i->url().fileName() ) ) ) ;
}
///////////////////////////////////////////////////////////////////////////////
bool FileTreeWidget::matchesHidePattern(const QString &fileName)
{
QStringList::ConstIterator it;
for (it = m_hidePatterns.begin(); it != m_hidePatterns.end(); ++it) {
QRegExp re(*it, true, true);
if (re.search(fileName) == 0 && (uint)re.matchedLength() == fileName.length())
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
void FileTreeWidget::hideOrShow()
{
// This is called the first time the tree branch is expanded
FileTreeViewItem* item = static_cast<FileTreeViewItem*>(firstChild());
if( !item )
return;
// Need to skip the root item (which is the sub-directory)
// i.e. "/home/devmario/src/kdevelop/parts/cvsservice"
item = static_cast<FileTreeViewItem*>( item->firstChild() );
// Now fill the sub-tree
while (item)
{
item->hideOrShow();
item = static_cast<FileTreeViewItem*>(item->nextSibling());
}
}
void FileTreeWidget::finishPopulate(KFileTreeViewItem* item)
{
if( item == firstChild() )
{
changeActiveDirectory( "", m_part->project()->activeDirectory() );
}
}
///////////////////////////////////////////////////////////////////////////////
void FileTreeWidget::slotItemExecuted( QListViewItem* item )
{
if (!item)
return;
KFileTreeViewItem* ftitem = static_cast<KFileTreeViewItem*>(item);
if (ftitem->isDir())
return;
m_part->partController()->editDocument( ftitem->url() );
}
///////////////////////////////////////////////////////////////////////////////
void FileTreeWidget::slotContextMenu( KListView *, QListViewItem* item, const QPoint &p )
{
kdDebug(9017) << "FileTreeWidget::slotContextMenu(...)" << endl;
KPopupMenu popup( i18n("File Tree"), this );
// If an item is selected, fill the file context with selected files' list
if (item)
{
m_impl->fillPopupMenu( &popup, item );
FileContext context( m_impl->selectedPathUrls() );
m_part->core()->fillContextMenu( &popup, &context );
}
popup.exec( p );
}
///////////////////////////////////////////////////////////////////////////////
QString FileTreeWidget::projectDirectory()
{
return m_part->project()->projectDirectory();
}
///////////////////////////////////////////////////////////////////////////////
/**
* @brief Test whether given file (or a directory) is part of this project.
*
* @param fileName or directory to test for presence.
*/
bool FileTreeWidget::isInProject(const QString &fileName) const
{
return m_projectFiles.contains(fileName);
}
///////////////////////////////////////////////////////////////////////////////
/**
* @brief Add a bunch of files to this project.
*
* Whenever we load a project or user chooses to add a bunch of files using UI,
* we end in this method.
* We merge the list of files already in the project (if any) with the incoming set.
*
* @param fileList
* @param constructing
*
* @see m_projectFiles
*/
void FileTreeWidget::addProjectFiles( QStringList const & fileList, bool constructing )
{
kdDebug(9017) << "files added to project: " << fileList << endl;
QStringList::ConstIterator it;
for ( it = fileList.begin(); it != fileList.end(); ++it )
{
if( (*it).isEmpty() )
continue;
kdDebug(9017) << "adding file: " << *it << endl;
const QString file = projectDirectory() + "/" + ( *it );
if ( !m_projectFiles.contains( file ) )
{
// We got a new file to add to this project.
// Ensure all the parent directories are part of the project set, too.
QStringList paths = QStringList::split( "/", *it);
paths.pop_back();
while( !paths.isEmpty() )
{
// We are adding the directories from longest (the one containing our file),
// to the shortest, measured from root directory of our project.
// Whenever we find out that a directory is already recorded as part of our project,
// we may stop adding, because its parent directories were already added -
// in some previous addition.
QString joinedPaths = paths.join("/");
if( m_projectFiles.contains( joinedPaths ) )
break;
m_projectFiles.insert( projectDirectory() + "/" + joinedPaths, true );
paths.pop_back();
}
m_projectFiles.insert( file, false );
// kdDebug(9017) << "file added: " << file << endl;
}
if ( !constructing )
{
FileTreeViewItem* item = static_cast<FileTreeViewItem*>(firstChild());
if( item )
{
item->setProjectFile( file, true );
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
void FileTreeWidget::removeProjectFiles( QStringList const & fileList )
{
kdDebug(9017) << "files removed from project: " << fileList.count() << endl;
QStringList::ConstIterator it;
for ( it = fileList.begin(); it != fileList.end(); ++it )
{
QString file = m_part->project()->projectDirectory() + "/" + ( *it );
m_projectFiles.remove( file );
kdDebug(9017) << "file removed: " << file << endl;
FileTreeViewItem* item = static_cast<FileTreeViewItem*>(firstChild());
if( item )
{
item->setProjectFile( file, false );
}
}
}
void FileTreeWidget::changeActiveDirectory( const QString& olddir, const QString& newdir )
{
FileTreeViewItem* item = static_cast<FileTreeViewItem*>(firstChild());
if( item )
{
item->changeActiveDir( projectDirectory() + "/" + olddir, projectDirectory() + "/" + newdir );
}
}
///////////////////////////////////////////////////////////////////////////////
void FileTreeWidget::applyHidePatterns( const QString &hidePatterns )
{
m_hidePatterns = QStringList::split( ",", hidePatterns );
hideOrShow();
}
///////////////////////////////////////////////////////////////////////////////
QString FileTreeWidget::hidePatterns() const
{
return m_hidePatterns.join( "," );
}
///////////////////////////////////////////////////////////////////////////////
KDevVersionControl *FileTreeWidget::versionControl() const
{
if (part() && part()->versionControl())
return part()->versionControl();
else
return 0;
}
///////////////////////////////////////////////////////////////////////////////
bool FileTreeWidget::showNonProjectFiles() const
{
return m_impl->showNonProjectFiles();
}
///////////////////////////////////////////////////////////////////////////////
void FileTreeWidget::slotImplementationInvalidated()
{
kdDebug(9017) << "FileTreeWidget::slotImplementationInvalidated()" << endl;
// Destroy old implementation, create the simpler default impl. and
// reload list view
// remove old branch
removeBranch( m_rootBranch );
m_rootBranch = 0; // avoid openDirectory() trying to release the branch
// Restore a clean situation for an eventual new & different implementation
/** \FIXME this for-loop should really go in ~FileTreeViewWidgetImpl() but
* it crashes there: here it works :-/
*/
for (int i=columns()-1; i>=0; --i)
{
kdDebug(9017) << "Removing column: " << i << endl;
removeColumn( i );
}
delete m_impl;
m_impl = new StdFileTreeWidgetImpl( this );
openDirectory( projectDirectory() );
}
#include "filetreewidget.moc"