KDevelop API Documentation

doxygenpart.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (C) 2001 by Bernd Gehrmann                                  *
00003  *   bernd@kdevelop.org                                                    *
00004  *   Copyright (C) 2004 by Jonas Jacobi                                    *
00005  *    jonas.jacobi@web.de                                                  *
00006  *                                                                         *
00007  *   This program is free software; you can redistribute it and/or modify  *
00008  *   it under the terms of the GNU General Public License as published by  *
00009  *   the Free Software Foundation; either version 2 of the License, or     *
00010  *   (at your option) any later version.                                   *
00011  *                                                                         *
00012  ***************************************************************************/
00013 
00014 #include "doxygenpart.h"
00015 #include "doxygenconfigwidget.h"
00016 #include "configwidgetproxy.h"
00017 #include "config.h"
00018 
00019 #include <kdevmainwindow.h>
00020 #include <kdevproject.h>
00021 #include <kdevmakefrontend.h>
00022 #include <kdevcore.h>
00023 #include <codemodel.h>
00024 #include <codemodel_utils.h>
00025 #include <domutil.h>
00026 
00027 #include <kdebug.h>
00028 #include <klocale.h>
00029 #include <kdevgenericfactory.h>
00030 #include <kaction.h>
00031 #include <kmessagebox.h>
00032 #include <kmainwindow.h>
00033 #include <kparts/part.h>
00034 #include <ktexteditor/document.h>
00035 #include <ktexteditor/viewcursorinterface.h>
00036 #include <ktexteditor/editinterface.h>
00037 #include <partcontroller.h>
00038 #include <kdialogbase.h>
00039 
00040 #include <qvbox.h>
00041 #include <qfile.h>
00042 #include <qtextstream.h>
00043 #include <qpopupmenu.h>
00044 #include <qfileinfo.h>
00045 
00046 #define PROJECTOPTIONS 1
00047 
00048 typedef KDevGenericFactory<DoxygenPart> DoxygenFactory;
00049 static const KAboutData data("kdevdoxygen", I18N_NOOP("Doxygen"), "1.0");
00050 K_EXPORT_COMPONENT_FACTORY( libkdevdoxygen, DoxygenFactory( &data ) )
00051 
00052 DoxygenPart::DoxygenPart(QObject *parent, const char *name, const QStringList &)
00053     : KDevPlugin("Doxgen", "kdevelop", parent, name ? name : "DoxygenPart"), m_activeEditor(0), m_cursor(0)
00054 {
00055     setInstance(DoxygenFactory::instance());
00056     setXMLFile("kdevdoxygen.rc");
00057 
00058     KAction *action;
00059     action = new KAction( i18n("Build API Documentation"), 0,
00060                           this, SLOT(slotDoxygen()),
00061                           actionCollection(), "build_doxygen" );
00062     action->setToolTip(i18n("Build API documentation"));
00063     action->setWhatsThis(i18n("<b>Build API documentation</b><p>Runs doxygen on a project Doxyfile to generate API documentation. "
00064         "If the search engine is enabled in Doxyfile, this also runs doxytag to create it."));
00065 
00066     action = new KAction( i18n("Clean API Documentation"), 0,
00067                           this, SLOT(slotDoxClean()),
00068                           actionCollection(), "clean_doxygen" );
00069     action->setToolTip(i18n("Clean API documentation"));
00070     action->setWhatsThis(i18n("<b>Clean API documentation</b><p>Removes all generated by doxygen files."));
00071 
00072 //    connect( core(), SIGNAL(projectConfigWidget(KDialogBase*)), this, SLOT(projectConfigWidget(KDialogBase*)) );
00073 
00074     _configProxy = new ConfigWidgetProxy( core() );
00075     _configProxy->createProjectConfigPage( i18n("Doxygen"), PROJECTOPTIONS, icon() );
00076     connect( _configProxy, SIGNAL(insertConfigWidget(const KDialogBase*, QWidget*, unsigned int )),
00077         this, SLOT(insertConfigWidget(const KDialogBase*, QWidget*, unsigned int )) );
00078 
00079     m_actionDocumentFunction = new KAction(i18n("Document Current Function"), 0, CTRL+SHIFT+Key_S, this, SLOT(slotDocumentFunction()), actionCollection(), "edit_document_function");
00080     m_actionDocumentFunction->setToolTip( i18n("Create a documentation template above a function"));
00081     m_actionDocumentFunction->setWhatsThis(i18n("<b>Document Current Function</b><p>Creates a documentation template according to a function's signature above a function definition/declaration."));
00082 
00083     m_tmpDir.setAutoDelete(true);
00084     connect(&m_process, SIGNAL(processExited(KProcess*)), this, SLOT(slotPreviewProcessExited()));
00085     connect( partController(), SIGNAL(activePartChanged(KParts::Part*)), this, SLOT(slotActivePartChanged(KParts::Part* )));
00086     m_actionPreview = new KAction(i18n("Preview Doxygen Output"), 0, CTRL+ALT+Key_P, this, SLOT(slotRunPreview()), actionCollection(), "show_preview_doxygen_output");
00087     m_actionPreview->setToolTip( i18n("Show a preview of the Doxygen output of this file") );
00088     m_actionPreview->setWhatsThis( i18n("<b>Preview Doxygen output</b><p>Runs Doxygen over the current file and shows the created index.html.") );
00089 
00090     //read Doxygen configuration, if none exists yet, create it with some defaults
00091     adjustDoxyfile();
00092     QString fileName = project()->projectDirectory() + "/Doxyfile";
00093 
00094     QFile file(fileName);
00095     if (file.open(IO_ReadOnly)) {
00096         QTextStream is(&file);
00097 
00098         Config::instance()->parse(QFile::encodeName(fileName));
00099         Config::instance()->convertStrToVal();
00100 
00101         file.close();
00102     }
00103 }
00104 
00105 
00106 DoxygenPart::~DoxygenPart()
00107 {
00108     delete _configProxy;
00109 }
00110 
00111 void DoxygenPart::insertConfigWidget( const KDialogBase * dlg, QWidget * page, unsigned int pagenumber )
00112 {
00113     if ( pagenumber == PROJECTOPTIONS )
00114     {
00115         adjustDoxyfile();
00116 
00117         DoxygenConfigWidget *w = new DoxygenConfigWidget(project()->projectDirectory() + "/Doxyfile", page );
00118         connect( dlg, SIGNAL(okClicked()), w, SLOT(accept()) );
00119     }
00120 }
00121 
00125 void DoxygenPart::adjustDoxyfile()
00126 {
00127   QString fileName = project()->projectDirectory() + "/Doxyfile";
00128   if (QFile::exists(fileName))
00129     return;
00130 
00131   // Initialize configuration
00132   Config::instance()->init();
00133 
00134   // Do some checks and improve the configuration a bit
00135   Config::instance()->check();
00136 
00137   // set "General/PROJECT_NAME"
00138   ConfigString *name = dynamic_cast<ConfigString*>(Config::instance()->get("PROJECT_NAME"));
00139   if (name)
00140   {
00141     name->setDefaultValue(project()->projectName().latin1());
00142     name->init();
00143   }
00144 
00145   // set "General/PROJECT_NUMBER"
00146   ConfigString *version = dynamic_cast<ConfigString*>(Config::instance()->get("PROJECT_NUMBER"));
00147   if (version)
00148   {
00149     version->setDefaultValue(DomUtil::readEntry(*projectDom(), "/general/version").latin1());
00150     version->init();
00151   }
00152 
00153   // insert input files into "Input/INPUT"
00154   ConfigList *input_files = dynamic_cast<ConfigList*>(Config::instance()->get("INPUT"));
00155   if (input_files)
00156   {
00157     input_files->init();
00158     input_files->addValue(QFile::encodeName(project()->projectDirectory()));
00159   }
00160 
00161   // insert file patterns into "Input/FILE_PATTERNS"
00162   ConfigList *patterns = dynamic_cast<ConfigList*>(Config::instance()->get("FILE_PATTERNS"));
00163   if (patterns)
00164   {
00165     // Remove Doxygen's default patterns
00166 //    patterns->init();
00167 
00168     // Add this ones:
00169     patterns->addValue("*.C");
00170     patterns->addValue("*.H");
00171     patterns->addValue("*.tlh");
00172     patterns->addValue("*.diff");
00173     patterns->addValue("*.patch");
00174     patterns->addValue("*.moc");
00175     patterns->addValue("*.xpm");
00176     patterns->addValue("*.dox");
00177   }
00178 
00179   // set "Input/RECURSIVE" to recurse into subdirectories
00180   ConfigBool *recursive = dynamic_cast<ConfigBool*>(Config::instance()->get("RECURSIVE"));
00181   if (recursive)
00182   {
00183     recursive->setValueString("yes");
00184   }
00185 
00186   // set "XML/GENERATE_XML" to generate XML information to be used with code hinting
00187   ConfigBool *gen_xml = dynamic_cast<ConfigBool*>(Config::instance()->get("GENERATE_XML"));
00188   if (gen_xml)
00189   {
00190     gen_xml->setValueString("yes");
00191   }
00192 
00193   // set "Enternal/GENERATE_TAGFILE" to generate tag file for documentation browser
00194   ConfigString *gen_tag = dynamic_cast<ConfigString*>(Config::instance()->get("GENERATE_TAGFILE"));
00195   if (gen_tag)
00196   {
00197     gen_tag->setDefaultValue(QString(project()->projectName().remove(".kdevelop")+".tag").latin1());
00198     gen_tag->init();
00199   }
00200   
00201   // write doxy file
00202   QFile f2(fileName);
00203   if (!f2.open(IO_WriteOnly))
00204     KMessageBox::information(mainWindow()->main(), i18n("Cannot write Doxyfile."));
00205   else
00206   {
00207     Config::instance()->writeTemplate(&f2, true, true);
00208 
00209     f2.close();
00210   }
00211 }
00212 
00213 
00214 void DoxygenPart::slotDoxygen()
00215 {
00216     bool searchDatabase = false;
00217     QString outputDirectory;
00218     QString htmlDirectory;
00219 
00220     adjustDoxyfile();
00221 
00222     QString fileName = project()->projectDirectory() + "/Doxyfile";
00223 
00224     Config::instance()->init();
00225 
00226     QFile f(fileName);
00227     if (f.open(IO_ReadOnly))
00228     {
00229       QTextStream is(&f);
00230 
00231       Config::instance()->parse(QFile::encodeName(fileName));
00232       Config::instance()->convertStrToVal();
00233 
00234       f.close();
00235     }
00236 
00237     // search engine
00238     ConfigBool *search = dynamic_cast<ConfigBool*>(Config::instance()->get("SEARCHENGINE"));
00239     if (search)
00240     {
00241       searchDatabase = Config_getBool("SEARCHENGINE");
00242 
00243       if (searchDatabase)
00244       {
00245         // get input files
00246         outputDirectory = Config_getString("OUTPUT_DIRECTORY");
00247         if ( outputDirectory.isEmpty() == false )
00248           outputDirectory += "/";
00249         htmlDirectory   = Config_getString("HTML_OUTPUT");
00250         if ( htmlDirectory.isEmpty() == true )
00251           htmlDirectory = "html";
00252         htmlDirectory.prepend(outputDirectory);
00253       }
00254     }
00255 
00256     QString dir = project()->projectDirectory();
00257     QString cmdline = "cd ";
00258     cmdline += KShellProcess::quote( dir );
00259     cmdline += " && doxygen Doxyfile";
00260     if (searchDatabase)
00261     {
00262       // create search database in the same directory where the html docs are
00263       if ( htmlDirectory.length() > 0 )
00264         cmdline += " && cd " + KShellProcess::quote( htmlDirectory );
00265       cmdline += " && doxytag -s search.idx ";
00266     }
00267 
00268     kdDebug(9026) << "Doxygen command line: " << cmdline << endl;
00269 
00270     makeFrontend()->queueCommand(dir, cmdline);
00271 }
00272 
00273 
00274 void DoxygenPart::slotDoxClean()
00275 {
00276     bool could_be_dirty = false;
00277 
00278     QString outputDirectory = Config_getString("OUTPUT_DIRECTORY");
00279     if ( outputDirectory.isEmpty() )
00280         outputDirectory = project()->projectDirectory();
00281     if ( outputDirectory.right(1) != "/" )
00282         outputDirectory += "/";
00283     QString cmdline = "cd " + KShellProcess::quote( outputDirectory );
00284 
00285     if ( Config_getBool("GENERATE_HTML") ) {
00286         QString htmlDirectory   = Config_getString("HTML_OUTPUT");
00287         if ( htmlDirectory.isEmpty() )
00288             htmlDirectory = "html";
00289         if ( htmlDirectory.right(1) != "/" )
00290             htmlDirectory += "/";
00291         cmdline += " && rm -f " + KShellProcess::quote( htmlDirectory ) + "*";
00292         could_be_dirty= true;
00293     }
00294 
00295     if ( Config_getBool("GENERATE_LATEX") ) {
00296         QString latexDirectory   = Config_getString("LATEX_OUTPUT");
00297         if ( latexDirectory.isEmpty() )
00298             latexDirectory = "latex";
00299         if ( latexDirectory.right(1) != "/" )
00300             latexDirectory += "/";
00301         cmdline += " && rm -f " + KShellProcess::quote( latexDirectory ) + "*";
00302         could_be_dirty= true;
00303     }
00304 
00305     if ( Config_getBool("GENERATE_RTF") ) {
00306         QString rtfDirectory   = Config_getString("RTF_OUTPUT");
00307         if ( rtfDirectory.isEmpty() )
00308             rtfDirectory = "rtf";
00309         if ( rtfDirectory.right(1) != "/" )
00310             rtfDirectory += "/";
00311         cmdline += " && rm -f " + KShellProcess::quote( rtfDirectory ) + "*";
00312         could_be_dirty= true;
00313     }
00314 
00315     if ( Config_getBool("GENERATE_MAN") ) {
00316         QString manDirectory   = Config_getString("MAN_OUTPUT");
00317         if ( manDirectory.isEmpty() )
00318             manDirectory = "man";
00319         if ( manDirectory.right(1) != "/" )
00320             manDirectory += "/";
00321         cmdline += " && rm -f " + KShellProcess::quote( manDirectory ) + "*";
00322         could_be_dirty= true;
00323     }
00324 
00325     if ( Config_getBool("GENERATE_XML") ) {
00326         QString xmlDirectory   = Config_getString("XML_OUTPUT");
00327         if ( xmlDirectory.isEmpty() )
00328             xmlDirectory = "xml";
00329         if ( xmlDirectory.right(1) != "/" )
00330             xmlDirectory += "/";
00331         cmdline += " && rm -f " + KShellProcess::quote( xmlDirectory ) + "*";
00332         could_be_dirty= true;
00333     }
00334 
00335     if (could_be_dirty) {
00336         kdDebug(9026) << "Cleaning Doxygen generated API documentation using: " << cmdline << endl;
00337         makeFrontend()->queueCommand(KShellProcess::quote(project()->projectDirectory()), cmdline);
00338     }
00339     else
00340        kdDebug(9026) << "No Doxygen generated API documentation exists. There's nothing to clean!" << endl;
00341 
00342 }
00343 
00344 void DoxygenPart::slotPreviewProcessExited( )
00345 {
00346     partController()->showDocument(KURL(m_tmpDir.name()+"html/index.html"));
00347 }
00348 
00349 void DoxygenPart::slotRunPreview( )
00350 {
00351     if (m_file.isNull())
00352         return;
00353 
00354     if (m_process.isRunning()) {
00355         if ( KMessageBox::warningYesNo(mainWindow()->main(), i18n("Previous Doxygen process is still running.\nDo you want to cancel that process?"))  == KMessageBox::Yes )
00356             m_process.kill();
00357     else
00358             return;
00359     }
00360 
00361     m_process.clearArguments();
00362 
00363     m_tmpDir.unlink();
00364     m_tmpDir = KTempDir();
00365     m_tmpDir.setAutoDelete(true);
00366 
00367     Config* config = Config::instance();
00368 
00369     ConfigString* poDir = dynamic_cast<ConfigString*>(config->get("OUTPUT_DIRECTORY"));
00370     ConfigList* pInput = dynamic_cast<ConfigList*>(config->get("INPUT"));
00371     ConfigString* pHeader = dynamic_cast<ConfigString*>(config->get("HTML_HEADER"));
00372     ConfigString* pFooter = dynamic_cast<ConfigString*>(config->get("HTML_FOOTER"));
00373     ConfigString* pStyle = dynamic_cast<ConfigString*>(config->get("HTML_STYLESHEET"));
00374 
00375     //store config values to restore them later | override config values to get only the current file processed
00376     QCString dirVal;
00377     if (poDir != 0) {
00378         dirVal = *poDir->valueRef();
00379         *poDir->valueRef() = m_tmpDir.name().ascii();
00380     }
00381 
00382    QStrList inputVal;
00383     if (pInput != 0) {
00384         inputVal = *pInput->valueRef();
00385          QStrList xl;
00386          xl.append(m_file.ascii());
00387         *pInput->valueRef() = xl;
00388     } else {
00389         config->addList("INPUT", "# The INPUT tag can be used to specify the files and/or directories that contain\n"
00390                                                      "# documented source files. You may enter file names like \"myfile.cpp\" or\n"
00391                                                      "# directories like \"/usr/src/myproject\". Separate the files or directories\n"
00392                                                      "# with spaces.");
00393         pInput = dynamic_cast<ConfigList*>(config->get("INPUT")); //pinput now has to be != 0
00394         QStrList xl;
00395          xl.append(m_file.ascii());
00396         *pInput->valueRef() = xl;
00397     }
00398 
00399     QCString header;
00400     QCString footer;
00401     QCString stylesheet;
00402     //if header/footer/stylesheets are set, make sure they get found in the doxygen run
00403     QString projectDir = project()->projectDirectory();
00404     if (pHeader != 0 && !pHeader->valueRef()->isEmpty()){
00405         header = *pHeader->valueRef();
00406         QFileInfo info (header);
00407         if (info.isRelative())
00408             *pHeader->valueRef() = QString(projectDir + "/" + QString(header)).ascii();
00409         else
00410             header = 0;
00411     }
00412 
00413     if (pFooter != 0 && !pFooter->valueRef()->isEmpty()){
00414         footer = *pFooter->valueRef();
00415         QFileInfo info (footer);
00416         if (info.isRelative())
00417             *pFooter->valueRef() = QString(projectDir + "/" + QString(footer)).ascii();
00418         else
00419             footer = 0;
00420     }
00421 
00422     if (pStyle != 0 && !pStyle->valueRef()->isEmpty()){
00423         stylesheet = *pStyle->valueRef();
00424         QFileInfo info (stylesheet);
00425         if (info.isRelative())
00426             *pStyle->valueRef() = QString(projectDir +"/" + QString(stylesheet)).ascii();
00427         else
00428             stylesheet = 0;
00429     }
00430 
00431     QFile file(m_tmpDir.name() +"PreviewDoxyfile"); //file gets deleted automatically 'cause of tempdir
00432     if (!file.open(IO_WriteOnly)){
00433         //restore config values
00434         if (pInput != 0)
00435             *pInput->valueRef() = inputVal;
00436 
00437         if (poDir != 0)
00438             *poDir->valueRef() = dirVal;
00439 
00440         KMessageBox::error(mainWindow()->main(), i18n("Cannot create temporary file '%1'").arg(file.name()));
00441         return;
00442     }
00443 
00444     config->writeTemplate(&file, false, false);
00445 
00446     if (inputVal.count() == 0) //pInput is always != 0
00447         *pInput->valueRef() = QStrList();
00448     else
00449         *pInput->valueRef() = inputVal;
00450 
00451     if (poDir != 0)
00452         *poDir->valueRef() = dirVal;
00453 
00454     if (pHeader != 0 && !header.isNull())
00455         *pHeader->valueRef() = header;
00456 
00457     if (pFooter != 0 && !footer.isNull())
00458         *pFooter->valueRef() = footer;
00459 
00460     if (pStyle != 0 && !stylesheet.isNull())
00461         *pStyle->valueRef() = stylesheet;
00462 
00463     m_process << "doxygen" << file.name().ascii();
00464     m_process.start(KProcess::NotifyOnExit, KProcess::NoCommunication);
00465 
00466 }
00467 
00468 void DoxygenPart::slotActivePartChanged( KParts::Part * part )
00469 {
00470     // -> idea from cppsupportpart.cpp
00471     KTextEditor::Document* doc = dynamic_cast<KTextEditor::Document*>(part);
00472     if (doc != 0)
00473         m_file = doc->url().path();
00474     else
00475         m_file = QString::null;
00476     // <-
00477     m_activeEditor = dynamic_cast<KTextEditor::EditInterface*>(part);
00478     m_cursor = part ? dynamic_cast<KTextEditor::ViewCursorInterface*>(part->widget()) : 0;
00479 }
00480 
00481 void DoxygenPart::slotDocumentFunction(){
00482     if (m_activeEditor != 0 && m_cursor != 0){
00483         if ( codeModel()->hasFile( m_file ) ) {
00484             unsigned int cursorLine, cursorCol;
00485             m_cursor->cursorPosition(&cursorLine, &cursorCol);
00486 
00487             FunctionDom function = 0;
00488             FunctionDefinitionDom functionDef = 0;
00489 
00490             FileDom file = codeModel()->fileByName( m_file );
00491 
00492             FunctionList functionList = CodeModelUtils::allFunctions(file);
00493             FunctionList::ConstIterator theend = functionList.end();
00494             for( FunctionList::ConstIterator ci = functionList.begin(); ci!= theend; ++ci ){
00495                 int sline, scol;
00496                 int eline, ecol;
00497                 (*ci)->getStartPosition(&sline, &scol);
00498                 (*ci)->getEndPosition(&eline, &ecol);
00499                 if(cursorLine >= sline && cursorLine <= eline )
00500                     function = *ci;
00501             }
00502             if (function == 0){
00503                 FunctionDefinitionList functionDefList = CodeModelUtils::allFunctionDefinitionsDetailed(file).functionList;
00504                 FunctionDefinitionList::ConstIterator theend = functionDefList.end();
00505                 for( FunctionDefinitionList::ConstIterator ci = functionDefList.begin(); ci!= theend; ++ci ){
00506                     int sline, scol;
00507                     int eline, ecol;
00508                     (*ci)->getStartPosition(&sline, &scol);
00509                     (*ci)->getEndPosition(&eline, &ecol);
00510                     if(cursorLine >= sline && cursorLine <= eline)
00511                         functionDef = *ci;
00512                 }
00513             }
00514 
00515             int line, col;
00516             if (function != 0)
00517                 function->getStartPosition(&line, &col);
00518             else if (functionDef != 0)
00519                 functionDef->getStartPosition(&line, &col);
00520             else
00521                 return;
00522             QString funcLine = m_activeEditor->textLine(line);
00523             unsigned int pos = 0;
00524             unsigned int length = funcLine.length();
00525             while (pos < length && funcLine.at(pos).isSpace())
00526                 ++pos;
00527             //store chars used for indenting the line and put it in front of every created doc line
00528             QString indentChars = funcLine.left(pos);
00529             QString text = indentChars + "\n";
00544             m_activeEditor->insertText(line, 0, text);
00545             m_cursor->setCursorPosition( line + 1, indentChars.length() + 3);
00546         }
00547     }
00548 }
00549 
00550 
00551 #include "doxygenpart.moc"
KDE Logo
This file is part of the documentation for KDevelop Version 3.1.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Feb 22 09:22:40 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003