/*
 *  Copyright (C) 2003 Roberto Raggi (roberto@kdevelop.org)
 *
 *  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.
 *
 *  This program 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 General Public License
 *  along with this program; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 *
 * Partially based on KDE Studio ClassListView http://www.thekompany.com/projects/kdestudio/
 */

#include "classviewpart.h"
#include "classviewwidget.h"

#include <kiconloader.h>
#include <kinstance.h>
#include <kurl.h>
#include <tdeaction.h>
#include <tdeactionclasses.h>
#include <tdepopupmenu.h>
#include <tdeconfig.h>

#include <urlutil.h>
#include <kdevcore.h>
#include <kdevlanguagesupport.h>
#include <kdevproject.h>
#include <kdevpartcontroller.h>
#include <codemodel.h>
#include <codemodel_utils.h>

#include <tdelocale.h>
#include <kdebug.h>

#include <tqheader.h>
#include <tqdir.h>
#include <tqstylesheet.h>
#include <tqstringlist.h>

// namespace ?!?

ClassViewWidget::ClassViewWidget( ClassViewPart * part )
    : TDEListView( 0, "ClassViewWidget" ), TQToolTip( viewport() ), m_part( part ), m_projectDirectoryLength( 0 )
{
    addColumn( "" );
    header()->hide();
    setSorting( 0 );
    setRootIsDecorated( true );
	setAllColumnsShowFocus( true );

    m_projectItem = 0;

    connect( this, TQT_SIGNAL(returnPressed(TQListViewItem*)), this, TQT_SLOT(slotExecuted(TQListViewItem*)) );
    connect( this, TQT_SIGNAL(executed(TQListViewItem*)), this, TQT_SLOT(slotExecuted(TQListViewItem*)) );
    connect( m_part->core(), TQT_SIGNAL(projectOpened()), this, TQT_SLOT(slotProjectOpened()) );
    connect( m_part->core(), TQT_SIGNAL(projectClosed()), this, TQT_SLOT(slotProjectClosed()) );
    connect( m_part->core(), TQT_SIGNAL(languageChanged()), this, TQT_SLOT(slotProjectOpened()) );

    TQStringList lst;
    lst << i18n( "Group by Directories" ) << i18n( "Plain List" ) << i18n( "Java Like Mode" );
    m_actionViewMode = new TDESelectAction( i18n("View Mode"), TDEShortcut(), m_part->actionCollection(), "classview_mode" );
    m_actionViewMode->setItems( lst );
    m_actionViewMode->setWhatsThis(i18n("<b>View mode</b><p>Class browser items can be grouped by directories, listed in a plain or java like view."));

    m_actionNewClass = new TDEAction( i18n("New Class..."), TDEShortcut(), TQT_TQOBJECT(this), TQT_SLOT(slotNewClass()),
				    m_part->actionCollection(), "classview_new_class" );
    m_actionNewClass->setWhatsThis(i18n("<b>New class</b><p>Calls the <b>New Class</b> wizard."));
	
	m_actionCreateAccessMethods = new TDEAction( i18n("Create get/set Methods"), TDEShortcut(), TQT_TQOBJECT(this), TQT_SLOT(slotCreateAccessMethods()), m_part->actionCollection(), "classview_create_access_methods" );
	
	
    m_actionAddMethod = new TDEAction( i18n("Add Method..."), TDEShortcut(), TQT_TQOBJECT(this), TQT_SLOT(slotAddMethod()),
				    m_part->actionCollection(), "classview_add_method" );
    m_actionAddMethod->setWhatsThis(i18n("<b>Add method</b><p>Calls the <b>New Method</b> wizard."));
    m_actionAddAttribute = new TDEAction( i18n("Add Attribute..."), TDEShortcut(), TQT_TQOBJECT(this), TQT_SLOT(slotAddAttribute()),
				    m_part->actionCollection(), "classview_add_attribute" );
    m_actionAddAttribute->setWhatsThis(i18n("<b>Add attribute</b><p>Calls the <b>New Attribute</b> wizard."));

    m_actionOpenDeclaration = new TDEAction( i18n("Open Declaration"), TDEShortcut(), TQT_TQOBJECT(this), TQT_SLOT(slotOpenDeclaration()),
				    m_part->actionCollection(), "classview_open_declaration" );
    m_actionOpenDeclaration->setWhatsThis(i18n("<b>Open declaration</b><p>Opens a file where the selected item is declared and jumps to the declaration line."));
    m_actionOpenImplementation = new TDEAction( i18n("Open Implementation"), TDEShortcut(), TQT_TQOBJECT(this), TQT_SLOT(slotOpenImplementation()),
				    m_part->actionCollection(), "classview_open_implementation" );
    m_actionOpenImplementation->setWhatsThis(i18n("<b>Open implementation</b><p>Opens a file where the selected item is defined (implemented) and jumps to the definition line."));

	m_actionFollowEditor = new TDEToggleAction( i18n("Follow Editor"), TDEShortcut(), TQT_TQOBJECT(this), TQT_SLOT(slotFollowEditor()), m_part->actionCollection(), "classview_follow_editor" );

    TDEConfig* config = m_part->instance()->config();
    config->setGroup( "General" );
    setViewMode( config->readNumEntry( "ViewMode", KDevelop3ViewMode ) );
    m_doFollowEditor = config->readBoolEntry( "FollowEditor", false );
}

