/***************************************************************************
 *   Copyright (C) 1999 by Jonas Nordin                                    *
 *   jonas.nordin@syncom.se                                                *
 *   Copyright (C) 2000-2001 by Bernd Gehrmann                             *
 *   bernd@kdevelop.org                                                    *
 *   Copyright (C) 2002-2003 by Roberto Raggi                              *
 *   roberto@kdevelop.org                                                  *
 *   Copyright (C) 2003-2004 by Alexander Dymo                             *
 *   adymo@mksat.net                                                       *
 *                                                                         *
 *   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 "cppsupportpart.h"
#include "cppsupport_events.h"
#include "problemreporter.h"
#include "backgroundparser.h"
#include "store_walker.h"
#include "ast.h"
#include "ast_utils.h"
#include "cppcodecompletion.h"
#include "ccconfigwidget.h"
#include "KDevCppSupportIface.h"
#include "cppsupportfactory.h"
#include "catalog.h"
#include "cpp_tags.h"
#include "kdevdriver.h"
#include "cppcodecompletionconfig.h"
#include "tag_creator.h"
#include "cppsupport_utils.h"
#include "classgeneratorconfig.h"
#include "urlutil.h"
#include "creategettersetterconfiguration.h"
#include "kdevsourceformatter.h"
#include "kdevcreatefile.h"
// wizards
#include "cppnewclassdlg.h"
#include "subclassingdlg.h"
#include "addmethoddialog.h"
#include "addattributedialog.h"
#include "creategettersetterdialog.h"
// designer integration
#include "qtdesignercppintegration.h"
#include "cppimplementationwidget.h"

#include <qheader.h>
#include <qdir.h>
#include <qdom.h>
#include <qfileinfo.h>
#include <qguardedptr.h>
#include <qpopupmenu.h>
#include <qprogressdialog.h>
#include <qstringlist.h>
#include <qtimer.h>
#include <qstatusbar.h>
#include <qprogressbar.h>
#include <qregexp.h>
#include <qlabel.h>
#include <qvbox.h>
#include <kmessagebox.h>
#include <kaction.h>
#include <kapplication.h>
#include <kdebug.h>
#include <kdialogbase.h>
#include <kgenericfactory.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kmainwindow.h>
#include <kstatusbar.h>
#include <kconfig.h>
#include <kdeversion.h>
#include <kstandarddirs.h>
#include <kiconloader.h>

#include <ktexteditor/document.h>
#include <ktexteditor/editinterface.h>
#include <ktexteditor/view.h>
#include <ktexteditor/selectioninterface.h>
#include <ktexteditor/viewcursorinterface.h>
#include <ktexteditor/clipboardinterface.h>

#if defined(KDE_MAKE_VERSION)
# if KDE_VERSION >= KDE_MAKE_VERSION(3,1,90)
#  include <ktexteditor/texthintinterface.h>
# else
#  include <kde30x_texthintinterface.h>
# endif
#else
#  include <kde30x_texthintinterface.h>
#endif

#include <kdevcore.h>
#include <kdevproject.h>
#include <kdevmainwindow.h>
#include <kdevpartcontroller.h>
#include <kdevmakefrontend.h>
#include <kdevcoderepository.h>
#include <codemodel_utils.h>
#include <kdevplugininfo.h>

#include <domutil.h>
#include <config.h>

enum { KDEV_DB_VERSION = 6 };
enum { KDEV_PCS_VERSION = 5 };

QStringList CppSupportPart::m_sourceMimeTypes = QStringList() << "text/x-csrc" << "text/x-c++src";
QStringList CppSupportPart::m_headerMimeTypes = QStringList() << "text/x-chdr" << "text/x-c++hdr";

QStringList CppSupportPart::m_sourceExtensions = QStringList::split( ",", "c,C,cc,cpp,c++,cxx,m,mm,M" );
QStringList CppSupportPart::m_headerExtensions = QStringList::split( ",", "h,H,hh,hxx,hpp,inl,tlh,diff,ui.h" );

class CppDriver: public KDevDriver
{
public:
    CppDriver( CppSupportPart* cppSupport )
	: KDevDriver( cppSupport )
    {
    }

    void fileParsed( const QString& fileName )
    {
	//kdDebug(9007) << "-----> file " << fileName << " parsed!" << endl;
	TranslationUnitAST::Node ast = takeTranslationUnit( fileName );

        if( cppSupport()->problemReporter() ){
	    cppSupport()->problemReporter()->removeAllProblems( fileName );

	    QValueList<Problem> pl = problems( fileName );
	    QValueList<Problem>::ConstIterator it = pl.begin();
	    while( it != pl.end() ){
	        const Problem& p = *it++;
	        cppSupport()->problemReporter()->reportProblem( fileName, p );
	    }
	}

	StoreWalker walker( fileName, cppSupport()->codeModel() );

	if( cppSupport()->codeModel()->hasFile(fileName) ){
	    FileDom file = cppSupport()->codeModel()->fileByName( fileName );
	    cppSupport()->removeWithReferences( fileName );
	}

	walker.parseTranslationUnit( ast.get() );
	cppSupport()->codeModel()->addFile( walker.file() );
	remove( fileName );
    }
};

CppSupportPart::CppSupportPart(QObject *parent, const char *name, const QStringList &args)
    : KDevLanguageSupport(CppSupportFactory::info(), parent, name ? name : "KDevCppSupport"),
      m_activeDocument( 0 ), m_activeView( 0 ), m_activeSelection( 0 ), m_activeEditor( 0 ),
      m_activeViewCursor( 0 ), m_projectClosed( true ), m_valid( false )
{
    setInstance(CppSupportFactory::instance());

    m_pCompletionConfig = new CppCodeCompletionConfig( this, projectDom() );
	m_pCreateGetterSetterConfiguration = new CreateGetterSetterConfiguration(this);
    connect( m_pCompletionConfig, SIGNAL(stored()), this, SLOT(codeCompletionConfigStored()) );

    m_driver = new CppDriver( this );
    m_problemReporter = 0;

    m_functionHintTimer = new QTimer( this );
//    connect( m_functionHintTimer, SIGNAL(timeout()), this, SLOT(slotFunctionHint()) );

    setXMLFile( "kdevcppsupport.rc" );

    m_catalogList.setAutoDelete( true );

    connect( core(), SIGNAL(projectOpened()), this, SLOT(projectOpened()) );
    connect( core(), SIGNAL(projectClosed()), this, SLOT(projectClosed()) );
    connect( core(), SIGNAL(languageChanged()), this, SLOT(projectOpened()) );
    connect( partController(), SIGNAL(savedFile(const KURL&)),
             this, SLOT(savedFile(const KURL&)) );
    connect( core(), SIGNAL(contextMenu(QPopupMenu *, const Context *)),
             this, SLOT(contextMenu(QPopupMenu *, const Context *)) );
    connect( partController(), SIGNAL(activePartChanged(KParts::Part*)),
             this, SLOT(activePartChanged(KParts::Part*)));
    connect( partController(), SIGNAL(partRemoved(KParts::Part*)),
             this, SLOT(partRemoved(KParts::Part*)));

    connect( core(), SIGNAL(configWidget(KDialogBase*)),
             this, SLOT(configWidget(KDialogBase*)) );

    KAction *action;

    action = new KAction(i18n("Switch Header/Implementation"), SHIFT+Key_F12,
                         this, SLOT(slotSwitchHeader()),
                         actionCollection(), "edit_switchheader");
    action->setToolTip( i18n("Switch between header and implementation files") );
    action->setWhatsThis( i18n("<b>Switch Header/Implementation</b><p>"
                               "If you are currently looking at a header file, this "
                               "brings you to the corresponding implementation file. "
                               "If you are looking at an implementation file (.cpp etc.), "
                               "this brings you to the corresponding header file.") );
    action->setEnabled(false);

    action = new KAction(i18n("Complete Text"), CTRL+Key_Space,
                         this, SLOT(slotCompleteText()),
                         actionCollection(), "edit_complete_text");
    action->setToolTip( i18n("Complete current expression") );
    action->setWhatsThis( i18n("<b>Complete Text</p><p>Completes current expression using "
                               "memory class store for the current project and persistant class stores "
                               "for external libraries.") );
    action->setEnabled(false);
	
	m_createGetterSetterAction = new KAction(i18n("Create Accessor Methods"), 0, this, SLOT(slotCreateAccessMethods()), actionCollection(), "edit_create_getter_setter");
	
    action = new KAction(i18n("Make Member"), 0, Key_F2,
                         this, SLOT(slotMakeMember()),
                         actionCollection(), "edit_make_member");
    action->setToolTip(i18n("Make member"));
    action->setWhatsThis(i18n("<b>Make member</b><p>Creates a class member function in implementation file "
                              "based on the member declaration at the current line."));
    action->plug(new QWidget());

    action = new KAction(i18n("New Class..."), "classnew", 0,
                         this, SLOT(slotNewClass()),
                         actionCollection(), "project_newclass");
    action->setToolTip( i18n("Generate a new class") );
    action->setWhatsThis( i18n("<b>New Class</b><p>Calls the <b>New Class</b> wizard.") );

    m_pCompletion  = 0;

    withcpp = false;
    if ( args.count() == 1 && args[ 0 ] == "Cpp" )
        withcpp = true;

    // daniel
    connect( core( ), SIGNAL( projectConfigWidget( KDialogBase* ) ), this,
             SLOT( projectConfigWidget( KDialogBase* ) ) );

    new KDevCppSupportIface( this );
    //(void) dcopClient();
}


CppSupportPart::~CppSupportPart()
{
    if ( !m_projectClosed )
      projectClosed();

    delete( m_driver );
    m_driver = 0;

    if( m_backgroundParser ){
	m_backgroundParser->close();
//	m_backgroundParser->wait();
	delete m_backgroundParser;
	m_backgroundParser = 0;
    }

    codeRepository()->setMainCatalog( 0 );

    QPtrListIterator<Catalog> it( m_catalogList );
    while( Catalog* catalog = it.current() ){
        ++it;
        codeRepository()->unregisterCatalog( catalog );
    }

    mainWindow( )->removeView( m_problemReporter );

    delete m_pCompletion;
    delete m_problemReporter;

    m_pCompletion = 0;
    m_problemReporter = 0;

	delete _jd;
	_jd = 0;

	kdDebug(9007) << k_funcinfo << endl;
}

void CppSupportPart::customEvent( QCustomEvent* ev )
{
    kdDebug(9007) << "CppSupportPart::customEvent(" << ev->type() << ")" << endl;

    QTime t;
    t.start();

    if( ev->type() == int(Event_FileParsed) ){
	FileParsedEvent* event = (FileParsedEvent*) ev;
	QString fileName = event->fileName();
        if( m_problemReporter ){
	    m_problemReporter->removeAllProblems( fileName );

	    bool hasErrors = false;
	    QValueList<Problem> problems = event->problems();
	    QValueList<Problem>::ConstIterator it = problems.begin();
	    while( it != problems.end() ){
	        const Problem& p = *it++;
		if( p.level() == Problem::Level_Error )
		    hasErrors = true;

	        m_problemReporter->reportProblem( fileName, p );
	    }
            recomputeCodeModel( fileName );
	    //QTimer::singleShot( 0, this, SLOT(recomputeCodeModel()) );
	}

	emit fileParsed( fileName );
    }
}

void CppSupportPart::projectConfigWidget( KDialogBase* dlg )
{
    QVBox* vbox = 0;

	vbox = dlg->addVBoxPage( i18n( "C++ Specific" ), i18n( "C++ Specific" ), BarIcon( info()->icon(), KIcon::SizeMedium) );
    CCConfigWidget* w = new CCConfigWidget( this, vbox );
    connect( dlg, SIGNAL( okClicked( ) ), w, SLOT( accept( ) ) );
}

void CppSupportPart::configWidget(KDialogBase *dlg)
{
	QVBox *vbox = dlg->addVBoxPage(i18n("C++ Class Generator"), i18n( "C++ Class Generator" ), BarIcon( info()->icon(), KIcon::SizeMedium) );
  ClassGeneratorConfig *w = new ClassGeneratorConfig(vbox, "classgenerator config widget");
  connect(dlg, SIGNAL(okClicked()), w, SLOT(storeConfig()));
}

void CppSupportPart::activePartChanged(KParts::Part *part)
{
    kdDebug(9032) << "CppSupportPart::activePartChanged()" << endl;

    bool enabled = false;

    m_functionHintTimer->stop();

    if( m_activeView )
    {
	disconnect( m_activeView, SIGNAL(cursorPositionChanged()), this, SLOT(slotCursorPositionChanged()) );
    }

    m_activeDocument = dynamic_cast<KTextEditor::Document*>( part );
    m_activeView = part ? dynamic_cast<KTextEditor::View*>( part->widget() ) : 0;
    m_activeEditor = dynamic_cast<KTextEditor::EditInterface*>( part );
    m_activeSelection = dynamic_cast<KTextEditor::SelectionInterface*>( part );
    m_activeViewCursor = part ? dynamic_cast<KTextEditor::ViewCursorInterface*>( m_activeView ) : 0;

    m_activeFileName = QString::null;

    if (m_activeDocument) {
	m_activeFileName = URLUtil::canonicalPath( m_activeDocument->url().path() );
        QFileInfo fi( m_activeFileName );
        QString ext = fi.extension();
        if( isSource(m_activeFileName) || isHeader(m_activeFileName) )
	    enabled = true;
    }

    actionCollection()->action( "edit_switchheader" )->setEnabled( enabled );
    actionCollection()->action( "edit_complete_text" )->setEnabled( enabled );
    actionCollection()->action( "edit_make_member" )->setEnabled( enabled );

    if( !part )
	return;

    if( !m_activeView )
	return;

    if( m_activeViewCursor )
    {
	connect( m_activeView, SIGNAL(cursorPositionChanged()),
		 this, SLOT(slotCursorPositionChanged()) );
    }

#if 0
    KTextEditor::TextHintInterface* textHintIface = dynamic_cast<KTextEditor::TextHintInterface*>( m_activeView );
    if( !textHintIface )
	return;

    connect( view, SIGNAL(needTextHint(int,int,QString&)),
	     this, SLOT(slotNeedTextHint(int,int,QString&)) );

    textHintIface->enableTextHints( 1000 );
#endif
}


void CppSupportPart::projectOpened( )
{
    kdDebug( 9007 ) << "projectOpened( )" << endl;

    m_backgroundParser = new BackgroundParser( this, &m_eventConsumed );
    m_backgroundParser->start();

    // setup the driver
    QString conf_file_name = specialHeaderName();
    if( QFile::exists(conf_file_name) )
        m_driver->parseFile( conf_file_name, true );

    m_projectDirectory = URLUtil::canonicalPath( project()->projectDirectory() );
    m_projectFileList = project()->allFiles();

    setupCatalog();

    m_problemReporter = new ProblemReporter( this );
    m_problemReporter->setIcon( SmallIcon("info") );
    m_problemReporter->setCaption( i18n("Problem Reporter") );
    mainWindow( )->embedOutputView( m_problemReporter, i18n("Problems"), i18n("Problem reporter"));

    connect( core(), SIGNAL(configWidget(KDialogBase*)),
             m_problemReporter, SLOT(configWidget(KDialogBase*)) );

    connect( project( ), SIGNAL( addedFilesToProject( const QStringList & ) ),
             this, SLOT( addedFilesToProject( const QStringList & ) ) );
    connect( project( ), SIGNAL( removedFilesFromProject( const QStringList &) ),
             this, SLOT( removedFilesFromProject( const QStringList & ) ) );
    connect( project( ), SIGNAL( changedFilesInProject( const QStringList & ) ),
             this, SLOT( changedFilesInProject( const QStringList & ) ) );
    connect( project(), SIGNAL(projectCompiled()),
	     this, SLOT(slotProjectCompiled()) );

    QDir::setCurrent( m_projectDirectory );

    m_timestamp.clear();

    m_pCompletion = new CppCodeCompletion( this );
    m_projectClosed = false;

    QTimer::singleShot( 500, this, SLOT( initialParse( ) ) );
}


void CppSupportPart::projectClosed( )
{
    kdDebug( 9007 ) << "projectClosed( )" << endl;

    QStringList enabledPCSs;
    QValueList<Catalog*> catalogs = codeRepository()->registeredCatalogs();
    for( QValueList<Catalog*>::Iterator it=catalogs.begin(); it!=catalogs.end(); ++it )
    {
	Catalog* c = *it;
	if( c->enabled() )
	    enabledPCSs.push_back( QFileInfo(c->dbName()).baseName() );
    }
    DomUtil::writeListEntry( *project()->projectDom(), "kdevcppsupport/references", "pcs", enabledPCSs );

    for (QMap<KInterfaceDesigner::DesignerType, KDevDesignerIntegration*>::const_iterator it =  m_designers.begin();
        it != m_designers.end(); ++it)
    {
        kdDebug() << "calling save settings fro designer integration" << endl;
        it.data()->saveSettings(*project()->projectDom(), "kdevcppsupport/designerintegration");
    }
    
    saveProjectSourceInfo();

    m_pCompletionConfig->store();

    delete _jd;
    _jd = 0;

    delete m_pCompletion;
    m_pCompletion = 0;
    m_projectClosed = true;
}


QString CppSupportPart::findHeader(const QStringList &list, const QString &header)
{
    QStringList::ConstIterator it;
    for (it = list.begin(); it != list.end(); ++it) {
        QString s = *it;
        int pos = s.findRev('.');
        if (pos != -1)
            s = s.left(pos) + ".h";
        if (s.right(header.length()) == header)
            return s;
    }

    return QString::null;
}


void CppSupportPart::contextMenu(QPopupMenu *popup, const Context *context)
{
	m_activeClass = 0;
	m_activeFunction = 0;
	m_activeVariable = 0;
	m_curAttribute = 0;
	m_curClass = 0;
	
	if( context->hasType(Context::EditorContext) )
	{
		int id;
	
		popup->insertItem( "Switch header/implementation", this, SLOT( slotSwitchHeader() ) );
		popup->setWhatsThis( id, i18n("<b>Switch Header/Implementation</b><p>"
										"If you are currently looking at a header file, this "
										"brings you to the corresponding implementation file. "
										"If you are looking at an implementation file (.cpp etc.), "
										"this brings you to the corresponding header file.") );

		// 	CodeModelItemContext
		if(context->type() == Context::EditorContext)
		{
			m_curClass = currentClass();
			if (m_curClass != 0)
			{
				m_curAttribute = currentAttribute(m_curClass);
				if ( m_curAttribute != 0)
					m_createGetterSetterAction->plug(popup);
			}
		}
		
		QString text;
		int atline, atcol;
		MakeMemberHelper(text,atline,atcol);
		if(!text.isEmpty())
		{
			id = popup->insertItem( i18n( "Make Member"), this, SLOT( slotMakeMember() ) );
			popup->setWhatsThis( id, i18n("<b>Make member</b><p>Creates a class member function in implementation file "
												"based on the member declaration at the current line."));
		}
			
		kdDebug(9007) << "======> code model has the file: " << m_activeFileName << " = " << codeModel()->hasFile( m_activeFileName ) << endl;
		
		bool showContextMenuExplosion = false;
		KConfig *config = CppSupportFactory::instance()->config();
		if (config)
		{
			config->setGroup("General");
			showContextMenuExplosion = config->readBoolEntry("ShowContextMenuExplosion", false );
		}

		if( showContextMenuExplosion && codeModel()->hasFile(m_activeFileName) )
		{
			//kdDebug() << "CppSupportPart::contextMenu 1" << endl;
			QString candidate;
			if ( isSource(m_activeFileName) )
				candidate = sourceOrHeaderCandidate();
			else
				candidate = m_activeFileName;
			
			unsigned int curLine = 0, curCol = 0;
			if (m_activeViewCursor != 0)
				m_activeViewCursor->cursorPosition(&curLine, &curCol);
			
			//kdDebug() << "CppSupportPart::contextMenu 2: candidate: " << candidate << endl;
			
			if ( !candidate.isEmpty() && codeModel()->hasFile(candidate) )
			{
				QPopupMenu* m2 = new QPopupMenu( popup );
				id = popup->insertItem( i18n("Go to Declaration"), m2 );
				popup->setWhatsThis(id, i18n("<b>Go to declaration</b><p>Provides a menu to select available function declarations "
					"in the current file and in the corresponding header (if the current file is an implementation) or source (if the current file is a header) file."));
	
				FileDom file2 = codeModel()->fileByName( candidate );
				//kdDebug() << "CppSupportPart::contextMenu 3: " << file2->name() << endl;
	
				FunctionList functionList2 = CodeModelUtils::allFunctions(file2);
				for( FunctionList::ConstIterator it=functionList2.begin(); it!=functionList2.end(); ++it )
				{
					QString text = (*it)->scope().join( "::");
					//kdDebug() << "CppSupportPart::contextMenu 3 text: " << text << endl;
					if( !text.isEmpty() )
					{
						text += "::";
					}
					text += formatModelItem( *it, true );
					text = text.replace( QString::fromLatin1("&"), QString::fromLatin1("&&") );
					int id = m2->insertItem( text, this, SLOT(gotoDeclarationLine(int)) );
					int line, column;
					(*it)->getStartPosition( &line, &column );
					m2->setItemParameter( id, line );
				}
				
				if(m2->count() == 0)
				{
					popup->removeItem(id);
				}
				//kdDebug() << "CppSupportPart::contextMenu 4" << endl;
			}

			QString candidate1;
			if ( isHeader(m_activeFileName) )
			{
				candidate1 = sourceOrHeaderCandidate();
			}
			else
			{
				candidate1 = m_activeFileName;
			}
			//kdDebug() << "CppSupportPart::go to definition in " << candidate1 << endl;
			if( codeModel()->hasFile(candidate1) )
			{
				QPopupMenu* m = new QPopupMenu( popup );
				id = popup->insertItem( i18n("Go to Definition"), m );
				popup->setWhatsThis(id, i18n("<b>Go to definition</b><p>Provides a menu to select available function definitions "
						"in the current file and in the corresponding header (if the current file is an implementation) or source (if the current file is a header) file."));
	
				const FileDom file = codeModel()->fileByName( candidate1 );
				const FunctionDefinitionList functionDefinitionList = CodeModelUtils::allFunctionDefinitionsDetailed(file).functionList;
				for( FunctionDefinitionList::ConstIterator it=functionDefinitionList.begin(); it!=functionDefinitionList.end(); ++it )
				{
					QString text = (*it)->scope().join( "::");
					if( !text.isEmpty() )
					{
						text += "::";
					}
					text += formatModelItem( *it, true );
					text = text.replace( QString::fromLatin1("&"), QString::fromLatin1("&&") );
					int id = m->insertItem( text, this, SLOT(gotoLine(int)) );
					int line, column;
					(*it)->getStartPosition( &line, &column );
					m->setItemParameter( id, line );
				}
				if(m->count() == 0)
				{
					popup->removeItem(id);
				}
				
			}
		}

		const EditorContext *econtext = static_cast<const EditorContext*>(context);
		QString str = econtext->currentLine();
		if (str.isEmpty()) return;
	
		QRegExp re("[ \t]*#include[ \t]*[<\"](.*)[>\"][ \t]*");
		if (!re.exactMatch(str)) return;
	
		QString popupstr = re.cap(1);
		m_contextFileName = findHeader(m_projectFileList, popupstr);
		if (m_contextFileName.isEmpty()) return;
		
		id = popup->insertItem( i18n("Goto Include File: %1").arg(popupstr), this, SLOT(slotGotoIncludeFile()) );
		popup->setWhatsThis(id, i18n("<b>Goto include file</b><p>Opens an include file under the cursor position."));

	} 
	else if( context->hasType(Context::CodeModelItemContext) )
	{
		const CodeModelItemContext* mcontext = static_cast<const CodeModelItemContext*>( context );
	
		if( mcontext->item()->isClass() )
		{
			m_activeClass = (ClassModel*) mcontext->item();
			int id = popup->insertItem( i18n("Extract Interface..."), this, SLOT(slotExtractInterface()) );
			popup->setWhatsThis(id, i18n("<b>Extract interface</b><p>Extracts interface from the selected class and creates a new class with this interface. "
				"No implementation code is extracted and no implementation code is created."));
		} 
		else if( mcontext->item()->isFunction() )
		{
			m_activeFunction = (FunctionModel*) mcontext->item();
		}
	}
	else if (context->hasType(Context::FileContext)){
		const FileContext *fc = static_cast<const FileContext*>(context);
		//this is a .ui file and only selection contains only one such file
		KURL url = fc->urls().first();
		kdDebug() << "file context with " << url.path() << endl;
		if (url.fileName().endsWith(".ui"))
		{
			m_contextFileName = url.path();
			int id = popup->insertItem(i18n("Create or Select Implementation..."), this, SLOT(slotCreateSubclass()));
			popup->setWhatsThis(id, i18n("<b>Create or select implementation</b><p>Creates or selects a subclass of selected form for use with integrated KDevDesigner."));
		}
	}
}


// Makes sure that header files come first
QStringList CppSupportPart::reorder(const QStringList &list)
{
    QStringList headers, others;

    QStringList headerExtensions = QStringList::split(",", "h,H,hh,hxx,hpp,tlh");

    QStringList::ConstIterator it;
    for (it = list.begin(); it != list.end(); ++it){
	QString fileName = *it;
        if (headerExtensions.contains(QFileInfo(*it).extension()))
            headers << (*it);
        else
            others << (*it);
    }

    return headers + others;
}

void CppSupportPart::addedFilesToProject(const QStringList &fileList)
{
    m_projectFileList = project()->allFiles();
    QStringList files = reorder( fileList );

    for ( QStringList::ConstIterator it = files.begin(); it != files.end(); ++it )
    {
	QString path = URLUtil::canonicalPath( m_projectDirectory + "/" + (*it) );

	maybeParse( path );
	emit addedSourceInfo( path );
    }
}

void CppSupportPart::removedFilesFromProject(const QStringList &fileList)
{
    m_projectFileList = project()->allFiles();
    for ( QStringList::ConstIterator it = fileList.begin(); it != fileList.end(); ++it )
    {
	QString path = URLUtil::canonicalPath( m_projectDirectory + "/" + *it );
        kdDebug(9007) << "=====================> remove file: " << path << endl;

	removeWithReferences( path );
	m_backgroundParser->removeFile( path );
    }
}

void CppSupportPart::changedFilesInProject( const QStringList & fileList )
{
    QStringList files = reorder( fileList );

    for ( QStringList::ConstIterator it = files.begin(); it != files.end(); ++it )
    {
	QString path = URLUtil::canonicalPath( m_projectDirectory + "/" + *it );

	maybeParse( path );
	emit addedSourceInfo( path );
    }
}

void CppSupportPart::savedFile(const KURL &fileName)
{
    Q_UNUSED( fileName.path() );

#if 0  // not needed anymore
    kdDebug(9007) << "savedFile(): " << fileName.mid ( m_projectDirectory.length() + 1 ) << endl;

    if (m_projectFileList.contains(fileName.mid ( m_projectDirectory.length() + 1 ))) {
	maybeParse( fileName );
	emit addedSourceInfo( fileName );
    }
#endif
}

QString CppSupportPart::findSourceFile()
{
    // get the path of the currently active document
    QFileInfo fi( m_activeFileName );
    QString path = fi.filePath();
    QString ext = fi.extension();
    // extract the base path (full path without '.' and extension)
    QString base = path.left( path.length() - ext.length() - 1);
    QStringList candidates;

    if (QStringList::split(',', "h,H,hh,hxx,hpp,tlh").contains(ext)) {
        candidates << (base + ".c");
        candidates << (base + ".cc");
        candidates << (base + ".cpp");
        candidates << (base + ".c++");
        candidates << (base + ".cxx");
        candidates << (base + ".C");
        candidates << (base + ".m");
        candidates << (base + ".mm");
        candidates << (base + ".M");
        candidates << (base + ".inl");
        candidates << (base + "_impl.h");        
    } 
    
    QStringList::ConstIterator it;
    for (it = candidates.begin(); it != candidates.end(); ++it) {
        kdDebug(9007) << "Trying " << (*it) << endl;
        if (QFileInfo(*it).exists()) {
            return *it;
        }
    }
    return m_activeFileName;
}

QString CppSupportPart::sourceOrHeaderCandidate()
{
    KTextEditor::Document *doc = dynamic_cast<KTextEditor::Document*>(partController()->activePart());
    if (!doc)
      return QString::null;

    // get the path of the currently active document
    QFileInfo fi(doc->url().path());
    QString path = fi.filePath();
    // extract the exension 
    QString ext = fi.extension();
    if (ext.isEmpty()) return QString::null;
    // extract the base path (full path without '.' and extension)
    QString base = path.left(path.length()-ext.length() - 1);
    kdDebug(9007) << "base: " << base << ", ext: " << ext << endl;
    // depending on the current extension assemble a list of 
    // candidate files to look for
    QStringList candidates;
    // special case for template classes created by the new class dialog
    if (path.endsWith("_impl.h")) {
        QString headerpath = path;
        headerpath.replace("_impl.h",".h");
        candidates << headerpath;
    }
    // if file is a header file search for implementation file
    else if (QStringList::split(',', "h,H,hh,hxx,hpp,tlh").contains(ext)) {
        candidates << (base + ".c");
        candidates << (base + ".cc");
        candidates << (base + ".cpp");
        candidates << (base + ".c++");
        candidates << (base + ".cxx");
        candidates << (base + ".C");
        candidates << (base + ".m");
        candidates << (base + ".mm");
        candidates << (base + ".M");
        candidates << (base + ".inl");
        candidates << (base + "_impl.h");        
    } 
    // if file is an implementation file, search for header file
    else if (QStringList::split(',', "c,cc,cpp,c++,cxx,C,m,mm,M,inl").contains(ext)) {
        candidates << (base + ".h");
        candidates << (base + ".H");
        candidates << (base + ".hh");
        candidates << (base + ".hxx");
        candidates << (base + ".hpp");
        candidates << (base + ".tlh");
    }
    // search for files from the assembled candidate lists, return the first 
    // candidate file that actually exists or QString::null if nothing is found.
    QStringList::ConstIterator it;
    for (it = candidates.begin(); it != candidates.end(); ++it) {
        kdDebug(9007) << "Trying " << (*it) << endl;
        if (QFileInfo(*it).exists()) {
            return *it;
        }
    }
    return QString::null;
}

void CppSupportPart::slotSwitchHeader()
{
	QString candidate = sourceOrHeaderCandidate();
	if	( candidate == QString::null ) return;
	
	bool attemptMatch = true;
	KConfig *config = CppSupportFactory::instance()->config();
	if (config)
	{
		config->setGroup("General");
		attemptMatch = config->readBoolEntry("SwitchShouldMatch", true );
	}

	// ok, both files exist. Do the codemodel have them?
	if ( codeModel()->hasFile( m_activeFileName ) && codeModel()->hasFile( candidate ) && m_activeViewCursor && attemptMatch )
	{
		unsigned int currentline, column;
		m_activeViewCursor->cursorPosition( &currentline, &column );
		
		if ( isHeader( m_activeFileName ) )
		{
			// we're in the header file, let's find the current function declaration
			FileDom header = codeModel()->fileByName( m_activeFileName );
			FunctionList functionList = CodeModelUtils::allFunctionsDetailed( header ).functionList;
			
			FunctionList::ConstIterator it_decl = functionList.begin();
			while( it_decl != functionList.end() )
			{
				int startline, column, endLine, endColumn;
				(*it_decl)->getStartPosition( &startline, &column );
				(*it_decl)->getEndPosition(&endLine, &endColumn);
				if ( currentline >= startline && currentline <= endLine )
				{
					// found it. can we find a matching defintion?
					FileDom source = codeModel()->fileByName( candidate );
					FunctionDefinitionList functionDefList = CodeModelUtils::allFunctionDefinitionsDetailed( source ).functionList;
					for ( FunctionDefinitionList::ConstIterator it_def = functionDefList.begin(); it_def != functionDefList.end(); ++it_def )
					{
						if ( CodeModelUtils::compareDeclarationToDefinition( *it_decl, *it_def ) )
						{
							// found the declaration, let's jump!
							int line, column;
							(*it_def)->getStartPosition( &line, &column );
							KURL url;
							url.setPath( candidate );
							partController()->editDocument( url, line );
							return;
						}
					}
					break;
				}
				++it_decl;
			}
		}
		else if ( isSource( m_activeFileName ) )
		{
			// we're in the source file, let's find the current function definition
			FileDom header = codeModel()->fileByName( m_activeFileName );
			FunctionDefinitionList functionDefList = CodeModelUtils::allFunctionDefinitionsDetailed( header ).functionList;
			
			FunctionDefinitionList::ConstIterator it_def = functionDefList.begin(); 
			while ( it_def != functionDefList.end())
			{
				int startline, column, endLine, endColumn;
				(*it_def)->getStartPosition( &startline, &column );
				(*it_def)->getEndPosition(&endLine, &endColumn);
				if ( currentline >= startline && currentline <= endLine )
				{
					// found it. can we find a matching declaration?
					FileDom source = codeModel()->fileByName( candidate );
					FunctionList functionList = CodeModelUtils::allFunctionsDetailed( source ).functionList;
					for ( FunctionList::ConstIterator it_decl = functionList.begin(); it_decl != functionList.end(); ++it_decl )
					{
						if ( CodeModelUtils::compareDeclarationToDefinition( *it_decl, *it_def ) )
						{
							// found the declaration, let's jump!
							int line, column;
							(*it_decl)->getStartPosition( &line, &column );
							KURL url;
							url.setPath( candidate );
							partController()->editDocument( url, line );
							return;
						}
					}
					break;
				}
				++it_def;
			}
		}
	}
	
	// last chance
	KURL url;
	url.setPath( candidate );
	partController()->editDocument( url );
}

void CppSupportPart::slotGotoIncludeFile()
{
    if (!m_contextFileName.isEmpty())
        partController()->editDocument(KURL( m_contextFileName ), 0);

}

KDevLanguageSupport::Features CppSupportPart::features()
{
    if (withcpp)
        return Features(Classes | Structs | Functions | Variables | Namespaces | Declarations
                        | Signals | Slots | AddMethod | AddAttribute | NewClass | CreateAccessMethods);
    else
        return Features (Structs | Functions | Variables | Declarations);
}

QString CppSupportPart::formatClassName(const QString &name)
{
    QString n = name;
    return n.replace(".", "::");
}

QString CppSupportPart::unformatClassName(const QString &name)
{
    QString n = name;
    return n.replace("::", ".");
}

void CppSupportPart::slotNewClass()
{
    CppNewClassDialog dlg(this);
    dlg.exec();
}

void CppSupportPart::addMethod( ClassDom klass )
{
    if( !klass ){
	KMessageBox::error(0,i18n("Please select a class."),i18n("Error"));
	return;
    }

    AddMethodDialog dlg( this, klass, mainWindow()->main() );
    dlg.exec();
}

void CppSupportPart::addAttribute( ClassDom klass )
{
    if( !klass ){
	KMessageBox::error(0,i18n("Please select a class."),i18n("Error"));
	return;
    }

    AddAttributeDialog dlg( this, klass, mainWindow()->main() );
    dlg.exec();
}

void CppSupportPart::slotCompleteText()
{
    if (!m_pCompletion)
        return;
    m_pCompletion->completeText();
}

/**
 * parsing stuff for project persistant classstore and code completion
 */
