/* Copyright (C) 2005 by Nicolas Escuder Copyright (C) 2001 by smeier@kdevelop.org This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public version 2, License as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "phpsupportpart.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "phpconfigdata.h" #include "phpconfigwidget.h" #include "phpcodecompletion.h" #include "phpparser.h" #include "phpnewclassdlg.h" #include "phphtmlview.h" #include "phperrorview.h" #include "phpsupport_event.h" using namespace std; static const KDevPluginInfo data("kdevphpsupport"); K_EXPORT_COMPONENT_FACTORY( libkdevphpsupport, PHPSupportFactory( data ) ) PHPSupportPart::PHPSupportPart(TQObject *tqparent, const char *name, const TQStringList &) : KDevLanguageSupport(&data, tqparent, name ? name : "PHPSupportPart") { m_htmlView = 0; m_parser = 0; phpExeProc = 0; setInstance(PHPSupportFactory::instance()); setXMLFile("kdevphpsupport.rc"); connect( core(), TQT_SIGNAL(projectOpened()), this, TQT_SLOT(projectOpened()) ); connect( core(), TQT_SIGNAL(projectClosed()), this, TQT_SLOT(projectClosed()) ); connect( partController(), TQT_SIGNAL(savedFile(const KURL&)), this, TQT_SLOT(savedFile(const KURL&)) ); connect( core(), TQT_SIGNAL(projectConfigWidget(KDialogBase*)), this, TQT_SLOT(projectConfigWidget(KDialogBase*)) ); KAction *action; action = new KAction( i18n("&Run"), "exec",Key_F9, this, TQT_SLOT(slotRun()), actionCollection(), "build_execute" ); action->setToolTip(i18n("Run")); action->setWhatsThis(i18n("Run

Executes script on a terminal or a webserver.")); action = new KAction( i18n("&New Class..."),0, this, TQT_SLOT(slotNewClass()), actionCollection(), "project_new_class" ); action->setToolTip(i18n("New class")); action->setWhatsThis(i18n("New class

Runs New Class wizard.")); m_phpErrorView = new PHPErrorView(this, 0, "phpErrorWidget"); m_phpErrorView->setIcon( SmallIcon("info") ); TQWhatsThis::add(m_phpErrorView, i18n("PHP problems

This view shows PHP parser warnings, errors, and fatal errors.")); mainWindow()->embedOutputView(m_phpErrorView, i18n("Problems"), i18n("Problems")); phpExeProc = new KShellProcess("/bin/sh"); connect( phpExeProc, TQT_SIGNAL(receivedStdout (KProcess*, char*, int)), this, TQT_SLOT(slotReceivedPHPExeStdout (KProcess*, char*, int))); connect( phpExeProc, TQT_SIGNAL(receivedStderr (KProcess*, char*, int)), this, TQT_SLOT(slotReceivedPHPExeStderr (KProcess*, char*, int))); connect( phpExeProc, TQT_SIGNAL(processExited(KProcess*)), this, TQT_SLOT(slotPHPExeExited(KProcess*))); m_htmlView = new PHPHTMLView(this); mainWindow()->embedOutputView(m_htmlView->view(), i18n("PHP"), i18n("PHP")); connect( m_htmlView, TQT_SIGNAL(started(KIO::Job*)), this, TQT_SLOT(slotWebJobStarted(KIO::Job*))); configData = new PHPConfigData(projectDom()); connect( configData, TQT_SIGNAL(configStored()), this, TQT_SLOT(slotConfigStored())); m_codeCompletion = new PHPCodeCompletion(this, configData); new KAction(i18n("Complete Text"), CTRL+Key_Space, m_codeCompletion, TQT_SLOT(cursorPositionChanged()), actionCollection(), "edit_complete_text"); connect( partController(), TQT_SIGNAL(activePartChanged(KParts::Part*)), this, TQT_SLOT(slotActivePartChanged(KParts::Part *))); connect( this, TQT_SIGNAL(fileParsed( PHPFile* )), this, TQT_SLOT(slotfileParsed( PHPFile* ))); } PHPSupportPart::~PHPSupportPart() { LastClass = NULL; LastMethod = NULL; LastVariable = NULL; if ( m_parser ) { m_parser->close() ; delete( m_parser ); m_parser = NULL; } if ( m_phpErrorView ) { mainWindow()->removeView( m_phpErrorView ); delete( m_phpErrorView ); m_phpErrorView = NULL; } kdDebug(9018) << "remove codeCompletition" << endl; if ( m_codeCompletion ) delete( m_codeCompletion ); kdDebug(9018) << "remove configData" << endl; if ( configData ) delete( configData ); if ( m_htmlView ) { kdDebug(9018) << "remove htmlView" << endl; mainWindow()->removeView( m_htmlView->view() ); delete( m_htmlView ); m_htmlView = NULL; } kdDebug(9018) << "remove phpExec" << endl; if ( phpExeProc ) delete( phpExeProc ); kdDebug(9018) << "finish" << endl; } void PHPSupportPart::slotActivePartChanged(KParts::Part *part) { kdDebug(9018) << "enter slotActivePartChanged" << endl; if (!part || !part->widget()) return; m_editInterface = dynamic_cast(part); if (m_editInterface) { // connect to the editor disconnect(part, 0, this, 0 ); // to make sure that it is't connected twice if (configData->getRealtimeParsing()) { connect(part,TQT_SIGNAL(textChanged()),this,TQT_SLOT(slotTextChanged())); } m_codeCompletion->setActiveEditorPart(part); } kdDebug(9018) << "exit slotActivePartChanged" << endl; } void PHPSupportPart::slotTextChanged() { kdDebug(9018) << "enter text changed" << endl; KParts::ReadOnlyPart *ro_part = dynamic_cast(partController()->activePart()); if (!ro_part) return; TQString fileName = ro_part->url().directory() + "/" + ro_part->url().fileName(); if (m_parser) { if (m_parser->hasFile( fileName )) m_parser->reparseFile( fileName ); } } void PHPSupportPart::slotConfigStored() { // fake a changing, this will read the configuration again and install the connects slotActivePartChanged(partController()->activePart()); } void PHPSupportPart::projectConfigWidget(KDialogBase *dlg) { TQVBox *vbox = dlg->addVBoxPage(i18n( "PHP Specific" ), i18n("PHP Settings"), BarIcon( "source", KIcon::SizeMedium )); PHPConfigWidget* w = new PHPConfigWidget(configData,vbox, "php config widget"); connect( dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(accept()) ); } void PHPSupportPart::slotNewClass() { TQStringList classNames = sortedNameList( codeModel()->globalNamespace()->classList() ); PHPNewClassDlg dlg(classNames,project()->projectDirectory()); dlg.exec(); } void PHPSupportPart::slotRun() { configData = new PHPConfigData(projectDom()); if (validateConfig()) { mainWindow()->raiseView(m_htmlView->view()); PHPConfigData::InvocationMode mode = configData->getInvocationMode() ; if (mode == PHPConfigData::Web) { executeOnWebserver(); } else if (mode == PHPConfigData::Shell) { executeInTerminal(); } } } bool PHPSupportPart::validateConfig() { if (!configData->validateConfig()) { KMessageBox::information(0,i18n("There is no configuration for executing a PHP file.\nPlease set the correct values in the next dialog.")); KDialogBase dlg(KDialogBase::TreeList, i18n("Customize PHP Mode"), KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, 0, "php config dialog"); TQVBox *vbox = dlg.addVBoxPage(i18n("PHP Settings")); PHPConfigWidget* w = new PHPConfigWidget(configData,vbox, "php config widget"); connect( &dlg, TQT_SIGNAL(okClicked()), w, TQT_SLOT(accept()) ); dlg.exec(); } if (configData->validateConfig()) { return true; } return false; } void PHPSupportPart::executeOnWebserver() { // Save all files once if (partController()->saveAllFiles()==false) return; //user cancelled // Figure out the name of the remote file TQString weburl = configData->getWebURL(); TQString file = getExecuteFile(); // Force KHTMLPart to reload the page KParts::BrowserExtension* be = m_htmlView->browserExtension(); if (be) { KParts::URLArgs urlArgs( be->urlArgs() ); urlArgs.reload = true; be->setURLArgs( urlArgs ); } // Acutally do the request m_phpExeOutput=""; m_htmlView->openURL(KURL(weburl + file)); m_htmlView->show(); } TQString PHPSupportPart::getExecuteFile() { TQString file; PHPConfigData::StartupFileMode mode = configData->getStartupFileMode(); TQString weburl = configData->getWebURL(); if (mode == PHPConfigData::Current) { KParts::ReadOnlyPart *ro_part = dynamic_cast(partController()->activePart()); if (ro_part) { if ( configData->getInvocationMode() == PHPConfigData::Web ) file = URLUtil::relativePath( project()->projectDirectory(), ro_part->url().path() ); else file = ro_part->url().path(); } } if (mode == PHPConfigData::Default) { file = configData->getStartupFile(); } return file; } void PHPSupportPart::slotWebJobStarted(KIO::Job* job) { if (job && job->className() == TQString("KIO::TransferJob")) { kdDebug(9018) << endl << "job started" << job->progressId(); KIO::TransferJob *tjob = static_cast(job); connect( tjob, TQT_SIGNAL(data(KIO::Job*, const TQByteArray&)), this, TQT_SLOT(slotWebData(KIO::Job*, const TQByteArray&))); connect( tjob, TQT_SIGNAL(result(KIO::Job*)), this, TQT_SLOT(slotWebResult(KIO::Job*))); } } void PHPSupportPart::slotWebData(KIO::Job* /*job*/,const TQByteArray& data) { kdDebug(9018) << "slotWebData()" << endl; TQString strData(data); m_phpExeOutput += strData; } void PHPSupportPart::slotWebResult(KIO::Job* /*job*/) { kdDebug(9018) << "slotWebResult()" << endl; TQString file = getExecuteFile(); PHPFile *pfile = new PHPFile(this, file); pfile->ParseStdout(m_phpExeOutput); delete pfile; } void PHPSupportPart::executeInTerminal() { kdDebug(9018) << "slotExecuteInTerminal()" << endl; // Save all files once if (partController()->saveAllFiles()==false) return; //user cancelled TQString file = getExecuteFile(); if (m_htmlView == 0) { m_htmlView = new PHPHTMLView(this); mainWindow()->embedOutputView(m_htmlView->view(), i18n("PHP"), i18n("PHP") ); } m_htmlView->show(); m_htmlView->begin(); m_phpExeOutput = ""; phpExeProc->clearArguments(); *phpExeProc << configData->getPHPExecPath(); *phpExeProc << "-f"; *phpExeProc << KShellProcess::quote(file); kdDebug(9018) << "" << file.latin1() << endl; phpExeProc->start(KProcess::NotifyOnExit,KProcess::All); // core()->gotoDocumentationFile(KURL("http://www.php.net")); } void PHPSupportPart::slotReceivedPHPExeStdout (KProcess* /*proc*/, char* buffer, int buflen) { kdDebug(9018) << "slotPHPExeStdout()" << endl; m_phpExeOutput += TQString::fromLocal8Bit(buffer,buflen+1); TQString buf = buffer; if (configData->getInvocationMode() == PHPConfigData::Shell) buf.replace("\n", "
"); m_htmlView->write(buf); } void PHPSupportPart::slotReceivedPHPExeStderr (KProcess* /*proc*/, char* buffer, int buflen) { kdDebug(9018) << "slotPHPExeStderr()" << endl; m_phpExeOutput += TQString::fromLocal8Bit(buffer,buflen+1); TQString buf = buffer; if (configData->getInvocationMode() == PHPConfigData::Shell) buf.replace("\n", "
"); m_htmlView->write(buf); } void PHPSupportPart::slotPHPExeExited (KProcess* /*proc*/) { kdDebug(9018) << "slotPHPExeExited()" << endl; m_htmlView->end(); TQString file = getExecuteFile(); PHPFile *pfile = new PHPFile(this, file); pfile->ParseStdout(m_phpExeOutput); delete pfile; } void PHPSupportPart::projectOpened() { kdDebug(9018) << "projectOpened()" << endl; connect( project(), TQT_SIGNAL(addedFilesToProject(const TQStringList &)), this, TQT_SLOT(addedFilesToProject(const TQStringList &)) ); connect( project(), TQT_SIGNAL(removedFilesFromProject(const TQStringList &)), this, TQT_SLOT(removedFilesFromProject(const TQStringList &)) ); if (!m_parser) { m_parser = new PHPParser( this ); m_parser->start(); } // We want to parse only after all components have been // properly initialized TQTimer::singleShot(500, this, TQT_SLOT( initialParse() ) ); } void PHPSupportPart::initialParse( ) { // For debugging if ( !project( ) ) { // messagebox ? kdDebug( 9018 ) << "No project" << endl; return ; } parseProject( ); return ; } void PHPSupportPart::projectClosed() { kdDebug(9018) << "projectClosed()" << endl; if (m_parser) { m_parser->close() ; delete( m_parser ); m_parser = 0; } } bool PHPSupportPart::parseProject() { kdDebug(9018) << "parseProject()" << endl; mainWindow() ->statusBar() ->message( i18n( "Updating..." ) ); kapp->setOverrideCursor( waitCursor ); _jd = new JobData; _jd->files = project()->allFiles(); TQProgressBar* bar = new TQProgressBar( _jd->files.count( ), mainWindow( ) ->statusBar( ) ); bar->setMinimumWidth( 120 ); bar->setCenterIndicator( true ); mainWindow()->statusBar()->addWidget( bar ); bar->show(); _jd->progressBar = bar; _jd->it = _jd->files.begin(); _jd->dir.setPath( project()->projectDirectory() ); TQTimer::singleShot( 0, this, TQT_SLOT( slotParseFiles() ) ); return TRUE; } void PHPSupportPart::slotParseFiles() { kdDebug(9018) << "slotParseFiles()" << endl; kapp->lock(); if ( _jd->it != _jd->files.end() ) { _jd->progressBar->setProgress( _jd->progressBar->progress() + 1 ); TQFileInfo fileInfo( _jd->dir, *( _jd->it ) ); if ( fileInfo.exists() && fileInfo.isFile() && fileInfo.isReadable() ) { TQString absFilePath = URLUtil::canonicalPath( fileInfo.absFilePath() ); // if ( isValidSource( absFilePath ) ) { if (m_parser) m_parser->addFile( absFilePath ); } ++( _jd->it ); } TQTimer::singleShot( 0, this, TQT_SLOT( slotParseFiles() ) ); } else // finished or interrupted { kapp->restoreOverrideCursor(); mainWindow()->statusBar()->removeWidget( _jd->progressBar ); mainWindow()->statusBar()->message( i18n( "Done" ), 2000 ); emit updatedSourceInfo(); if (m_parser) m_parser->startParse(); delete _jd; _jd = 0; } kapp->unlock(); } void PHPSupportPart::addedFilesToProject(const TQStringList &fileList) { kdDebug(9018) << "addedFilesToProject()" << endl; TQStringList::ConstIterator it; for ( it = fileList.begin(); it != fileList.end(); ++it ) { TQFileInfo fileInfo( project()->projectDirectory(), *it ); if (m_parser) { m_parser->addFile( fileInfo.absFilePath() ); emit addedSourceInfo( fileInfo.absFilePath() ); } } } void PHPSupportPart::removedFilesFromProject(const TQStringList &fileList) { kdDebug(9018) << "removedFilesFromProject()" << endl; TQStringList::ConstIterator it; for ( it = fileList.begin(); it != fileList.end(); ++it ) { TQFileInfo fileInfo( project()->projectDirectory(), *it ); TQString path = fileInfo.absFilePath(); if ( codeModel()->hasFile(path) ) { emit aboutToRemoveSourceInfo( path ); codeModel()->removeFile( codeModel()->fileByName(path) ); } } } void PHPSupportPart::savedFile(const KURL &fileName) { kdDebug(9018) << "savedFile()" << fileName.fileName() << endl; /// @fixme when activated could cause stop /* if (m_parser) { if (m_parser->hasFile( fileName.path() )) { m_parser->reparseFile( fileName.path() ); } } */ } TQString PHPSupportPart::getIncludePath() { return configData->getPHPIncludePath(); } TQString PHPSupportPart::getExePath() { return configData->getPHPExecPath(); } KDevLanguageSupport::Features PHPSupportPart::features() { return Features(Classes | Functions); } KMimeType::List PHPSupportPart::mimeTypes( ) { KMimeType::List list; KMimeType::Ptr mime = KMimeType::mimeType( "application/x-php" ); if ( mime ) list << mime; mime = KMimeType::mimeType( "text/plain" ); if ( mime ) list << mime; return list; } void PHPSupportPart::customEvent( TQCustomEvent* ev ) { // kdDebug(9018) << "phpSupportPart::customEvent(" << ev->type() << ") " << TQThread::currentThread() << endl; if ( ev->type() < Event_AddFile || ev->type() > Event_AddFixme ) return; kapp->lock(); FileParseEvent* event = (FileParseEvent*) ev; NamespaceDom ns = codeModel()->globalNamespace(); FileDom m_file = codeModel()->fileByName( event->fileName() ); if (!m_file) { m_file = codeModel()->create(); m_file->setName( event->fileName() ); codeModel()->addFile( m_file ); } switch (int(ev->type())) { case Event_AddFile: m_parser->addFile( event->fileName() ); break; case Event_StartParse: // kdDebug(9018) << "StartParse " << event->fileName() << endl; LastClass = NULL; LastMethod = NULL; LastVariable = NULL; if ( codeModel()->hasFile( event->fileName() ) ) { emit aboutToRemoveSourceInfo( event->fileName() ); codeModel()->removeFile( codeModel()->fileByName( event->fileName() ) ); emit removedSourceInfo( event->fileName() ); } ErrorView()->removeAllProblems( event->fileName() ); break; case Event_AddClass: { // kdDebug(9018) << "AddClass " << event->name() << endl; ClassDom nClass = codeModel()->create(); nClass->setFileName( event->fileName() ); nClass->setName( event->name() ); nClass->setStartPosition( event->posititon(), 0); m_file->addClass( nClass ); if ( event->arguments().isEmpty() != TRUE ) nClass->addBaseClass( event->arguments() ); ns->addClass( nClass ); LastClass = nClass; } break; case Event_CloseClass: if ( LastClass != NULL ) { // kdDebug(9018) << "CloseClass " << LastClass->name() << endl; LastClass->setEndPosition( event->posititon(), 0 ); LastClass = NULL; LastMethod = NULL; LastVariable = NULL; } break; case Event_AddFunction: { // kdDebug(9018) << "AddFunction " << event->name() << endl; FunctionDom nMethod = codeModel()->create(); nMethod->setFileName( event->fileName() ); nMethod->setName( event->name() ); nMethod->setStartPosition( event->posititon(), 0 ); ArgumentDom nArgument; nArgument = codeModel()->create(); nArgument->setType(event->arguments().stripWhiteSpace().local8Bit()); nMethod->addArgument( nArgument ); if (LastClass != NULL) { LastClass->addFunction(nMethod); } else { ns->addFunction(nMethod); } LastMethod = nMethod; } break; case Event_SetFunction: if ( LastMethod != NULL ) { // kdDebug(9018) << "SetFunction " << LastMethod->name() << " " << event->name() << endl; if ( event->name() == "static" ) LastMethod->setStatic(true); else if ( event->name() == "abstract" ) LastMethod->setAbstract(true); else if ( event->name() == "private" ) LastMethod->setAccess(FunctionModel::Private); else if ( event->name() == "public" ) LastMethod->setAccess(FunctionModel::Public); else if ( event->name() == "protected" ) LastMethod->setAccess(FunctionModel::Protected); else if ( event->name() == "result" ) { TQString ret = ""; if (event->arguments().lower() == "$this" && LastClass ) { ret = LastClass->name(); } LastMethod->setResultType(ret); } } break; case Event_CloseFunction: if ( LastMethod != NULL ) { // kdDebug(9018) << "CloseFunction " << LastMethod->name() << endl; LastMethod->setEndPosition( event->posititon(), 0 ); LastMethod = NULL; LastVariable = NULL; } break; case Event_AddVariable: { VariableDom nVariable = codeModel()->create(); nVariable->setFileName( event->fileName() ); nVariable->setName( event->name() ); nVariable->setStartPosition( event->posititon(), 0 ); nVariable->setAccess(VariableModel::Public); if ( event->arguments().isEmpty() != TRUE ) nVariable->setType( event->arguments() ); if ( LastClass != NULL && ( LastMethod == NULL || event->global() == TRUE ) ) { // kdDebug(9018) << "AddVariable To Class " << LastClass->name() << " " << nVariable->name() << endl; LastClass->addVariable(nVariable); } else { if ( LastMethod != NULL ) { kdDebug(9018) << "AddVariable " << LastMethod->name() << " " << nVariable->name() << endl; } else { ns->addVariable(nVariable); } } LastVariable = nVariable; } break; case Event_SetVariable: if ( LastVariable != NULL ) { // kdDebug(9018) << "SetVariable " << LastVariable->name() << " " << event->arguments() << endl; if ( event->arguments() == "static" ) LastVariable->setStatic(true); else if ( event->arguments() == "private" ) LastVariable->setAccess(FunctionModel::Private); else if ( event->arguments() == "public" ) LastVariable->setAccess(FunctionModel::Public); else if ( event->arguments() == "protected" ) LastVariable->setAccess(FunctionModel::Protected); } break; case Event_AddTodo: ErrorView()->reportProblem(Todo, event->fileName(), event->posititon(), event->arguments()); break; case Event_AddFixme: ErrorView()->reportProblem(Fixme, event->fileName(), event->posititon(), event->arguments()); break; case Event_EndParse: // kdDebug(9018) << "EndParse " << event->fileName() << endl; emit addedSourceInfo( event->fileName() ); break; } kapp->unlock(); kapp->processEvents(); } PHPErrorView *PHPSupportPart::ErrorView( ) { return m_phpErrorView; } PHPParser *PHPSupportPart::Parser( ) { return m_parser; } #include "phpsupportpart.moc"