ClassViewWidget::~ClassViewWidget( )
{
    TDEConfig* config = m_part->instance()->config();
    config->setGroup( "General" );
    config->writeEntry( "ViewMode", viewMode() );
    config->writeEntry( "FollowEditor", m_doFollowEditor );
    config->sync();
}

template <class ModelType, class ListItemType>
        static bool selectItemG( ItemDom item, const TQMap<TDESharedPtr<ModelType>, ListItemType*>& map )
{
    ModelType* c = dynamic_cast<ModelType*>( &(*item) );
    
    if( c ) {
        TDESharedPtr<ModelType> d( c );
        typename TQMap<TDESharedPtr<ModelType>, ListItemType*>::ConstIterator it = map.find(d);
        if( it != map.end() ) {
            ( *it )->select();
            return true;
        }
    }
    return false;
}


void ClassViewWidget::slotExecuted( TQListViewItem* item )
{
    if( ClassViewItem* cbitem = dynamic_cast<ClassViewItem*>( item ) ){
	if( cbitem->hasImplementation() )
	    cbitem->openImplementation();
	else
	    cbitem->openDeclaration();
    }
}

void ClassViewWidget::clear( )
{
    TDEListView::clear();
    removedText.clear();
    m_projectItem = 0;
}

void restoreOpenNodes( TQStringList & list, TQListViewItem * item )
{
	if ( item && !list.isEmpty() )
	{
		if ( item->text( 0 ) == list.first() )
		{
 			item->setOpen( true );
			list.pop_front();
			restoreOpenNodes( list, item->firstChild() );
		}
		else
		{	
			restoreOpenNodes( list, item->nextSibling() );
		}
	}
}

void storeOpenNodes( TQValueList<TQStringList> & openNodes, TQStringList const & list, TQListViewItem * item )
{
	if ( item )
	{
		if ( item->isOpen() )
		{
			TQStringList mylist( list );
			mylist << item->text( 0 );
			openNodes << mylist;
			storeOpenNodes( openNodes, mylist, item->firstChild() );
		}
		storeOpenNodes( openNodes, list, item->nextSibling() );
	}
}

void ClassViewWidget::refresh()
{
    if( !m_part->project() )
	return;

    TQValueList<TQStringList> openNodes;
    storeOpenNodes( openNodes, TQStringList(), firstChild() );
    int scrollbarPos = verticalScrollBar()->value();

    clear();
    m_projectItem = new FolderBrowserItem( this, this, m_part->project()->projectName() );
    m_projectItem->setOpen( true );
    blockSignals( true );

    FileList fileList = m_part->codeModel()->fileList();
    FileList::Iterator it = fileList.begin();
    while( it != fileList.end() ){
	insertFile( (*it)->name() );
	++it;
    }

    TQValueList<TQStringList>::iterator itt = openNodes.begin();
    while ( itt != openNodes.end() )
    {
        restoreOpenNodes ( *itt, firstChild() );
        ++itt;
    }
    verticalScrollBar()->setValue( scrollbarPos );

    blockSignals( false );
}

void ClassViewWidget::slotProjectOpened( )
{
    m_projectItem = new FolderBrowserItem( this, this, m_part->project()->projectName() );
    m_projectItem->setOpen( true );

    m_projectDirectory = URLUtil::canonicalPath( m_part->project()->projectDirectory() );
    if( m_projectDirectory.isEmpty() )
	m_projectDirectory = m_part->project()->projectDirectory();

    m_projectDirectoryLength = m_projectDirectory.length() + 1;

    connect( m_part->languageSupport(), TQT_SIGNAL(updatedSourceInfo()),
	     this, TQT_SLOT(refresh()) );
    connect( m_part->languageSupport(), TQT_SIGNAL(aboutToRemoveSourceInfo(const TQString&)),
	     this, TQT_SLOT(removeFile(const TQString&)) );
    connect( m_part->languageSupport(), TQT_SIGNAL(addedSourceInfo(const TQString&)),
	     this, TQT_SLOT(insertFile(const TQString&)) );
}

void ClassViewWidget::slotProjectClosed( )
{
}


void ClassViewWidget::insertFile( const TQString& fileName )
{
    TQString fn = URLUtil::canonicalPath( fileName );
    //kdDebug() << "======================== insertFile(" << fn << ")" << endl;

    FileDom dom = m_part->codeModel()->fileByName( fn );
    if( !dom )
	return;

    fn = URLUtil::relativePathToFile(m_part->project()->projectDirectory(), fn);
    TQStringList path;

    switch( viewMode() )
    {
    case KDevelop3ViewMode:
	{
	    path = TQStringList::split( "/", fn );
	    path.pop_back();
	}
	break;

    case KDevelop2ViewMode:
	{
	}
	break;

    case JavaLikeViewMode:
	{
	    TQStringList l = TQStringList::split( "/", fn );
	    l.pop_back();

	    TQString package = l.join(".");
	    if( !package.isEmpty() )
		path.push_back( package );
	}
	break;
    }

    m_projectItem->processFile( dom, path );
}

void ClassViewWidget::removeFile( const TQString& fileName )
{
    TQString fn = URLUtil::canonicalPath( fileName );
    //kdDebug() << "======================== removeFile(" << fn << ")" << endl;

    FileDom dom = m_part->codeModel()->fileByName( fn );
    if( !dom )
	return;

    fn = URLUtil::relativePathToFile(m_part->project()->projectDirectory(), fn);
    TQStringList path;

    switch( viewMode() )
    {
    case KDevelop3ViewMode:
	{
	    path = TQStringList::split( "/", fn );
	    path.pop_back();
	}
	break;

    case KDevelop2ViewMode:
	{
	}
	break;

    case JavaLikeViewMode:
	{
	    TQStringList l = TQStringList::split( "/", fn );
	    l.pop_back();

	    TQString package = l.join(".");
	    if( !package.isEmpty() )
		path.push_back( package );
	}
	break;
    }

    m_projectItem->processFile( dom, path, true );
}