void CppSupportPart::initialParse( )
{
    // For debugging
    if( !project( ) ){
        // messagebox ?
        kdDebug( 9007 ) << "No project" << endl;
        return;
    }

    parseProject( );
    m_valid = true;
    return;
}

#if QT_VERSION < 0x030100
// Taken from qt-3.2/tools/qdatetime.cpp/QDateTime::toTime_t() and modified for normal function
uint toTime_t(QDateTime t)
{
    tm brokenDown;
    brokenDown.tm_sec = t.time().second();
    brokenDown.tm_min = t.time().minute();
    brokenDown.tm_hour = t.time().hour();
    brokenDown.tm_mday = t.date().day();
    brokenDown.tm_mon = t.date().month() - 1;
    brokenDown.tm_year = t.date().year() - 1900;
    brokenDown.tm_isdst = -1;
    int secsSince1Jan1970UTC = (int) mktime( &brokenDown );
    if ( secsSince1Jan1970UTC < -1 )
    secsSince1Jan1970UTC = -1;
    return (uint) secsSince1Jan1970UTC;
}
#endif

bool
CppSupportPart::parseProject( bool force )
{
        kdDebug( 9007 ) << "CppSupportPart::parseProject 1" << endl;
    mainWindow()->statusBar()->message( i18n("Updating...") );

    kapp->setOverrideCursor( waitCursor );

	_jd = new JobData;
	_jd->file.setName( project()->projectDirectory() + "/" + project()->projectName() + ".pcs" );

	QString skip_file_name = project()->projectDirectory() + "/" + project()->projectName() + ".ignore_pcs";

    if( !force && !QFile::exists( skip_file_name ) && _jd->file.open(IO_ReadOnly) )
	{
		_jd->stream.setDevice( &(_jd->file) );
	
		createIgnorePCSFile();
	
		QString sig;
		int pcs_version = 0;
		_jd->stream >> sig >> pcs_version;
		if( sig == "PCS" && pcs_version == KDEV_PCS_VERSION ){
	
			int numFiles = 0;
			_jd->stream >> numFiles;
	
			for( int i=0; i<numFiles; ++i ){
			QString fn;
			uint ts;
			uint offset;
	
			_jd->stream >> fn >> ts >> offset;
			_jd->pcs[ fn ] = qMakePair( ts, offset );
			}
		}
    }
        kdDebug( 9007 ) << "CppSupportPart::parseProject 2" << endl;

	_jd->files = reorder( modifiedFileList() );
        kdDebug( 9007 ) << "CppSupportPart::parseProject 3" << endl;

    QProgressBar* bar = new QProgressBar( _jd->files.count( ), mainWindow( )->statusBar( ) );
    bar->setMinimumWidth( 120 );
    bar->setCenterIndicator( true );
    mainWindow( )->statusBar( )->addWidget( bar );
    bar->show( );
        kdDebug( 9007 ) << "CppSupportPart::parseProject 4" << endl;
	
	_jd->progressBar = bar;
	_jd->dir.setPath( m_projectDirectory );
	_jd->it = _jd->files.begin();

        kdDebug( 9007 ) << "CppSupportPart::parseProject 5" << endl;
	QTimer::singleShot( 0, this, SLOT(slotParseFiles()) );
	
	return true;
}

