1 #include "doxywizard.h"
10 #include <QVBoxLayout>
13 #include <QTextBrowser>
17 #include <QCloseEvent>
18 #include <QApplication>
20 #include <QFileDialog>
21 #include <QDesktopServices>
23 #include <QTextStream>
30 #define MAX_RECENT_FILES 10
32 const int messageTimeout = 5000; //!< status bar message timeout in milliseconds.
34 #define APPQT(x) QString::fromLatin1("<qt><pre>") + x + QString::fromLatin1("</pre></qt>")
36 static QString text1 = QString::fromLatin1("");
38 MainWindow &MainWindow::instance()
40 static MainWindow *theInstance = new MainWindow;
44 MainWindow::MainWindow()
45 : m_settings(QString::fromLatin1("Doxygen.org"), QString::fromLatin1("Doxywizard"))
47 QMenu *file = menuBar()->addMenu(tr("File"));
48 file->addAction(tr("Open..."),
49 this, SLOT(openConfig()), Qt::CTRL+Qt::Key_O);
50 m_recentMenu = file->addMenu(tr("Open recent"));
51 file->addAction(tr("Save"),
52 this, SLOT(saveConfig()), Qt::CTRL+Qt::Key_S);
53 file->addAction(tr("Save as..."),
54 this, SLOT(saveConfigAs()), Qt::SHIFT+Qt::CTRL+Qt::Key_S);
55 file->addAction(tr("Quit"),
56 this, SLOT(quit()), Qt::CTRL+Qt::Key_Q);
58 QMenu *settings = menuBar()->addMenu(tr("Settings"));
59 settings->addAction(tr("Reset to factory defaults"),
60 this,SLOT(resetToDefaults()));
61 settings->addAction(tr("Use current settings at startup"),
62 this,SLOT(makeDefaults()));
63 settings->addAction(tr("Clear recent list"),
64 this,SLOT(clearRecent()));
66 QMenu *help = menuBar()->addMenu(tr("Help"));
67 help->addAction(tr("Online manual"),
68 this, SLOT(manual()), Qt::Key_F1);
69 help->addAction(tr("About"),
70 this, SLOT(about()) );
72 m_expert = new Expert;
73 m_wizard = new Wizard(m_expert->modelData());
75 // ----------- top part ------------------
76 QWidget *topPart = new QWidget;
77 QVBoxLayout *rowLayout = new QVBoxLayout(topPart);
79 // select working directory
80 QHBoxLayout *dirLayout = new QHBoxLayout;
81 m_workingDir = new QLineEdit;
82 m_selWorkingDir = new QPushButton(tr("Select..."));
83 dirLayout->addWidget(m_workingDir);
84 dirLayout->addWidget(m_selWorkingDir);
86 //------------- bottom part --------------
87 QWidget *runTab = new QWidget;
88 QVBoxLayout *runTabLayout = new QVBoxLayout(runTab);
91 QHBoxLayout *runLayout = new QHBoxLayout;
92 m_run = new QPushButton(tr("Run doxygen"));
93 m_run->setEnabled(false);
94 m_runStatus = new QLabel(tr("Status: not running"));
95 m_saveLog = new QPushButton(tr("Save log..."));
96 m_saveLog->setEnabled(false);
97 QPushButton *showSettings = new QPushButton(tr("Show configuration"));
98 runLayout->addWidget(m_run);
99 runLayout->addWidget(m_runStatus);
100 runLayout->addStretch(1);
101 runLayout->addWidget(showSettings);
102 runLayout->addWidget(m_saveLog);
104 // output produced by Doxygen
105 runTabLayout->addLayout(runLayout);
106 runTabLayout->addWidget(new QLabel(tr("Output produced by doxygen")));
107 QGridLayout *grid = new QGridLayout;
108 //m_outputLog = new QTextEdit;
109 m_outputLog = new QTextBrowser;
110 //m_outputLog = new QPlainTextEdit;
111 m_outputLog->setReadOnly(true);
112 m_outputLog->setFontFamily(QString::fromLatin1("courier"));
113 m_outputLog->setMinimumWidth(600);
114 grid->addWidget(m_outputLog,0,0);
115 grid->setColumnStretch(0,1);
116 grid->setRowStretch(0,1);
117 QHBoxLayout *launchLayout = new QHBoxLayout;
118 m_launchHtml = new QPushButton(tr("Show HTML output"));
119 launchLayout->addWidget(m_launchHtml);
121 launchLayout->addStretch(1);
122 grid->addLayout(launchLayout,1,0);
123 runTabLayout->addLayout(grid);
125 m_tabs = new QTabWidget;
126 m_tabs->addTab(m_wizard,tr("Wizard"));
127 m_tabs->addTab(m_expert,tr("Expert"));
128 m_tabs->addTab(runTab,tr("Run"));
130 rowLayout->addWidget(new QLabel(tr("Step 1: Specify the working directory from which doxygen will run")));
131 rowLayout->addLayout(dirLayout);
132 rowLayout->addWidget(new QLabel(tr("Step 2: Configure doxygen using the Wizard and/or Expert tab, then switch to the Run tab to generate the documentation")));
133 rowLayout->addWidget(m_tabs);
135 setCentralWidget(topPart);
136 statusBar()->showMessage(tr("Welcome to Doxygen"),messageTimeout);
138 m_runProcess = new QProcess;
140 m_timer = new QTimer;
142 // connect signals and slots
143 connect(m_tabs,SIGNAL(currentChanged(int)),SLOT(selectTab(int)));
144 connect(m_selWorkingDir,SIGNAL(clicked()),SLOT(selectWorkingDir()));
145 connect(m_recentMenu,SIGNAL(triggered(QAction*)),SLOT(openRecent(QAction*)));
146 connect(m_workingDir,SIGNAL(returnPressed()),SLOT(updateWorkingDir()));
147 connect(m_runProcess,SIGNAL(readyReadStandardOutput()),SLOT(readStdout()));
148 connect(m_runProcess,SIGNAL(finished(int, QProcess::ExitStatus)),SLOT(runComplete()));
149 connect(m_timer,SIGNAL(timeout()),SLOT(readStdout()));
150 connect(m_run,SIGNAL(clicked()),SLOT(runDoxygen()));
151 connect(m_launchHtml,SIGNAL(clicked()),SLOT(showHtmlOutput()));
152 connect(m_saveLog,SIGNAL(clicked()),SLOT(saveLog()));
153 connect(showSettings,SIGNAL(clicked()),SLOT(showSettings()));
154 connect(m_expert,SIGNAL(changed()),SLOT(configChanged()));
155 connect(m_wizard,SIGNAL(done()),SLOT(selectRunTab()));
156 connect(m_expert,SIGNAL(done()),SLOT(selectRunTab()));
159 updateLaunchButtonState();
165 void MainWindow::closeEvent(QCloseEvent *event)
167 if (discardUnsavedChanges())
178 void MainWindow::quit()
180 if (discardUnsavedChanges())
184 QApplication::exit(0);
187 void MainWindow::setWorkingDir(const QString &dirName)
189 QDir::setCurrent(dirName);
190 m_workingDir->setText(dirName);
191 m_run->setEnabled(!dirName.isEmpty());
194 void MainWindow::selectWorkingDir()
196 QString dirName = QFileDialog::getExistingDirectory(this,
197 tr("Select working directory"),m_workingDir->text());
198 if (!dirName.isEmpty())
200 setWorkingDir(dirName);
204 void MainWindow::updateWorkingDir()
206 setWorkingDir(m_workingDir->text());
209 void MainWindow::manual()
211 QDesktopServices::openUrl(QUrl(QString::fromLatin1("http://www.doxygen.org/manual.html")));
214 void MainWindow::about()
217 QTextStream t(&msg,QIODevice::WriteOnly);
218 t << QString::fromLatin1("<qt><center>A tool to configure and run doxygen version ")+
219 QString::fromLatin1(versionString)+
220 QString::fromLatin1(" on your source files.</center><p><br>"
221 "<center>Written by<br> Dimitri van Heesch<br>© 2000-2015</center><p>"
223 QMessageBox::about(this,tr("Doxygen GUI"),msg);
226 void MainWindow::openConfig()
228 if (discardUnsavedChanges(false))
230 QString fn = QFileDialog::getOpenFileName(this,
231 tr("Open configuration file"),
232 m_workingDir->text());
235 loadConfigFromFile(fn);
240 void MainWindow::updateConfigFileName(const QString &fileName)
242 if (m_fileName!=fileName)
244 m_fileName = fileName;
245 QString curPath = QFileInfo(fileName).path();
246 setWorkingDir(curPath);
247 addRecentFile(fileName);
252 void MainWindow::loadConfigFromFile(const QString & fileName)
254 // save full path info of original file
255 QString absFileName = QFileInfo(fileName).absoluteFilePath();
256 // updates the current directory
257 updateConfigFileName(fileName);
258 // open the specified configuration file
259 m_expert->loadConfig(absFileName);
261 updateLaunchButtonState();
266 void MainWindow::saveConfig(const QString &fileName)
268 if (fileName.isEmpty()) return;
270 if (!f.open(QIODevice::WriteOnly))
272 QMessageBox::warning(this,
274 tr("Error: cannot open the file ")+fileName+tr(" for writing!\n")+
275 tr("Reason given: ")+f.error());
279 m_expert->writeConfig(t,false);
280 updateConfigFileName(fileName);
285 bool MainWindow::saveConfig()
287 if (m_fileName.isEmpty())
289 return saveConfigAs();
293 saveConfig(m_fileName);
298 bool MainWindow::saveConfigAs()
300 QString fileName = QFileDialog::getSaveFileName(this, QString(),
301 m_workingDir->text()+QString::fromLatin1("/Doxyfile"));
302 if (fileName.isEmpty()) return false;
303 saveConfig(fileName);
307 void MainWindow::makeDefaults()
309 if (QMessageBox::question(this,tr("Use current setting at startup?"),
310 tr("Do you want to save the current settings "
311 "and use them next time Doxywizard starts?"),
313 QMessageBox::Cancel)==QMessageBox::Save)
315 //printf("MainWindow:makeDefaults()\n");
316 m_expert->saveSettings(&m_settings);
317 m_settings.setValue(QString::fromLatin1("wizard/loadsettings"), true);
322 void MainWindow::clearRecent()
324 if (QMessageBox::question(this,tr("Clear the list of recent files?"),
325 tr("Do you want to clear the list of recently "
326 "loaded configuration files?"),
328 QMessageBox::Cancel)==QMessageBox::Yes)
330 m_recentMenu->clear();
331 m_recentFiles.clear();
332 for (int i=0;i<MAX_RECENT_FILES;i++)
334 m_settings.setValue(QString().sprintf("recent/config%d",i++),QString::fromLatin1(""));
341 void MainWindow::resetToDefaults()
343 if (QMessageBox::question(this,tr("Reset settings to their default values?"),
344 tr("Do you want to revert all settings back "
345 "to their original values?"),
347 QMessageBox::Cancel)==QMessageBox::Reset)
349 //printf("MainWindow:resetToDefaults()\n");
350 m_expert->resetToDefaults();
351 m_settings.setValue(QString::fromLatin1("wizard/loadsettings"), false);
357 void MainWindow::loadSettings()
359 QVariant geometry = m_settings.value(QString::fromLatin1("main/geometry"), QVariant::Invalid);
360 QVariant state = m_settings.value(QString::fromLatin1("main/state"), QVariant::Invalid);
361 QVariant wizState = m_settings.value(QString::fromLatin1("wizard/state"), QVariant::Invalid);
362 QVariant loadSettings = m_settings.value(QString::fromLatin1("wizard/loadsettings"), QVariant::Invalid);
363 QVariant workingDir = m_settings.value(QString::fromLatin1("wizard/workingdir"), QVariant::Invalid);
365 if (geometry !=QVariant::Invalid) restoreGeometry(geometry.toByteArray());
366 if (state !=QVariant::Invalid) restoreState (state.toByteArray());
367 if (wizState !=QVariant::Invalid) m_wizard->restoreState(wizState.toByteArray());
368 if (loadSettings!=QVariant::Invalid && loadSettings.toBool())
370 m_expert->loadSettings(&m_settings);
371 if (workingDir!=QVariant::Invalid && QDir(workingDir.toString()).exists())
373 setWorkingDir(workingDir.toString());
377 /* due to prepend use list in reversed order */
378 for (int i=MAX_RECENT_FILES;i>=0;i--)
380 QString entry = m_settings.value(QString().sprintf("recent/config%d",i)).toString();
381 if (!entry.isEmpty() && QFileInfo(entry).exists())
383 addRecentFileList(entry);
390 void MainWindow::saveSettings()
392 QSettings settings(QString::fromLatin1("Doxygen.org"), QString::fromLatin1("Doxywizard"));
394 m_settings.setValue(QString::fromLatin1("main/geometry"), saveGeometry());
395 m_settings.setValue(QString::fromLatin1("main/state"), saveState());
396 m_settings.setValue(QString::fromLatin1("wizard/state"), m_wizard->saveState());
397 m_settings.setValue(QString::fromLatin1("wizard/workingdir"), m_workingDir->text());
400 void MainWindow::selectTab(int id)
402 if (id==0) m_wizard->refresh();
403 else if (id==1) m_expert->refresh();
406 void MainWindow::selectRunTab()
408 m_tabs->setCurrentIndex(2);
411 void MainWindow::addRecentFile(const QString &fileName)
413 addRecentFileList(fileName);
416 void MainWindow::addRecentFileList(const QString &fileName)
418 int i=m_recentFiles.indexOf(fileName);
419 if (i!=-1) m_recentFiles.removeAt(i);
422 if (m_recentFiles.count() < MAX_RECENT_FILES) // append
424 m_recentFiles.prepend(fileName);
426 else // add + drop last item
428 m_recentFiles.removeLast();
429 m_recentFiles.prepend(fileName);
432 void MainWindow::updateRecentFile(void)
434 m_recentMenu->clear();
436 foreach( QString str, m_recentFiles )
438 m_recentMenu->addAction(str);
439 m_settings.setValue(QString().sprintf("recent/config%d",i++),str);
441 for (;i<MAX_RECENT_FILES;i++)
443 m_settings.setValue(QString().sprintf("recent/config%d",i++),QString::fromLatin1(""));
447 void MainWindow::openRecent(QAction *action)
449 if (discardUnsavedChanges(false))
451 loadConfigFromFile(action->text());
455 void MainWindow::runDoxygen()
460 #if defined(Q_OS_MACX)
461 doxygenPath = qApp->applicationDirPath()+QString::fromLatin1("/../Resources/");
462 qDebug() << tr("Doxygen path: ") << doxygenPath;
463 if ( !QFile(doxygenPath + QString::fromLatin1("doxygen")).exists() )
465 // No Doxygen binary in the resources, if there is a system Doxygen binary, use that instead
466 if ( QFile(QString::fromLatin1("/usr/local/bin/doxygen")).exists() )
468 doxygenPath = QString::fromLatin1("/usr/local/bin/");
472 qDebug() << tr("Can't find the doxygen command, make sure it's in your $$PATH");
473 doxygenPath = QString::fromLatin1("");
476 qDebug() << tr("Getting doxygen from: ") << doxygenPath;
479 m_runProcess->setReadChannel(QProcess::StandardOutput);
480 m_runProcess->setProcessChannelMode(QProcess::MergedChannels);
481 m_runProcess->setWorkingDirectory(m_workingDir->text());
482 QStringList env=QProcess::systemEnvironment();
483 // set PWD environment variable to m_workingDir
484 env.replaceInStrings(QRegExp(QString::fromLatin1("^PWD=(.*)"),Qt::CaseInsensitive),
485 QString::fromLatin1("PWD=")+m_workingDir->text());
486 m_runProcess->setEnvironment(env);
489 args << QString::fromLatin1("-b"); // make stdout unbuffered
490 args << QString::fromLatin1("-"); // read config from stdin
492 m_outputLog->clear();
493 text1 = QString::fromLatin1("");
494 m_runProcess->start(doxygenPath + QString::fromLatin1("doxygen"), args);
496 if (!m_runProcess->waitForStarted())
498 m_outputLog->append(APPQT(QString::fromLatin1("*** Failed to run doxygen\n")));
501 QTextStream t(m_runProcess);
502 m_expert->writeConfig(t,false);
503 m_runProcess->closeWriteChannel();
505 if (m_runProcess->state() == QProcess::NotRunning)
507 m_outputLog->append(APPQT(QString::fromLatin1("*** Failed to run doxygen\n")));
511 m_saveLog->setEnabled(false);
513 m_run->setText(tr("Stop doxygen"));
514 m_runStatus->setText(tr("Status: running"));
515 m_timer->start(1000);
521 m_run->setText(tr("Run doxygen"));
522 m_runStatus->setText(tr("Status: not running"));
523 m_runProcess->kill();
525 //updateRunnable(m_workingDir->text());
529 void MainWindow::readStdout()
533 QByteArray data = m_runProcess->readAllStandardOutput();
534 QString text = QString::fromUtf8(data);
538 m_outputLog->clear();
539 m_outputLog->append(APPQT(text1.trimmed()));
544 void MainWindow::runComplete()
548 m_outputLog->append(APPQT(tr("*** Doxygen has finished\n")));
552 m_outputLog->append(APPQT(tr("*** Cancelled by user\n")));
554 m_outputLog->ensureCursorVisible();
555 m_run->setText(tr("Run doxygen"));
556 m_runStatus->setText(tr("Status: not running"));
558 updateLaunchButtonState();
559 //updateRunnable(m_workingDir->text());
560 m_saveLog->setEnabled(true);
563 void MainWindow::updateLaunchButtonState()
565 m_launchHtml->setEnabled(m_expert->htmlOutputPresent(m_workingDir->text()));
567 m_launchPdf->setEnabled(m_expert->pdfOutputPresent(m_workingDir->text()));
571 void MainWindow::showHtmlOutput()
573 QString indexFile = m_expert->getHtmlOutputIndex(m_workingDir->text());
574 QFileInfo fi(indexFile);
575 // TODO: the following doesn't seem to work with IE
577 //QString indexUrl(QString::fromLatin1("file:///"));
578 ShellExecute(NULL, L"open", (LPCWSTR)fi.absoluteFilePath().utf16(), NULL, NULL, SW_SHOWNORMAL);
580 QString indexUrl(QString::fromLatin1("file://"));
581 indexUrl+=fi.absoluteFilePath();
582 QDesktopServices::openUrl(QUrl(indexUrl));
586 void MainWindow::saveLog()
588 QString fn = QFileDialog::getSaveFileName(this, tr("Save log file"),
589 m_workingDir->text()+
590 QString::fromLatin1("/doxygen_log.txt"));
594 if (f.open(QIODevice::WriteOnly))
597 t << m_outputLog->toPlainText();
598 statusBar()->showMessage(tr("Output log saved"),messageTimeout);
602 QMessageBox::warning(0,tr("Warning"),
603 tr("Cannot open file ")+fn+tr(" for writing. Nothing saved!"),tr("ok"));
608 void MainWindow::showSettings()
611 QTextStream t(&text);
612 m_expert->writeConfig(t,true);
613 m_outputLog->clear();
614 m_outputLog->append(APPQT(text));
615 m_outputLog->ensureCursorVisible();
616 m_saveLog->setEnabled(true);
619 void MainWindow::configChanged()
625 void MainWindow::updateTitle()
627 QString title = tr("Doxygen GUI frontend");
630 title+=QString::fromLatin1(" +");
632 if (!m_fileName.isEmpty())
634 title+=QString::fromLatin1(" (")+m_fileName+QString::fromLatin1(")");
636 setWindowTitle(title);
639 bool MainWindow::discardUnsavedChanges(bool saveOption)
643 QMessageBox::StandardButton button;
646 button = QMessageBox::question(this,
647 tr("Unsaved changes"),
648 tr("Unsaved changes will be lost! Do you want to save the configuration file?"),
650 QMessageBox::Discard |
653 if (button==QMessageBox::Save)
660 button = QMessageBox::question(this,
661 tr("Unsaved changes"),
662 tr("Unsaved changes will be lost! Do you want to continue?"),
663 QMessageBox::Discard |
667 return button==QMessageBox::Discard;
672 //-----------------------------------------------------------------------
673 int main(int argc,char **argv)
675 QApplication a(argc,argv);
678 if (!qstrcmp(argv[1],"--help"))
681 msgBox.setText(QString().sprintf("Usage: %s [config file]",argv[0]));
689 msgBox.setText(QString().sprintf("Too many arguments specified\n\nUsage: %s [config file]",argv[0]));
695 MainWindow &main = MainWindow::instance();
696 if (argc==2 && argv[1][0]!='-') // name of config file as an argument
698 main.loadConfigFromFile(QString::fromLocal8Bit(argv[1]));