void ClassViewWidget::contentsContextMenuEvent( TQContextMenuEvent * ev )
{
    TDEPopupMenu menu( this );

    ClassViewItem* item = dynamic_cast<ClassViewItem*>( selectedItem() );

    m_actionFollowEditor->plug( &menu );
    m_actionFollowEditor->setChecked( m_doFollowEditor );
    menu.insertSeparator();

    m_actionOpenDeclaration->setEnabled( item && item->hasDeclaration() );
    m_actionOpenImplementation->setEnabled( item && item->hasImplementation() );

    m_actionOpenDeclaration->plug( &menu );
    m_actionOpenImplementation->plug( &menu );
    menu.insertSeparator();

    bool sep = false;
    if( item && item->isClass() ){
        if( m_part->langHasFeature(KDevLanguageSupport::AddMethod) ) {
            m_actionAddMethod->plug( &menu );
            sep = true;
        }
	
        if( m_part->langHasFeature(KDevLanguageSupport::AddAttribute) ) {
	    m_actionAddAttribute->plug( &menu );
            sep = true;
        }	
    }
	
	if (item && item->isVariable()){
		if( m_part->langHasFeature(KDevLanguageSupport::CreateAccessMethods) ) 
				m_actionCreateAccessMethods->plug( &menu );
	}

    if( item && item->model() ){
	CodeModelItemContext context( item->model() );
	m_part->core()->fillContextMenu( &menu, &context );
//        sep = true;
    }
    if (sep)
        menu.insertSeparator();

    int oldViewMode = viewMode();
    m_actionViewMode->plug( &menu );

    menu.exec( ev->globalPos() );

    if( viewMode() != oldViewMode )
	refresh();

    ev->consume();
}

void ClassViewWidget::setViewMode( int mode ) {
    m_actionViewMode->setCurrentItem( mode );
}

int ClassViewWidget::viewMode( ) const
{
     return m_actionViewMode->currentItem();
}

TQString ClassViewItem::comment() {
    return TQString();
}

void ClassViewItem::select( ) {
    setOpen(true);
    listView()->setCurrentItem(this);
    TQListViewItem* c = firstChild();
    int size = 0;
    if(c != 0) {
        while(c->nextSibling()) {
            c = c->nextSibling();
            size++;
        }
        listView()->ensureItemVisible(c);
    }
    listView()->ensureItemVisible(this);
}


bool ClassViewWidget::selectItem( ItemDom item ) {
    if(!m_projectItem || !isVisible()) return false;
    if(item->isFunctionDefinition() && dynamic_cast<FunctionDefinitionModel*>(&(*item)) != 0) {
        FunctionList lst;
        FileList fileList = m_part->codeModel()->fileList();
        CodeModelUtils::findFunctionDeclarations( FindOp2(FunctionDefinitionDom( (FunctionDefinitionModel*)&(*item) )), fileList, lst );
        if(lst.isEmpty()) return false;

        item = *lst.begin();
    }
    
    return m_projectItem->selectItem( item );
}


TQString NamespaceDomBrowserItem::comment() {
    return m_dom->comment();
}

bool FolderBrowserItem::selectItem(ItemDom item)
{
    if(item->kind() == CodeModelItem::Class) {
        if( selectItemG ( item, m_classes ) ) return true;
    }

    if(item->kind() == CodeModelItem::Function) {
        if( selectItemG ( item, m_functions ) ) return true;
    }
    
    if(item->kind() == CodeModelItem::TypeAlias) {
        if( selectItemG ( item, m_typeAliases ) ) return true;
    }
    
    if(item->kind() == CodeModelItem::Variable) {
        if( selectItemG ( item, m_variables ) ) return true;
    }
    
    if(item->kind() == CodeModelItem::Namespace) {
        ///searching for namespaces is currently not supported and not useful
    }
    
    for( TQMap<ClassDom, ClassDomBrowserItem*>::Iterator it = m_classes.begin(); it != m_classes.end(); ++it ) {
        if( (*it)->selectItem(item) )
            return true;
    }
    
    for( TQMap<TQString, NamespaceDomBrowserItem*>::Iterator it = m_namespaces.begin(); it != m_namespaces.end(); ++it ) {
        if( (*it)->selectItem(item) )
            return true;
    }

    for( TQMap<TQString, FolderBrowserItem*>::Iterator it = m_folders.begin(); it != m_folders.end(); ++it ) {
        if( (*it)->selectItem(item) ) {
            return true;
        }
    }
    return false;
}