void CppSupportPart::slotParseFiles()
{
	// NOTE: The checking for m_projectClosed is actually (currently) not needed.
	// When the project is closed, the language support plugin is destroyed
	// and as a consequence, the timer job signal never arrives at this method
	
	if ( !m_projectClosed && _jd->it != _jd->files.end() )
	{
		_jd->progressBar->setProgress( _jd->progressBar->progress() +1 );
		
		QFileInfo fileInfo( _jd->dir, *(_jd->it) );
		
		if( fileInfo.exists() && fileInfo.isFile() && fileInfo.isReadable() )
		{
			QString absFilePath = URLUtil::canonicalPath(fileInfo.absFilePath() );
		
			if( isValidSource(absFilePath) )
			{
				QDateTime t = fileInfo.lastModified();
				if( ! (m_timestamp.contains(absFilePath) && m_timestamp[absFilePath] == t ) )
				{	
					if( _jd->pcs.contains(absFilePath) && t.toTime_t() == _jd->pcs[absFilePath].first )
					{
						_jd->stream.device()->at( _jd->pcs[absFilePath].second );
						FileDom file = codeModel()->create<FileModel>();
						file->read( _jd->stream );
						codeModel()->addFile( file );
					} 
					else 
					{
						kdDebug(9007) << "newly parsing: " << absFilePath << endl;
						m_driver->parseFile( absFilePath );
					}
			
					m_timestamp[ absFilePath ] = t;
				}
			}
		}
		
		++(_jd->it);
		QTimer::singleShot( 0, this, SLOT(slotParseFiles()) );
	}
	else // finished or interrupted
	{
		kapp->restoreOverrideCursor( );
		mainWindow( )->statusBar( )->removeWidget( _jd->progressBar );
			
		if ( !m_projectClosed )
		{
			kdDebug( 9007 ) << "updating sourceinfo" << endl;
			emit updatedSourceInfo();
			mainWindow( )->statusBar( )->message( i18n( "Done" ), 2000 );
			QFile::remove( project()->projectDirectory() + "/" + project()->projectName() + ".ignore_pcs" );
		}
		else
		{
			kdDebug(9007) << "ABORT" << endl;
		}
		
		delete _jd;
		_jd = 0;
	}
}

void CppSupportPart::maybeParse( const QString& fileName )
{
    if( !isValidSource(fileName) )
        return;

    QFileInfo fileInfo( fileName );
    QString path = URLUtil::canonicalPath(fileName);
    QDateTime t = fileInfo.lastModified();

    if( !fileInfo.exists() ){
	removeWithReferences( path );
	return;
    }

    QMap<QString, QDateTime>::Iterator it = m_timestamp.find( path );
    if( it != m_timestamp.end() && *it == t ){
	return;
    }

    m_timestamp[ path ] = t;
    m_driver->parseFile( path );
}

void CppSupportPart::slotNeedTextHint( int line, int column, QString& textHint )
{
    if( 1 || !m_activeEditor )
	return;

    m_backgroundParser->lock();
    TranslationUnitAST* ast = m_backgroundParser->translationUnit( m_activeFileName );
    AST* node = 0;
    if( ast && (node = findNodeAt(ast, line, column)) ){

	while( node && node->nodeType() != NodeType_FunctionDefinition )
	    node = node->parent();

	if( node ){
	    int startLine, startColumn;
	    int endLine, endColumn;
	    node->getStartPosition( &startLine, &startColumn );
	    node->getEndPosition( &endLine, &endColumn );

	    if( !node->text().isNull() )
	        textHint = node->text();
	    else
	        textHint = m_activeEditor->textLine( startLine ).simplifyWhiteSpace();
	}
    }
    m_backgroundParser->unlock();
}