void FolderBrowserItem::processFile( FileDom file, TQStringList& path, bool remove )
{
    if( path.isEmpty() ){
	NamespaceList namespaceList = file->namespaceList();
	ClassList classList = file->classList();
	TypeAliasList typeAliasList = file->typeAliasList();
	FunctionList functionList = file->functionList();
	VariableList variableList = file->variableList();

	for( NamespaceList::Iterator it=namespaceList.begin(); it!=namespaceList.end(); ++it )
	    processNamespace( *it, remove );
	for( ClassList::Iterator it=classList.begin(); it!=classList.end(); ++it )
	    processClass( *it, remove );
	for( TypeAliasList::Iterator it=typeAliasList.begin(); it!=typeAliasList.end(); ++it )
	    processTypeAlias( *it, remove );
	for( FunctionList::Iterator it=functionList.begin(); it!=functionList.end(); ++it )
	    processFunction( *it, remove );
	for( VariableList::Iterator it=variableList.begin(); it!=variableList.end(); ++it )
	    processVariable( *it, remove );

	return;
    }

    TQString current = path.front();
    path.pop_front();

    FolderBrowserItem* item = m_folders.contains( current ) ? m_folders[ current ] : 0;
    if( !item ){
	if( remove )
	    return;

	item = new FolderBrowserItem( m_widget, this, current );
	if( listView()->removedText.contains(current) )
	    item->setOpen( true );
	m_folders.insert( current, item );
    }

    item->processFile( file, path, remove );

    if( remove && item->childCount() == 0 ){
	m_folders.remove( current );
	if( item->isOpen() ){
	    listView()->removedText << current;
	}
	delete( item );
	item = 0;
    }
}

void FolderBrowserItem::processNamespace( NamespaceDom ns, bool remove )
{
    NamespaceDomBrowserItem* item = m_namespaces.contains( ns->name() ) ? m_namespaces[ ns->name() ] : 0;
    if( !item ){
	if( remove )
	    return;

	item = new NamespaceDomBrowserItem( this, ns );
	if( listView()->removedText.contains(ns->name()) )
	    item->setOpen( true );
	m_namespaces.insert( ns->name(), item );
    }

    NamespaceList namespaceList = ns->namespaceList();
    ClassList classList = ns->classList();
    TypeAliasList typeAliasList = ns->typeAliasList();
    FunctionList functionList = ns->functionList();
    VariableList variableList = ns->variableList();

    for( NamespaceList::Iterator it=namespaceList.begin(); it!=namespaceList.end(); ++it )
	item->processNamespace( *it, remove );
    for( ClassList::Iterator it=classList.begin(); it!=classList.end(); ++it )
	item->processClass( *it, remove );
    for( TypeAliasList::Iterator it=typeAliasList.begin(); it!=typeAliasList.end(); ++it )
	item->processTypeAlias( *it, remove );
    for( FunctionList::Iterator it=functionList.begin(); it!=functionList.end(); ++it )
	item->processFunction( *it, remove );
    for( VariableList::Iterator it=variableList.begin(); it!=variableList.end(); ++it )
	item->processVariable( *it, remove );

    if( remove && item->childCount() == 0 ){
	m_namespaces.remove( ns->name() );
	if( item->isOpen() ){
	    listView()->removedText << ns->name();
	}
	delete( item );
	item = 0;
    }
}

void FolderBrowserItem::processClass( ClassDom klass, bool remove )
{
    ClassDomBrowserItem* item = m_classes.contains( klass ) ? m_classes[ klass ] : 0;
    if( !item ){
	if( remove )
	    return;

	item = new ClassDomBrowserItem( this, klass );
	if( listView()->removedText.contains(klass->name()) )
	    item->setOpen( true );
	m_classes.insert( klass, item );
    }

    ClassList classList = klass->classList();
    TypeAliasList typeAliasList = klass->typeAliasList();
    FunctionList functionList = klass->functionList();
    VariableList variableList = klass->variableList();

    for( ClassList::Iterator it=classList.begin(); it!=classList.end(); ++it )
	item->processClass( *it, remove );
    for( TypeAliasList::Iterator it=typeAliasList.begin(); it!=typeAliasList.end(); ++it )
	item->processTypeAlias( *it, remove );
    for( FunctionList::Iterator it=functionList.begin(); it!=functionList.end(); ++it )
	item->processFunction( *it, remove );
    for( VariableList::Iterator it=variableList.begin(); it!=variableList.end(); ++it )
	item->processVariable( *it, remove );

    if( remove && item->childCount() == 0 ){
	m_classes.remove( klass );
	if( item->isOpen() ){
	    listView()->removedText << klass->name();
	}
	delete( item );
	item = 0;
    }
}

void FolderBrowserItem::processTypeAlias( TypeAliasDom typeAlias, bool remove )
{
    TypeAliasDomBrowserItem* item = m_typeAliases.contains( typeAlias ) ? m_typeAliases[ typeAlias ] : 0;
    if( !item ){
	if( remove )
	    return;

	item = new TypeAliasDomBrowserItem( this, typeAlias );
	if( listView()->removedText.contains(typeAlias->name()) )
	    item->setOpen( true );
	m_typeAliases.insert( typeAlias, item );
    }

    if( remove && item->childCount() == 0 ){
	m_typeAliases.remove( typeAlias );
	if( item->isOpen() ){
	    listView()->removedText << typeAlias->name();
	}
	delete( item );
	item = 0;
    }
}

void FolderBrowserItem::processFunction( FunctionDom fun, bool remove )
{
    FunctionDomBrowserItem* item = m_functions.contains( fun ) ? m_functions[ fun ] : 0;
    if( !item ){
	if( remove )
	    return;

	item = new FunctionDomBrowserItem( this, fun );
	m_functions.insert( fun, item );
    }

    if( remove ){
	m_functions.remove( fun );
	delete( item );
	item = 0;
    }
}

void FolderBrowserItem::processVariable( VariableDom var, bool remove )
{
    VariableDomBrowserItem* item = m_variables.contains( var ) ? m_variables[ var ] : 0;
    if( !item ){
	if( remove )
	    return;

	item = new VariableDomBrowserItem( this, var );
	m_variables.insert( var, item );
    }

    if( remove ){
	m_variables.remove( var );
	delete( item );
	item = 0;
    }
}