void CppSupportPart::MakeMemberHelper(QString& text, int& atLine, int& atColumn)
{
    if( !m_activeViewCursor || !m_valid )
        return;

    m_backgroundParser->lock();
    TranslationUnitAST* translationUnit = m_backgroundParser->translationUnit( m_activeFileName );
    if( translationUnit ){
        unsigned int line, column;
	m_activeViewCursor->cursorPositionReal( &line, &column );

        AST* currentNode = findNodeAt( translationUnit, line, column );
	DeclaratorAST* declarator = 0;
	while( currentNode && currentNode->nodeType() != NodeType_SimpleDeclaration ){
	    if( currentNode->nodeType() == NodeType_Declarator )
	        declarator = (DeclaratorAST*) currentNode;
	    currentNode = currentNode->parent();
	}
	SimpleDeclarationAST* decl = currentNode ? (SimpleDeclarationAST*) currentNode : 0;
	if( decl && decl->initDeclaratorList() && !declarator ){
	    InitDeclaratorAST* i = decl->initDeclaratorList()->initDeclaratorList().at( 0 );
	    if( i )
	       declarator = i->declarator();
	}

	if( decl && declarator && declarator->parameterDeclarationClause() ){

	    QStringList scope;
	    scopeOfNode( decl, scope );

	    QString scopeStr = scope.join( "::" );
	    if( !scopeStr.isEmpty() )
	        scopeStr += "::";

	    QString declStr = declaratorToString( declarator, scopeStr ).simplifyWhiteSpace();
	    if( declarator->exceptionSpecification() ){
		declStr += QString::fromLatin1( " throw( ");
		QPtrList<AST> l = declarator->exceptionSpecification()->nodeList();
		QPtrListIterator<AST> type_it( l );
		while( type_it.current() ){
		    declStr += type_it.current()->text();
		    ++type_it;

		    if( type_it.current() )
			declStr += QString::fromLatin1( ", " );
		}

		declStr += QString::fromLatin1(" )");
	    }

	    text += "\n\n";
	    QString type = typeSpecToString( decl->typeSpec() );
	    text += type;
	    if( !type.isNull() )
		text +=  + " ";

	    text += declStr + "\n{\n}";
	}
 
        m_backgroundParser->unlock();
        
        QString implFile = findSourceFile();
        
        m_backgroundParser->lock();
	translationUnit = m_backgroundParser->translationUnit( implFile );
	if( translationUnit ){
	    translationUnit->getEndPosition( &atLine, &atColumn );
	} else {
	    atLine = -2;
	    atColumn = 0;
	}
         kdDebug() << "at line in mm: " << atLine << endl;
    }
    m_backgroundParser->unlock();
}

void CppSupportPart::slotMakeMember()
{
QString text;
int atColumn, atLine;
MakeMemberHelper(text,atLine,atColumn);

if(!text.isEmpty())
{ 
 QString implFile = findSourceFile();

 if(!implFile.isEmpty() ){
    partController()->editDocument( KURL( implFile ) );
    kapp->processEvents( 500 );
 }
 if (atLine == -2)
    atLine = m_activeEditor->numLines() - 1;

 m_backgroundParser->lock();

         kdDebug() << "at line in mm: " << atLine  << " atCol: " << atColumn << endl;
         kdDebug() << "text: " << text << endl;
 if( m_activeEditor )
  m_activeEditor->insertText( atLine, atColumn, text );
 if( m_activeViewCursor )
  m_activeViewCursor->setCursorPositionReal( atLine+3, 1 );

 m_backgroundParser->unlock();
 }
}

QStringList CppSupportPart::subclassWidget(const QString& formName)
{
    QStringList newFileNames;
    SubclassingDlg *dlg = new SubclassingDlg(this, formName, newFileNames);
    dlg->exec();
    return newFileNames;
}

QStringList CppSupportPart::updateWidget(const QString& formName, const QString& fileName)
{
    QStringList dummy;
    SubclassingDlg *dlg = new SubclassingDlg(this, formName, fileName, dummy);
    dlg->exec();
    return dummy;
}

void CppSupportPart::partRemoved( KParts::Part* part )
{
    kdDebug(9032) << "CppSupportPart::partRemoved()" << endl;

    if( KTextEditor::Document* doc = dynamic_cast<KTextEditor::Document*>( part ) ){

	QString fileName = doc->url().path();
	if( !isValidSource(fileName) )
	    return;

	QString canonicalFileName = URLUtil::canonicalPath( fileName );
	m_backgroundParser->removeFile( canonicalFileName );
	m_backgroundParser->addFile( canonicalFileName, true );
    }
}

void CppSupportPart::slotProjectCompiled()
{
    kdDebug(9007) << "CppSupportPart::slotProjectCompiled()" << endl;
    parseProject();
}

QStringList CppSupportPart::modifiedFileList()
{
    QStringList lst;

    QStringList fileList = m_projectFileList;
    QStringList::Iterator it = fileList.begin();
    while( it != fileList.end() ){
	QString fileName = *it;
	++it;

	QFileInfo fileInfo( m_projectDirectory, fileName );
	QString path = URLUtil::canonicalPath(fileInfo.absFilePath());

	if( !(isSource(path) || isHeader(path)) )
	    continue;

	QDateTime t = fileInfo.lastModified();

	QMap<QString, QDateTime>::Iterator dictIt = m_timestamp.find( path );
	if( fileInfo.exists() && dictIt != m_timestamp.end() && *dictIt == t )
	    continue;

	lst << fileName;
    }

    return lst;
}

KTextEditor::Document * CppSupportPart::findDocument( const KURL & url )
{
    if( !partController()->parts() )
        return 0;

    QPtrList<KParts::Part> parts( *partController()->parts() );
    QPtrListIterator<KParts::Part> it( parts );
    while( KParts::Part* part = it.current() ){
        KTextEditor::Document* doc = dynamic_cast<KTextEditor::Document*>( part );
	if( doc && doc->url() == url )
	    return doc;
        ++it;
    }

    return 0;
}

void CppSupportPart::setupCatalog( )
{
    kdDebug(9007) << "CppSupportPart::setupCatalog()" << endl;

    KStandardDirs *dirs = CppSupportFactory::instance()->dirs();
    QStringList pcsList = dirs->findAllResources( "pcs", "*.db", false, true );
    QStringList pcsIdxList = dirs->findAllResources( "pcs", "*.idx", false, true );

    QStringList enabledPCSs;
    if( DomUtil::elementByPath( *project()->projectDom(), "kdevcppsupport/references" ).isNull() ){
        for( QStringList::Iterator it=pcsList.begin(); it!=pcsList.end(); ++it ){
            enabledPCSs.push_back( QFileInfo(*it).baseName() );
        }
    } else {
        enabledPCSs = DomUtil::readListEntry( *project()->projectDom(), "kdevcppsupport/references", "pcs" );
    }

    QStringList indexList = QStringList() << "kind" << "name" << "scope" << "fileName" << "prefix";

    if( pcsList.size() && pcsVersion() < KDEV_DB_VERSION ){
        QStringList l = pcsList + pcsIdxList;
        int rtn = KMessageBox::questionYesNoList( 0, i18n("Persistant class store will be disabled: you have a wrong version of pcs installed.\nRemove old pcs files?"), l, i18n("C++ Support") );
        if( rtn == KMessageBox::Yes ){
            QStringList::Iterator it = l.begin();
            while( it != l.end() ){
                QFile::remove( *it );
                ++it;
            }
            // @todo regenerate the pcs list
            pcsList.clear();
        } else {
            return;
        }
    }

    QStringList::Iterator it = pcsList.begin();
    while( it != pcsList.end() ){
        Catalog* catalog = new Catalog();
        catalog->open( *it );
	catalog->setEnabled( enabledPCSs.contains(QFileInfo(*it).baseName()) );
        ++it;

        for( QStringList::Iterator idxIt=indexList.begin(); idxIt!=indexList.end(); ++idxIt )
            catalog->addIndex( (*idxIt).utf8() );

        m_catalogList.append( catalog );
        codeRepository()->registerCatalog( catalog );
    }

    setPcsVersion( KDEV_DB_VERSION );
}

KMimeType::List CppSupportPart::mimeTypes( )
{
    QStringList mimeList;
    mimeList += m_headerMimeTypes;
    mimeList += m_sourceMimeTypes;

    KMimeType::List list;
    for( QStringList::Iterator it=mimeList.begin(); it!=mimeList.end(); ++it )
    {
	if( KMimeType::Ptr mime = KMimeType::mimeType(*it) )
	    list << mime;
    }

    return list;
}

int CppSupportPart::pcsVersion()
{
    KConfig* config = CppSupportFactory::instance()->config();
    KConfigGroupSaver cgs( config, "PCS" );
    return config->readNumEntry( "Version", 0 );
}

void CppSupportPart::setPcsVersion( int version )
{
    KConfig* config = CppSupportFactory::instance()->config();
    KConfigGroupSaver cgs( config, "PCS" );
    config->writeEntry( "Version", version );
    config->sync();
}

QString CppSupportPart::formatTag( const Tag & inputTag )
{
    Tag tag = inputTag;

    switch( tag.kind() )
    {
        case Tag::Kind_Namespace:
            return QString::fromLatin1("namespace ") + tag.name();

        case Tag::Kind_Class:
            return QString::fromLatin1("class ") + tag.name();

        case Tag::Kind_Function:
        case Tag::Kind_FunctionDeclaration:
        {
            CppFunction<Tag> tagInfo( tag );
            return tagInfo.name() + "( " + tagInfo.arguments().join(", ") + " ) : " + tagInfo.type();
        }
        break;

        case Tag::Kind_Variable:
        case Tag::Kind_VariableDeclaration:
        {
            CppVariable<Tag> tagInfo( tag );
            return tagInfo.name() + " : " + tagInfo.type();
        }
        break;
    }
    return tag.name();
}

void CppSupportPart::codeCompletionConfigStored( )
{
    partController()->setActivePart( partController()->activePart() );
}

void CppSupportPart::removeWithReferences( const QString & fileName )
{
    kdDebug(9007) << "remove with references: " << fileName << endl;
    m_timestamp.remove( fileName );
    if( !codeModel()->hasFile(fileName) )
        return;

    emit aboutToRemoveSourceInfo( fileName );

    codeModel()->removeFile( codeModel()->fileByName(fileName) );
}

bool CppSupportPart::isValidSource( const QString& fileName ) const
{
    QFileInfo fileInfo( fileName );
    QString path = URLUtil::canonicalPath( fileInfo.absFilePath() );

    return project() && project()->isProjectFile( path )
	&& (isSource( path ) || isHeader( path ))
	&& !QFile::exists(fileInfo.dirPath(true) + "/.kdev_ignore");
}

QString CppSupportPart::formatModelItem( const CodeModelItem *item, bool shortDescription )
{
    if (item->isFunction() || item->isFunctionDefinition() )
    {
        const FunctionModel *model = static_cast<const FunctionModel*>(item);
        QString function;
        QString args;
        ArgumentList argumentList = model->argumentList();
        for (ArgumentList::const_iterator it = argumentList.begin(); it != argumentList.end(); ++it)
        {
            args.isEmpty() ? args += "" : args += ", " ;
            args += formatModelItem((*it).data());
        }
	if( !shortDescription )
            function += (model->isVirtual() ? QString("virtual ") : QString("") ) + model->resultType() + " ";

	function += model->name() + "(" + args + ")" + (model->isConstant() ? QString(" const") : QString("") ) +
            (model->isAbstract() ? QString(" = 0") : QString("") );

        return function;
    }
    else if (item->isVariable())
    {
        const VariableModel *model = static_cast<const VariableModel*>(item);
	if( shortDescription )
	    return model->name();
        return model->type() + " " + model->name();
    }
    else if (item->isArgument())
    {
        const ArgumentModel *model = static_cast<const ArgumentModel*>(item);
	QString arg;
	if( !shortDescription )
	    arg += model->type() + " ";
	arg += model->name();
	if( !shortDescription )
	    arg += model->defaultValue().isEmpty() ? QString("") : QString(" = ") + model->defaultValue();
	return arg.stripWhiteSpace();
    }
    else
        return KDevLanguageSupport::formatModelItem( item, shortDescription );
}