// ------------------------------------------------------------------------
bool NamespaceDomBrowserItem::selectItem( ItemDom item) 
{
    if(item->kind() == CodeModelItem::Class) {
        if( selectItemG ( item, m_classes ) ) return true;
    }

    if(item->kind() == CodeModelItem::Function) {
        if( selectItemG ( item, m_functions ) ) return true;
    }
    
    if(item->kind() == CodeModelItem::TypeAlias) {
        if( selectItemG ( item, m_typeAliases ) ) return true;
    }
    
    if(item->kind() == CodeModelItem::Variable) {
        if( selectItemG ( item, m_variables ) ) return true;
    }
    
    if(item->kind() == CodeModelItem::Namespace) {
        ///currently not neccessary..
    }
    
    for(TQMap<ClassDom, ClassDomBrowserItem*>::Iterator it = m_classes.begin(); it != m_classes.end(); ++it) {
        if( (*it)->selectItem(item) )
            return true;
    }
    
    for(TQMap<TQString, NamespaceDomBrowserItem*>::Iterator it = m_namespaces.begin(); it != m_namespaces.end(); ++it) {
        if( (*it)->selectItem(item) )
            return true;
    }
    return false;
}


void NamespaceDomBrowserItem::processNamespace( NamespaceDom ns, bool remove )
{
    NamespaceDomBrowserItem* item = m_namespaces.contains( ns->name() ) ? m_namespaces[ ns->name() ] : 0;
    if( !item ){
	if( remove )
	    return;

	item = new NamespaceDomBrowserItem( this, ns );
	if( listView()->removedText.contains(ns->name()) )
	    item->setOpen( true );
	m_namespaces.insert( ns->name(), item );
    }

    NamespaceList namespaceList = ns->namespaceList();
    ClassList classList = ns->classList();
    TypeAliasList typeAliasList = ns->typeAliasList();
    FunctionList functionList = ns->functionList();
    VariableList variableList = ns->variableList();

    for( NamespaceList::Iterator it=namespaceList.begin(); it!=namespaceList.end(); ++it )
	item->processNamespace( *it, remove );
    for( ClassList::Iterator it=classList.begin(); it!=classList.end(); ++it )
	item->processClass( *it, remove );
    for( TypeAliasList::Iterator it=typeAliasList.begin(); it!=typeAliasList.end(); ++it )
	item->processTypeAlias( *it, remove );
    for( FunctionList::Iterator it=functionList.begin(); it!=functionList.end(); ++it )
	item->processFunction( *it, remove );
    for( VariableList::Iterator it=variableList.begin(); it!=variableList.end(); ++it )
	item->processVariable( *it, remove );

    if( remove && item->childCount() == 0 ){
	m_namespaces.remove( ns->name() );
	if( item->isOpen() ){
	    listView()->removedText << ns->name();
	}
	delete( item );
	item = 0;
    }
}

void NamespaceDomBrowserItem::processClass( ClassDom klass, bool remove )
{
    ClassDomBrowserItem* item = m_classes.contains( klass ) ? m_classes[ klass ] : 0;
    if( !item ){
	if( remove )
	    return;

	item = new ClassDomBrowserItem( this, klass );
	if( listView()->removedText.contains(klass->name()) )
	    item->setOpen( true );
	m_classes.insert( klass, item );
    }

    ClassList classList = klass->classList();
    TypeAliasList typeAliasList = klass->typeAliasList();
    FunctionList functionList = klass->functionList();
    VariableList variableList = klass->variableList();

    for( ClassList::Iterator it=classList.begin(); it!=classList.end(); ++it )
	item->processClass( *it, remove );
    for( TypeAliasList::Iterator it=typeAliasList.begin(); it!=typeAliasList.end(); ++it )
	item->processTypeAlias( *it, remove );
    for( FunctionList::Iterator it=functionList.begin(); it!=functionList.end(); ++it )
	item->processFunction( *it, remove );
    for( VariableList::Iterator it=variableList.begin(); it!=variableList.end(); ++it )
	item->processVariable( *it, remove );

    if( remove && item->childCount() == 0 ){
	m_classes.remove( klass );
	if( item->isOpen() ){
	    listView()->removedText << klass->name();
	}
	delete( item );
	item = 0;
    }
}

void NamespaceDomBrowserItem::processTypeAlias( TypeAliasDom typeAlias, bool remove )
{
    TypeAliasDomBrowserItem* item = m_typeAliases.contains( typeAlias ) ? m_typeAliases[ typeAlias ] : 0;
    if( !item ){
	if( remove )
	    return;

	item = new TypeAliasDomBrowserItem( this, typeAlias );
	if( listView()->removedText.contains(typeAlias->name()) )
	    item->setOpen( true );
	m_typeAliases.insert( typeAlias, item );
    }

    if( remove && item->childCount() == 0 ){
	m_typeAliases.remove( typeAlias );
	if( item->isOpen() ){
	    listView()->removedText << typeAlias->name();
	}
	delete( item );
	item = 0;
    }
}

void NamespaceDomBrowserItem::processFunction( FunctionDom fun, bool remove )
{
    FunctionDomBrowserItem* item = m_functions.contains( fun ) ? m_functions[ fun ] : 0;
    if( !item ){
	if( remove )
	    return;

	item = new FunctionDomBrowserItem( this, fun );
	m_functions.insert( fun, item );
    }

    if( remove ){
	m_functions.remove( fun );
	delete( item );
	item = 0;
    }
}