void CppSupportPart::addClass( )
{
    slotNewClass();
}

void CppSupportPart::saveProjectSourceInfo( )
{
    const FileList fileList = codeModel()->fileList();

    if( !project() || fileList.isEmpty() )
	return;

    QFile f( project()->projectDirectory() + "/" + project()->projectName() + ".pcs" );
    if( !f.open( IO_WriteOnly ) )
	return;

    createIgnorePCSFile();

    QDataStream stream( &f );
    QMap<QString, uint> offsets;

    QString pcs( "PCS" );
    stream << pcs << KDEV_PCS_VERSION;

    stream << int( fileList.size() );
    for( FileList::ConstIterator it=fileList.begin(); it!=fileList.end(); ++it ){
	const FileDom dom = (*it);
#if QT_VERSION >= 0x030100
  stream << dom->name() << m_timestamp[ dom->name() ].toTime_t();
#else
  stream << dom->name() << toTime_t(m_timestamp[ dom->name() ]);
#endif
	offsets.insert( dom->name(), stream.device()->at() );
	stream << (uint)0; // dummy offset
    }

    for( FileList::ConstIterator it=fileList.begin(); it!=fileList.end(); ++it ){
	const FileDom dom = (*it);
	int offset = stream.device()->at();

	dom->write( stream );

	int end = stream.device()->at();

	stream.device()->at( offsets[dom->name()] );
	stream << offset;
	stream.device()->at( end );
    }

    QString skip_file_name = project()->projectDirectory() + "/" + project()->projectName() + ".ignore_pcs";
    QFile::remove( skip_file_name );
}

QString CppSupportPart::extractInterface( const ClassDom& klass )
{
    QString txt;
    QTextStream stream( &txt, IO_WriteOnly );

    QString name = klass->name() + "Interface";
    QString ind;
    ind.fill( QChar(' '), 4 );

    stream
	<< "class " << name << "\n"
	<< "{" << "\n"
	<< "public:" << "\n"
	<< ind << name << "() {}" << "\n"
	<< ind << "virtual ~" << name << "() {}" << "\n"
	<< "\n";

    const FunctionList functionList = klass->functionList();
    for( FunctionList::ConstIterator it=functionList.begin(); it!=functionList.end(); ++it ){
	const FunctionDom& fun = *it;

	if( !fun->isVirtual() || fun->name().startsWith("~") )
	    continue;

	stream << ind << formatModelItem( fun );
	if( !fun->isAbstract() )
	    stream << " = 0";

	stream << ";\n";
    }

    stream
	<< "\n"
	<< "private:" << "\n"
	<< ind << name << "( const " << name << "& source );" << "\n"
	<< ind << "void operator = ( const " << name << "& source );" << "\n"
	<< "};" << "\n\n";

    return txt;
}

void CppSupportPart::slotExtractInterface( )
{
    if( !m_activeClass )
	return;

    QFileInfo fileInfo( m_activeClass->fileName() );
    QString ifaceFileName = fileInfo.dirPath( true ) + "/" + m_activeClass->name().lower() + "_interface.h";
    if( QFile::exists(ifaceFileName) ){
	KMessageBox::error( mainWindow()->main(), i18n("File %1 already exists").arg(ifaceFileName),
			    i18n("C++ Support") );
    } else {
	QString text = extractInterface( m_activeClass );

	QFile f( ifaceFileName );
	if( f.open(IO_WriteOnly) ){
	    QTextStream stream( &f );
	    stream
		<< "#ifndef __" << m_activeClass->name().upper() << "_INTERFACE_H" << "\n"
		<< "#define __" << m_activeClass->name().upper() << "_INTERFACE_H" << "\n"
		<< "\n"
		<< extractInterface( m_activeClass )
		<< "\n"
		<< "#endif // __" << m_activeClass->name().upper() << "_INTERFACE_H" << "\n";
	    f.close();

	    project()->addFile( ifaceFileName );
	}
    }

    m_activeClass = 0;
}

void CppSupportPart::gotoLine( int line )
{
    if (isHeader(m_activeFileName))
    {
        KURL url;
        url.setPath(sourceOrHeaderCandidate());
        partController()->editDocument(url, line);
    }
    else
        m_activeViewCursor->setCursorPositionReal( line, 0 );
}

void CppSupportPart::recomputeCodeModel( const QString& fileName )
{
    if( codeModel()->hasFile(fileName) ){
	FileDom file = codeModel()->fileByName( fileName );
	removeWithReferences( fileName );
    }

    m_backgroundParser->lock();
    if( TranslationUnitAST* ast = m_backgroundParser->translationUnit(fileName) ){

	if( true /*!hasErrors*/ ){
	    StoreWalker walker( fileName, codeModel() );
	    walker.parseTranslationUnit( ast );
	    codeModel()->addFile( walker.file() );
	    emit addedSourceInfo( fileName );
	}
    }
    m_backgroundParser->unlock();
}

void CppSupportPart::emitFileParsed( )
{
    emit fileParsed( m_activeFileName );
}

bool CppSupportPart::isHeader( const QString& fileName ) const
{
    KMimeType::Ptr ptr = KMimeType::findByPath( fileName );
    if( ptr && m_headerMimeTypes.contains( ptr->name() ) )
        return true;

    return m_headerExtensions.contains( QFileInfo(fileName).extension() );
}

bool CppSupportPart::isSource( const QString& fileName ) const
{
    KMimeType::Ptr ptr = KMimeType::findByPath( fileName );
    if( ptr && m_sourceMimeTypes.contains( ptr->name() ) )
        return true;

    return m_sourceExtensions.contains( QFileInfo(fileName).extension() );
}

void CppSupportPart::gotoDeclarationLine( int line )
{
    if (isHeader(m_activeFileName))
        m_activeViewCursor->setCursorPositionReal( line, 0 );
    else
    {
        KURL url;
        url.setPath(sourceOrHeaderCandidate());
        partController()->editDocument(url, line);
    }
}

void CppSupportPart::removeCatalog( const QString & dbName )
{
    if( !QFile::exists(dbName) )
	return;

    QValueList<Catalog*> catalogs = codeRepository()->registeredCatalogs();
    Catalog* c = 0;
    for( QValueList<Catalog*>::Iterator it=catalogs.begin(); it!=catalogs.end(); ++it )
    {
	if( (*it)->dbName() == dbName ){
	    c = *it;
	    break;
	}
    }

    if( c ){
	codeRepository()->unregisterCatalog( c );
	m_catalogList.remove( c );
    }

    QFileInfo fileInfo( dbName );
    QDir dir( fileInfo.dir(true) );
    QStringList fileList = dir.entryList( fileInfo.baseName() + "*.idx" );
    for( QStringList::Iterator it=fileList.begin(); it!=fileList.end(); ++it )
    {
	QString idxName = fileInfo.dirPath( true ) + "/" + *it;
	kdDebug(9007) << "=========> remove db index: " << idxName << endl;
	dir.remove( *it );
    }

    dir.remove( fileInfo.fileName() );
}

void CppSupportPart::addCatalog( Catalog * catalog )
{
    m_catalogList.append( catalog );
    codeRepository()->registerCatalog( catalog );
}

FunctionDefinitionDom CppSupportPart::functionDefinitionAt( int line, int column )
{
    if( !codeModel()->hasFile(m_activeFileName) )
	return FunctionDefinitionDom();

    FileDom file = codeModel()->fileByName( m_activeFileName );
    return functionDefinitionAt( model_cast<NamespaceDom>(file), line, column );
}

FunctionDefinitionDom CppSupportPart::currentFunctionDefinition( )
{
    if( !this->m_activeViewCursor )
	return FunctionDefinitionDom();

    unsigned int line, column;
    this->m_activeViewCursor->cursorPositionReal( &line, &column );
    return functionDefinitionAt( line, column );
}

FunctionDefinitionDom CppSupportPart::functionDefinitionAt( NamespaceDom ns, int line, int column )
{
    NamespaceList namespaceList = ns->namespaceList();
    for( NamespaceList::Iterator it=namespaceList.begin(); it!=namespaceList.end(); ++it )
    {
	if( FunctionDefinitionDom def = functionDefinitionAt(*it, line, column) )
	    return def;
    }

    ClassList classList = ns->classList();
    for( ClassList::Iterator it=classList.begin(); it!=classList.end(); ++it )
    {
	if( FunctionDefinitionDom def = functionDefinitionAt(*it, line, column) )
	    return def;
    }

    FunctionDefinitionList functionDefinitionList = ns->functionDefinitionList();
    for( FunctionDefinitionList::Iterator it=functionDefinitionList.begin(); it!=functionDefinitionList.end(); ++it )
    {
	if( FunctionDefinitionDom def = functionDefinitionAt(*it, line, column) )
	    return def;
    }

    return FunctionDefinitionDom();
}