void NamespaceDomBrowserItem::processVariable( VariableDom var, bool remove )
{
    VariableDomBrowserItem* item = m_variables.contains( var ) ? m_variables[ var ] : 0;
    if( !item ){
	if( remove )
	    return;

	item = new VariableDomBrowserItem( this, var );
	m_variables.insert( var, item );
    }

    if( remove ){
	m_variables.remove( var );
	delete( item );
	item = 0;
    }
}

// ------------------------------------------------------------------------


bool ClassDomBrowserItem::selectItem(ItemDom item) 
{
    if(item->kind() == CodeModelItem::Class) {
        if( selectItemG ( item, m_classes ) ) return true;
    }

    if(item->kind() == CodeModelItem::Function) {
        if( selectItemG ( item, m_functions ) ) return true;
    }
    
    if(item->kind() == CodeModelItem::TypeAlias) {
        if( selectItemG ( item, m_typeAliases ) ) return true;
    }
    
    if(item->kind() == CodeModelItem::Variable) {
        if( selectItemG ( item, m_variables ) ) return true;
    }
    
    for(TQMap<ClassDom, ClassDomBrowserItem*>::Iterator it = m_classes.begin(); it != m_classes.end(); ++it) {
        if( (*it)->selectItem(item) )
            return true;
    }
    return false;
}


void ClassDomBrowserItem::processClass( ClassDom klass, bool remove )
{
    ClassDomBrowserItem* item = m_classes.contains( klass ) ? m_classes[ klass ] : 0;
    if( !item ){
	if( remove )
	    return;

	item = new ClassDomBrowserItem( this, klass );
	if( listView()->removedText.contains(klass->name()) )
	    item->setOpen( true );
	m_classes.insert( klass, item );
    }

    ClassList classList = klass->classList();
    TypeAliasList typeAliasList = klass->typeAliasList();
    FunctionList functionList = klass->functionList();
    VariableList variableList = klass->variableList();

    for( ClassList::Iterator it=classList.begin(); it!=classList.end(); ++it )
	item->processClass( *it, remove );
    for( TypeAliasList::Iterator it=typeAliasList.begin(); it!=typeAliasList.end(); ++it )
	item->processTypeAlias( *it, remove );
    for( FunctionList::Iterator it=functionList.begin(); it!=functionList.end(); ++it )
	item->processFunction( *it, remove );
    for( VariableList::Iterator it=variableList.begin(); it!=variableList.end(); ++it )
	item->processVariable( *it, remove );

    if( remove && item->childCount() == 0 ){
	m_classes.remove( klass );
	if( item->isOpen() ){
	    listView()->removedText << klass->name();
	}
	delete( item );
	item = 0;
    }
}

void ClassDomBrowserItem::processTypeAlias( TypeAliasDom typeAlias, bool remove )
{
    TypeAliasDomBrowserItem* item = m_typeAliases.contains( typeAlias ) ? m_typeAliases[ typeAlias ] : 0;
    if( !item ){
	if( remove )
	    return;

	item = new TypeAliasDomBrowserItem( this, typeAlias );
	if( listView()->removedText.contains(typeAlias->name()) )
	    item->setOpen( true );
	m_typeAliases.insert( typeAlias, item );
    }

    if( remove && item->childCount() == 0 ){
	m_typeAliases.remove( typeAlias );
	if( item->isOpen() ){
	    listView()->removedText << typeAlias->name();
	}
	delete( item );
	item = 0;
    }
}

void ClassDomBrowserItem::processFunction( FunctionDom fun, bool remove )
{
    FunctionDomBrowserItem* item = m_functions.contains( fun ) ? m_functions[ fun ] : 0;
    if( !item ){
	if( remove )
	    return;

	item = new FunctionDomBrowserItem( this, fun );
	m_functions.insert( fun, item );
    }

    if( remove ){
	m_functions.remove( fun );
	delete( item );
	item = 0;
    }
}

void ClassDomBrowserItem::processVariable( VariableDom var, bool remove )
{
    VariableDomBrowserItem* item = m_variables.contains( var ) ? m_variables[ var ] : 0;
    if( !item ){
	if( remove )
	    return;

	item = new VariableDomBrowserItem( this, var );
	m_variables.insert( var, item );
    }

    if( remove ){
	m_variables.remove( var );
	delete( item );
	item = 0;
    }
}

void FolderBrowserItem::setup( )
{
    ClassViewItem::setup();
    setPixmap( 0, SmallIcon("folder") );
    setExpandable( true );
}

void NamespaceDomBrowserItem::setup( )
{
    ClassViewItem::setup();
    setPixmap( 0, UserIcon("CVnamespace", TDEIcon::DefaultState, listView()->m_part->instance()) );
    setExpandable( true );

    TQString txt = listView()->m_part->languageSupport()->formatModelItem(m_dom.data(), true);
    setText( 0, txt );
}

void ClassDomBrowserItem::setup( )
{
    ClassViewItem::setup();
    setPixmap( 0, UserIcon("CVclass", TDEIcon::DefaultState, listView()->m_part->instance()) );
    setExpandable( true );

    TQString txt = listView()->m_part->languageSupport()->formatModelItem(m_dom.data(), true);
    setText( 0, txt );
}