FunctionDefinitionDom CppSupportPart::functionDefinitionAt( ClassDom klass, int line, int column )
{
    ClassList classList = klass->classList();
    for( ClassList::Iterator it=classList.begin(); it!=classList.end(); ++it )
    {
	if( FunctionDefinitionDom def = functionDefinitionAt(*it, line, column) )
	    return def;
    }

    FunctionDefinitionList functionDefinitionList = klass->functionDefinitionList();
    for( FunctionDefinitionList::Iterator it=functionDefinitionList.begin(); it!=functionDefinitionList.end(); ++it )
    {
	if( FunctionDefinitionDom def = functionDefinitionAt(*it, line, column) )
	    return def;
    }

    return FunctionDefinitionDom();
}

FunctionDefinitionDom CppSupportPart::functionDefinitionAt( FunctionDefinitionDom fun, int line, int column )
{
    int startLine, startColumn;
    int endLine, endColumn;

    fun->getStartPosition( &startLine, &startColumn );
    fun->getEndPosition( &endLine, &endColumn );

    if( ! (line >= startLine && line <= endLine) )
	return FunctionDefinitionDom();

    if( line == startLine && column < startColumn )
	return FunctionDefinitionDom();

    if( line == endLine && column > endColumn )
	return FunctionDefinitionDom();

    return fun;
}

void CppSupportPart::slotCursorPositionChanged()
{
//    m_functionHintTimer->changeInterval( 1000 );
}

void CppSupportPart::slotFunctionHint( )
{
    kdDebug(9007) << "=======> compute current function definition" << endl;
    m_functionHintTimer->stop();
    if( FunctionDefinitionDom fun = currentFunctionDefinition() )
    {
	QStringList scope = fun->scope();
	QString funName = scope.join( "::" );
	if( !funName.isEmpty() )
	    funName += "::";

	funName += formatModelItem( fun, true );

	mainWindow()->statusBar()->message( funName, 2000 );
    }
}

void CppSupportPart::createIgnorePCSFile( )
{
    static QCString skip_me( "ignore me\n" );

    QString skip_file_name = project()->projectDirectory() + "/" + project()->projectName() + ".ignore_pcs";
    QFile skip_pcs_file( skip_file_name );
    if( skip_pcs_file.open(IO_WriteOnly) )
    {
        skip_pcs_file.writeBlock( skip_me );
        skip_pcs_file.close();
    }
}

QString CppSupportPart::specialHeaderName( bool local ) const
{
	if( local )
		return ::locateLocal( "data", "kdevcppsupport/configuration", CppSupportFactory::instance() );

	return ::locate( "data", "kdevcppsupport/configuration", CppSupportFactory::instance() );
}

void CppSupportPart::updateParserConfiguration()
{
	m_backgroundParser->updateParserConfiguration();

	QString conf_file_name = specialHeaderName();
	m_driver->removeAllMacrosInFile( conf_file_name );

	m_driver->parseFile( conf_file_name, true );

	parseProject( true );
}

KDevDesignerIntegration * CppSupportPart::designer( KInterfaceDesigner::DesignerType type )
{
    KDevDesignerIntegration *des = 0;
    switch (type)
    {
        case KInterfaceDesigner::QtDesigner:
            des = m_designers[type];
            if (des == 0)
            {
                CppImplementationWidget *impl = new CppImplementationWidget(this);
                des = new QtDesignerCppIntegration(this, impl);
                des->loadSettings(*project()->projectDom(), "kdevcppsupport/designerintegration");
                m_designers[type] = des;
            }
            break;
    }
    return des;
}

void CppSupportPart::slotCreateSubclass()
{
    QFileInfo fi(m_contextFileName);
    if (fi.extension(false) != "ui")
        return;
    QtDesignerCppIntegration *des = dynamic_cast<QtDesignerCppIntegration*>(designer(KInterfaceDesigner::QtDesigner));
    if (des)
        des->selectImplementation(m_contextFileName);
}

void CppSupportPart::addMethod(ClassDom aClass, const QString& name, const QString type, const QString& parameters, CodeModelItem::Access accessType, bool isConst, bool isInline, bool isVirtual, bool isPureVirtual, const QString& implementation)
{
	partController()->editDocument( KURL( aClass->fileName() ) );
    KTextEditor::EditInterface* editIface = dynamic_cast<KTextEditor::EditInterface*>( partController()->activePart() );
    if( !editIface ){
	/// @fixme show messagebox
		return;
    }
	QString declarationString = type + " " + name + "(" + parameters + ")" + (isConst ? " const" : "");
	
	KDevSourceFormatter* sourceFormatter = extension<KDevSourceFormatter>("KDevelop/SourceFormatter");
	
	QString finalDeclaration = ((isVirtual || isPureVirtual) ? "\nvirtual " : "\n" + declarationString + 
			( isPureVirtual ? " = 0 " : "") + (isInline ? "\n{\n" + implementation + "\n}\n" : ";"));
	
	if (sourceFormatter != 0)
		finalDeclaration = sourceFormatter->formatSource(finalDeclaration);
	
	QString indentString = "\t";
	 
	if (sourceFormatter != 0)
	  indentString = sourceFormatter->indentString();
		
	editIface->insertText( findInsertionLineMethod(aClass, accessType), 0, finalDeclaration.replace("\n","\n\t")+"\n");

    backgroundParser()->addFile( aClass->fileName() );
	if (isInline || isPureVirtual)
		return;
	
	QString definitionString ="\n" + declarationString + "\n{\n" + implementation + "\n}\n";
	
	if (sourceFormatter != 0)
		   definitionString = sourceFormatter->formatSource( definitionString );
	
	QFileInfo info( aClass->fileName() );
	QString implementationFile = info.dirPath(true) + "/" + info.baseName() + ".cpp" ;
	QFileInfo fileInfo( implementationFile );
	KDevCreateFile* createFileSupport = extension<KDevCreateFile>("KDevelop/CreateFile");
	if( !QFile::exists(fileInfo.absFilePath()) && createFileSupport != 0)
		createFileSupport->createNewFile( fileInfo.extension(), fileInfo.dirPath(true), fileInfo.baseName() );

	partController()->editDocument( KURL( implementationFile ) );
	editIface = dynamic_cast<KTextEditor::EditInterface*>( partController()->activePart() );
	if( !editIface )
		return; //@fixme errorverdoedelung

	editIface->insertLine( editIface->numLines(), QString::fromLatin1("") );
	editIface->insertText( editIface->numLines()-1, 0, definitionString );
	backgroundParser()->addFile( implementationFile );
}

ClassDom CppSupportPart::currentClass( ) const
{
	FileDom file = codeModel()->fileByName( m_activeFileName );
	if (file == 0 || m_activeViewCursor == 0)
		return 0;
	
	unsigned int curLine, curCol;
	m_activeViewCursor->cursorPosition(&curLine, &curCol);
	
	return CodeModelUtils::findClassByPosition(file, curLine, curCol);
}

VariableDom CppSupportPart::currentAttribute( ClassDom curClass ) const
{
	if (m_activeViewCursor == 0 || curClass == 0)
		return 0;
	
	unsigned int line,col;
	m_activeViewCursor->cursorPosition(&line, &col);
	
	VariableList vars = curClass->variableList();
	
	for (VariableList::iterator i = vars.begin(); i != vars.end(); ++i)
	{
		int startLine, startCol;
		(*i)->getStartPosition(&startLine, &startCol);
		if (startLine < line || (startLine == line && startCol <= col) )
		{
			int endLine, endCol;
			(*i)->getEndPosition(&endLine, &endCol);
			if (endLine > line || (endLine == line && endCol >= col) )
				return *i;
		}
	}
	return 0;
}

void CppSupportPart::slotCreateAccessMethods( )
{
	if (m_curAttribute == 0 || m_curClass == 0)
		return;
	
	CreateGetterSetterDialog dlg (this, m_curClass, m_curAttribute);
	dlg.exec();
}

int CppSupportPart::findInsertionLineMethod( ClassDom aClass, CodeModelItem::Access access )
{	
	int line, column;
	aClass->getEndPosition( &line, &column );

	int point = CodeModelUtils::findLastMethodLine( aClass, access );
	
	if (point == -1)
	{
		KTextEditor::EditInterface* editIface = dynamic_cast<KTextEditor::EditInterface*>( partController()->activePart() );
		if( !editIface )
			return -1;
		
		editIface->insertLine( line - 1, CodeModelUtils::accessSpecifierToString(access) + ":\n" );
		return line;
	}
	
	return point + 1;
}

int CppSupportPart::findInsertionLineVariable( ClassDom aClass, CodeModelItem::Access access )
{
	int line, column;
	aClass->getEndPosition( &line, &column );

	int point = CodeModelUtils::findLastVariableLine( aClass, access );

	if (point == -1)
	{
		KTextEditor::EditInterface* editIface = dynamic_cast<KTextEditor::EditInterface*>( partController()->activePart() );
		if( !editIface )
			return -1;
		
		editIface->insertLine( line - 1, CodeModelUtils::accessSpecifierToString(access) + ":\n" );
		return line;
	}
	
	return point;
}

void CppSupportPart::createAccessMethods( ClassDom theClass, VariableDom theVariable )
{
	m_curClass = theClass;
	m_curAttribute = theVariable;
	
	slotCreateAccessMethods();
}

#include "cppsupportpart.moc"