void TypeAliasDomBrowserItem::setup( )
{
    ClassViewItem::setup();
    setPixmap( 0, UserIcon("CVtypedef", TDEIcon::DefaultState, listView()->m_part->instance()) );
    setExpandable( false );

    TQString txt = listView()->m_part->languageSupport()->formatModelItem(m_dom.data(), true);
    setText( 0, txt );
}

void FunctionDomBrowserItem::setup( )
{
    ClassViewItem::setup();

    TQString iconName;
	TQString methodType;

	if ( m_dom->isSignal() )
		methodType = "signal";
	else if (m_dom->isSlot() ) 
		methodType = "slot";
	else
		methodType = "meth";

    if( m_dom->access() == CodeModelItem::Private )
        iconName = "CVprivate_" + methodType;
    else if( m_dom->access() == CodeModelItem::Protected )
        iconName = "CVprotected_" + methodType;
    else
        iconName = "CVpublic_" + methodType;

    setPixmap( 0, UserIcon(iconName, TDEIcon::DefaultState, listView()->m_part->instance()) );

    TQString txt = listView()->m_part->languageSupport()->formatModelItem(m_dom.data(), true);
    
    item() = highlightFunctionName(txt, 1, m_styles);
}

void FunctionDomBrowserItem::openDeclaration()
{
    int startLine, startColumn;
    m_dom->getStartPosition( &startLine, &startColumn );
    listView()->m_part->partController()->editDocument( KURL(m_dom->fileName()), startLine );
}

void FunctionDomBrowserItem::openImplementation()
{
    FunctionDefinitionList lst;
    FileList fileList = listView()->m_part->codeModel()->fileList();
    CodeModelUtils::findFunctionDefinitions( FindOp(m_dom), fileList, lst );

    if( lst.isEmpty() )
        return;

    FunctionDefinitionDom fun;
    TQFileInfo fileInfo( m_dom->fileName() );
    TQString path = fileInfo.dirPath( true );

    for( FunctionDefinitionList::Iterator it=lst.begin(); it!=lst.end(); ++it )
    {
        TQFileInfo defFileInfo( (*it)->fileName() );
        TQString defPath = defFileInfo.dirPath( true );

        if( path != defPath )
            continue;

	if( defFileInfo.baseName() == fileInfo.baseName() ) {
            fun = *it;
        } else if( !fun ) {
	    fun = *it;
        }
    }

    if( !fun ) {
        fun = lst.front();
    }

    int startLine, startColumn;
    fun->getStartPosition( &startLine, &startColumn );
    listView()->m_part->partController()->editDocument( KURL(fun->fileName()), startLine );
}

void VariableDomBrowserItem::setup( )
{
    ClassViewItem::setup();
    TQString iconName;
    if( m_dom->access() == CodeModelItem::Private )
        iconName = "CVprivate_var";
    else if( m_dom->access() == CodeModelItem::Protected )
        iconName = "CVprotected_var";
    else
        iconName = "CVpublic_var";

    setPixmap( 0, UserIcon(iconName, TDEIcon::DefaultState, listView()->m_part->instance()) );

    TQString txt = listView()->m_part->languageSupport()->formatModelItem(m_dom.data(), true);
    setText( 0, txt );
}

void VariableDomBrowserItem::openDeclaration()
{
    int startLine, startColumn;
    m_dom->getStartPosition( &startLine, &startColumn );

    listView()->m_part->partController()->editDocument( KURL(m_dom->fileName()), startLine );
}

void VariableDomBrowserItem::openImplementation()
{
}

TQString FolderBrowserItem::key( int , bool ) const
{
    return "0 " + text( 0 );
}

TQString NamespaceDomBrowserItem::key( int , bool ) const
{
    return "1 " + text( 0 );
}

TQString ClassDomBrowserItem::key( int , bool ) const
{
    return "2 " + text( 0 );
}

TQString TypeAliasDomBrowserItem::key( int , bool ) const
{
    return "3 " + text( 0 );
}

TQString FunctionDomBrowserItem::key( int , bool ) const
{
    return "4 " + text( 0 );
}

TQString VariableDomBrowserItem::key( int , bool ) const
{
    return "5 " + text( 0 );
}

void ClassViewWidget::slotNewClass( )
{
    if( m_part->languageSupport()->features() & KDevLanguageSupport::NewClass )
	m_part->languageSupport()->addClass();
}

void ClassViewWidget::slotAddMethod( )
{
    if ( !selectedItem() ) return;

    if( m_part->languageSupport()->features() & KDevLanguageSupport::AddMethod )
        m_part->languageSupport()->addMethod( static_cast<ClassDomBrowserItem*>( selectedItem() )->dom() );
}

void ClassViewWidget::slotAddAttribute( )
{
    if ( !selectedItem() ) return;
    
    if( m_part->languageSupport()->features() & KDevLanguageSupport::AddAttribute )
        m_part->languageSupport()->addAttribute( static_cast<ClassDomBrowserItem*>( selectedItem() )->dom() );
}

void ClassViewWidget::slotOpenDeclaration( )
{
    if ( !selectedItem() ) return;
    
    static_cast<ClassViewItem*>( selectedItem() )->openDeclaration();
}

void ClassViewWidget::slotOpenImplementation( )
{
    if ( !selectedItem() ) return;
    
    static_cast<ClassViewItem*>( selectedItem() )->openImplementation();
}

void ClassDomBrowserItem::openDeclaration( )
{
    int startLine, startColumn;
    m_dom->getStartPosition( &startLine, &startColumn );
    listView()->m_part->partController()->editDocument( KURL(m_dom->fileName()), startLine );
}

void TypeAliasDomBrowserItem::openDeclaration( )
{
    int startLine, startColumn;
    m_dom->getStartPosition( &startLine, &startColumn );
    listView()->m_part->partController()->editDocument( KURL(m_dom->fileName()), startLine );
}

bool FunctionDomBrowserItem::hasImplementation() const
{
    FunctionDefinitionList lst;
    FileList fileList = listView()->m_part->codeModel()->fileList();
    CodeModelUtils::findFunctionDefinitions( FindOp(m_dom), fileList, lst );

    return !lst.isEmpty();
}

void ClassViewWidget::maybeTip( TQPoint const & p )
{
	ClassViewItem * item = dynamic_cast<ClassViewItem*>( itemAt( p ) );
	if ( !item ) return;
	
	TQString tooltip;
	
	if ( item->isNamespace() )
	{
		NamespaceDomBrowserItem * nitem = dynamic_cast<NamespaceDomBrowserItem*>( item );
		if ( nitem )
		{
			tooltip = nitem->dom()->scope().join("::") + "::" + nitem->dom()->name();
		}
	}
	else if ( item->isClass() )
	{
		ClassDomBrowserItem * citem = dynamic_cast<ClassDomBrowserItem*>( item );
		if ( citem )
		{
			tooltip = citem->dom()->scope().join("::") + "::" 
				+ citem->dom()->name() + " : " 
				+ citem->dom()->baseClassList().join(", ");
		}
	}
	else if ( item->isFunction() )
	{
		FunctionDomBrowserItem * fitem = dynamic_cast<FunctionDomBrowserItem*>( item );
		if ( fitem )
		{
			TQString access;
			if ( fitem->dom()->access() == CodeModelItem::Private )
				access = "[private] ";
			else if ( fitem->dom()->access() == CodeModelItem::Protected )
				access = "[protected] ";
			else if ( fitem->dom()->access() == CodeModelItem::Public )
				access = "[public] ";

			TQStringList arguments;
			ArgumentList const & list = fitem->dom()->argumentList();
			ArgumentList::ConstIterator it( list.begin() );
			while ( it != list.end() )
			{
				arguments << ((*it)->type() + " " + (*it)->name());
				++it;
			}
			
			TQString strstatic = fitem->dom()->isStatic() ? TQString( "[static] " ) : TQString();
			TQString strsignal = fitem->dom()->isSignal() ? TQString( "[signal] " ) : TQString();
			TQString strslot = fitem->dom()->isSlot() ? TQString( "[slot] " ) : TQString();
			TQString strresult = !fitem->dom()->resultType().isEmpty() ? fitem->dom()->resultType() + " " : TQString();
			
			TQString strconstant = fitem->dom()->isConstant() ? TQString( " [const]" ) : TQString();
			TQString strabstract = fitem->dom()->isAbstract() ? TQString( " [abstract]" ) : TQString();
			
			tooltip = access + strstatic + strsignal + strslot + strresult
				+ fitem->dom()->scope().join("::") + "::" + fitem->dom()->name() 
				+ "(" + arguments.join(", ") + ")" + strconstant + strabstract;
		}
	}
	else if ( item->isVariable() )
	{
		VariableDomBrowserItem * vitem = dynamic_cast<VariableDomBrowserItem*>( item );
		if ( vitem )
		{
			TQString access;
			if ( vitem->dom()->access() == CodeModelItem::Private )
				access = "[private] ";
			else if ( vitem->dom()->access() == CodeModelItem::Protected )
				access = "[protected] ";
			else if ( vitem->dom()->access() == CodeModelItem::Public )
				access = "[public] ";

			TQString strstatic = vitem->dom()->isStatic() ? TQString( "[static] " ) : TQString();
			tooltip = access + strstatic + vitem->dom()->type() + " " + vitem->dom()->name();
		}	
	}
	else if ( item->isTypeAlias() )
	{
		if( TypeAliasDomBrowserItem * titem = dynamic_cast<TypeAliasDomBrowserItem*>( item ) )
		{
			tooltip = TQString( "[Type] " ) + titem->dom()->type() + " " + titem->dom()->name();
		}
	}
        
        const int maxCommentSize = 300;
	
        if( !item->comment().isEmpty() ) {
            tooltip += "\n";
            tooltip += item->comment().length() > maxCommentSize ? item->comment().left( maxCommentSize ) + " [...]" : item->comment();
        }
        
	kdDebug(0) << tooltip << endl;
	
	TQRect r = itemRect( item );

	if ( item && r.isValid() && !tooltip.isEmpty() )
	{
		tip( r, TQString("<qt><pre>") + TQStyleSheet::escape( tooltip ) + TQString("</pre></qt>") );
	}
}

void ClassViewWidget::slotCreateAccessMethods( )
{
	if ( !selectedItem() ) return;

    if( m_part->languageSupport()->features() & KDevLanguageSupport::CreateAccessMethods )
	{
        VariableDomBrowserItem* item = dynamic_cast<VariableDomBrowserItem*>( selectedItem() );
		if (item == 0)
			return;
		
		m_part->languageSupport()->createAccessMethods(static_cast<ClassModel*>(static_cast<ClassDomBrowserItem*>(item->parent())->dom()),static_cast<VariableModel*>(item->dom()));
	}
}

void ClassViewWidget::slotFollowEditor()
{
	m_doFollowEditor = m_actionFollowEditor->isChecked();
}

bool ClassViewWidget::doFollowEditor()
{
	return m_doFollowEditor;
}


#include "classviewwidget.moc"
