2 #include "doxywizard.h"
11 #define MAX_RECENT_FILES 10
13 const int messageTimeout = 5000; //!< status bar message timeout in millisec.
15 MainWindow &MainWindow::instance()
17 static MainWindow *theInstance = new MainWindow;
21 MainWindow::MainWindow()
22 : m_settings(QString::fromAscii("Doxygen.org"), QString::fromAscii("Doxywizard"))
24 QMenu *file = menuBar()->addMenu(tr("File"));
25 file->addAction(tr("Open..."),
26 this, SLOT(openConfig()), Qt::CTRL+Qt::Key_O);
27 m_recentMenu = file->addMenu(tr("Open recent"));
28 file->addAction(tr("Save"),
29 this, SLOT(saveConfig()), Qt::CTRL+Qt::Key_S);
30 file->addAction(tr("Save as..."),
31 this, SLOT(saveConfigAs()), Qt::SHIFT+Qt::CTRL+Qt::Key_S);
32 file->addAction(tr("Quit"),
33 this, SLOT(quit()), Qt::CTRL+Qt::Key_Q);
35 QMenu *settings = menuBar()->addMenu(tr("Settings"));
36 settings->addAction(tr("Reset to factory defaults"),
37 this,SLOT(resetToDefaults()));
38 settings->addAction(tr("Use current settings at startup"),
39 this,SLOT(makeDefaults()));
40 settings->addAction(tr("Clear recent list"),
41 this,SLOT(clearRecent()));
43 QMenu *help = menuBar()->addMenu(tr("Help"));
44 help->addAction(tr("Online manual"),
45 this, SLOT(manual()), Qt::Key_F1);
46 help->addAction(tr("About"),
47 this, SLOT(about()) );
49 m_expert = new Expert;
50 m_wizard = new Wizard(m_expert->modelData());
52 // ----------- top part ------------------
53 QWidget *topPart = new QWidget;
54 QVBoxLayout *rowLayout = new QVBoxLayout(topPart);
56 // select working directory
57 QHBoxLayout *dirLayout = new QHBoxLayout;
58 m_workingDir = new QLineEdit;
59 m_selWorkingDir = new QPushButton(tr("Select..."));
60 dirLayout->addWidget(m_workingDir);
61 dirLayout->addWidget(m_selWorkingDir);
63 //------------- bottom part --------------
64 QWidget *runTab = new QWidget;
65 QVBoxLayout *runTabLayout = new QVBoxLayout(runTab);
68 QHBoxLayout *runLayout = new QHBoxLayout;
69 m_run = new QPushButton(tr("Run doxygen"));
70 m_run->setEnabled(false);
71 m_runStatus = new QLabel(tr("Status: not running"));
72 m_saveLog = new QPushButton(tr("Save log..."));
73 m_saveLog->setEnabled(false);
74 QPushButton *showSettings = new QPushButton(tr("Show configuration"));
75 runLayout->addWidget(m_run);
76 runLayout->addWidget(m_runStatus);
77 runLayout->addStretch(1);
78 runLayout->addWidget(showSettings);
79 runLayout->addWidget(m_saveLog);
81 // output produced by doxygen
82 runTabLayout->addLayout(runLayout);
83 runTabLayout->addWidget(new QLabel(tr("Output produced by doxygen")));
84 QGridLayout *grid = new QGridLayout;
85 m_outputLog = new QTextEdit;
86 m_outputLog->setReadOnly(true);
87 m_outputLog->setFontFamily(QString::fromAscii("courier"));
88 m_outputLog->setMinimumWidth(600);
89 grid->addWidget(m_outputLog,0,0);
90 grid->setColumnStretch(0,1);
91 grid->setRowStretch(0,1);
92 QHBoxLayout *launchLayout = new QHBoxLayout;
93 m_launchHtml = new QPushButton(tr("Show HTML output"));
94 launchLayout->addWidget(m_launchHtml);
96 launchLayout->addStretch(1);
97 grid->addLayout(launchLayout,1,0);
98 runTabLayout->addLayout(grid);
100 m_tabs = new QTabWidget;
101 m_tabs->addTab(m_wizard,tr("Wizard"));
102 m_tabs->addTab(m_expert,tr("Expert"));
103 m_tabs->addTab(runTab,tr("Run"));
105 rowLayout->addWidget(new QLabel(tr("Step 1: Specify the working directory from which doxygen will run")));
106 rowLayout->addLayout(dirLayout);
107 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")));
108 rowLayout->addWidget(m_tabs);
110 setCentralWidget(topPart);
111 statusBar()->showMessage(tr("Welcome to Doxygen"),messageTimeout);
113 m_runProcess = new QProcess;
115 m_timer = new QTimer;
117 // connect signals and slots
118 connect(m_tabs,SIGNAL(currentChanged(int)),SLOT(selectTab(int)));
119 connect(m_selWorkingDir,SIGNAL(clicked()),SLOT(selectWorkingDir()));
120 connect(m_recentMenu,SIGNAL(triggered(QAction*)),SLOT(openRecent(QAction*)));
121 connect(m_workingDir,SIGNAL(returnPressed()),SLOT(updateWorkingDir()));
122 connect(m_runProcess,SIGNAL(readyReadStandardOutput()),SLOT(readStdout()));
123 connect(m_runProcess,SIGNAL(finished(int, QProcess::ExitStatus)),SLOT(runComplete()));
124 connect(m_timer,SIGNAL(timeout()),SLOT(readStdout()));
125 connect(m_run,SIGNAL(clicked()),SLOT(runDoxygen()));
126 connect(m_launchHtml,SIGNAL(clicked()),SLOT(showHtmlOutput()));
127 connect(m_saveLog,SIGNAL(clicked()),SLOT(saveLog()));
128 connect(showSettings,SIGNAL(clicked()),SLOT(showSettings()));
129 connect(m_expert,SIGNAL(changed()),SLOT(configChanged()));
130 connect(m_wizard,SIGNAL(done()),SLOT(selectRunTab()));
131 connect(m_expert,SIGNAL(done()),SLOT(selectRunTab()));
134 updateLaunchButtonState();
140 void MainWindow::closeEvent(QCloseEvent *event)
142 if (discardUnsavedChanges())
153 void MainWindow::quit()
155 if (discardUnsavedChanges())
159 QApplication::exit(0);
162 void MainWindow::setWorkingDir(const QString &dirName)
164 QDir::setCurrent(dirName);
165 m_workingDir->setText(dirName);
166 m_run->setEnabled(!dirName.isEmpty());
169 void MainWindow::selectWorkingDir()
171 QString dirName = QFileDialog::getExistingDirectory(this,
172 tr("Select working directory"),m_workingDir->text());
173 if (!dirName.isEmpty())
175 setWorkingDir(dirName);
179 void MainWindow::updateWorkingDir()
181 setWorkingDir(m_workingDir->text());
184 void MainWindow::manual()
186 QDesktopServices::openUrl(QUrl(QString::fromAscii("http://www.doxygen.org/manual.html")));
189 void MainWindow::about()
192 QTextStream t(&msg,QIODevice::WriteOnly);
193 t << QString::fromAscii("<qt><center>A tool to configure and run doxygen version ")+
194 QString::fromAscii(versionString)+
195 QString::fromAscii(" on your source files.</center><p><br>"
196 "<center>Written by<br> Dimitri van Heesch<br>© 2000-2014</center><p>"
198 QMessageBox::about(this,tr("Doxygen GUI"),msg);
201 void MainWindow::openConfig()
203 if (discardUnsavedChanges(false))
205 QString fn = QFileDialog::getOpenFileName(this,
206 tr("Open configuration file"),
207 m_workingDir->text());
210 loadConfigFromFile(fn);
215 void MainWindow::updateConfigFileName(const QString &fileName)
217 if (m_fileName!=fileName)
219 m_fileName = fileName;
220 QString curPath = QFileInfo(fileName).path();
221 setWorkingDir(curPath);
222 addRecentFile(fileName);
227 void MainWindow::loadConfigFromFile(const QString & fileName)
229 // save full path info of original file
230 QString absFileName = QFileInfo(fileName).absoluteFilePath();
231 // updates the current directory
232 updateConfigFileName(fileName);
233 // open the specified configuration file
234 m_expert->loadConfig(absFileName);
236 updateLaunchButtonState();
241 void MainWindow::saveConfig(const QString &fileName)
243 if (fileName.isEmpty()) return;
245 if (!f.open(QIODevice::WriteOnly))
247 QMessageBox::warning(this,
249 tr("Error: cannot open the file ")+fileName+tr(" for writing!\n")+
250 tr("Reason given: ")+f.error());
254 m_expert->writeConfig(t,false);
255 updateConfigFileName(fileName);
260 bool MainWindow::saveConfig()
262 if (m_fileName.isEmpty())
264 return saveConfigAs();
268 saveConfig(m_fileName);
273 bool MainWindow::saveConfigAs()
275 QString fileName = QFileDialog::getSaveFileName(this, QString(),
276 m_workingDir->text()+QString::fromAscii("/Doxyfile"));
277 if (fileName.isEmpty()) return false;
278 saveConfig(fileName);
282 void MainWindow::makeDefaults()
284 if (QMessageBox::question(this,tr("Use current setting at startup?"),
285 tr("Do you want to save the current settings "
286 "and use them next time Doxywizard starts?"),
288 QMessageBox::Cancel)==QMessageBox::Save)
290 //printf("MainWindow:makeDefaults()\n");
291 m_expert->saveSettings(&m_settings);
292 m_settings.setValue(QString::fromAscii("wizard/loadsettings"), true);
297 void MainWindow::clearRecent()
299 if (QMessageBox::question(this,tr("Clear the list of recent files?"),
300 tr("Do you want to clear the list of recently "
301 "loaded configuration files?"),
303 QMessageBox::Cancel)==QMessageBox::Yes)
305 m_recentMenu->clear();
306 m_recentFiles.clear();
307 for (int i=0;i<MAX_RECENT_FILES;i++)
309 m_settings.setValue(QString().sprintf("recent/config%d",i++),QString::fromAscii(""));
316 void MainWindow::resetToDefaults()
318 if (QMessageBox::question(this,tr("Reset settings to their default values?"),
319 tr("Do you want to revert all settings back "
320 "to their original values?"),
322 QMessageBox::Cancel)==QMessageBox::Reset)
324 //printf("MainWindow:resetToDefaults()\n");
325 m_expert->resetToDefaults();
326 m_settings.setValue(QString::fromAscii("wizard/loadsettings"), false);
332 void MainWindow::loadSettings()
334 QVariant geometry = m_settings.value(QString::fromAscii("main/geometry"), QVariant::Invalid);
335 QVariant state = m_settings.value(QString::fromAscii("main/state"), QVariant::Invalid);
336 QVariant wizState = m_settings.value(QString::fromAscii("wizard/state"), QVariant::Invalid);
337 QVariant loadSettings = m_settings.value(QString::fromAscii("wizard/loadsettings"), QVariant::Invalid);
338 QVariant workingDir = m_settings.value(QString::fromAscii("wizard/workingdir"), QVariant::Invalid);
340 if (geometry !=QVariant::Invalid) restoreGeometry(geometry.toByteArray());
341 if (state !=QVariant::Invalid) restoreState (state.toByteArray());
342 if (wizState !=QVariant::Invalid) m_wizard->restoreState(wizState.toByteArray());
343 if (loadSettings!=QVariant::Invalid && loadSettings.toBool())
345 m_expert->loadSettings(&m_settings);
346 if (workingDir!=QVariant::Invalid && QDir(workingDir.toString()).exists())
348 setWorkingDir(workingDir.toString());
352 for (int i=0;i<MAX_RECENT_FILES;i++)
354 QString entry = m_settings.value(QString().sprintf("recent/config%d",i)).toString();
355 if (!entry.isEmpty() && QFileInfo(entry).exists())
357 addRecentFile(entry);
363 void MainWindow::saveSettings()
365 QSettings settings(QString::fromAscii("Doxygen.org"), QString::fromAscii("Doxywizard"));
367 m_settings.setValue(QString::fromAscii("main/geometry"), saveGeometry());
368 m_settings.setValue(QString::fromAscii("main/state"), saveState());
369 m_settings.setValue(QString::fromAscii("wizard/state"), m_wizard->saveState());
370 m_settings.setValue(QString::fromAscii("wizard/workingdir"), m_workingDir->text());
373 void MainWindow::selectTab(int id)
375 if (id==0) m_wizard->refresh();
376 else if (id==1) m_expert->refresh();
379 void MainWindow::selectRunTab()
381 m_tabs->setCurrentIndex(2);
384 void MainWindow::addRecentFile(const QString &fileName)
386 int i=m_recentFiles.indexOf(fileName);
387 if (i!=-1) m_recentFiles.removeAt(i);
390 if (m_recentFiles.count() < MAX_RECENT_FILES) // append
392 m_recentFiles.prepend(fileName);
394 else // add + drop last item
396 m_recentFiles.removeLast();
397 m_recentFiles.prepend(fileName);
399 m_recentMenu->clear();
401 foreach( QString str, m_recentFiles )
403 m_recentMenu->addAction(str);
404 m_settings.setValue(QString().sprintf("recent/config%d",i++),str);
406 for (;i<MAX_RECENT_FILES;i++)
408 m_settings.setValue(QString().sprintf("recent/config%d",i++),QString::fromAscii(""));
412 void MainWindow::openRecent(QAction *action)
414 if (discardUnsavedChanges(false))
416 loadConfigFromFile(action->text());
420 void MainWindow::runDoxygen()
425 #if defined(Q_OS_MACX)
426 doxygenPath = qApp->applicationDirPath()+QString::fromAscii("/../Resources/");
427 qDebug() << tr("Doxygen path: ") << doxygenPath;
428 if ( !QFile(doxygenPath + QString::fromAscii("doxygen")).exists() )
430 // No doygen binary in the resources, if there is a system doxygen binary, use that instead
431 if ( QFile(QString::fromAscii("/usr/local/bin/doxygen")).exists() )
433 doxygenPath = QString::fromAscii("/usr/local/bin/");
437 qDebug() << tr("Can't find the doxygen command, make sure it's in your $$PATH");
438 doxygenPath = QString::fromAscii("");
441 qDebug() << tr("Getting doxygen from: ") << doxygenPath;
444 m_runProcess->setReadChannel(QProcess::StandardOutput);
445 m_runProcess->setProcessChannelMode(QProcess::MergedChannels);
446 m_runProcess->setWorkingDirectory(m_workingDir->text());
447 QStringList env=QProcess::systemEnvironment();
448 // set PWD environment variable to m_workingDir
449 env.replaceInStrings(QRegExp(QString::fromAscii("^PWD=(.*)"),Qt::CaseInsensitive),
450 QString::fromAscii("PWD=")+m_workingDir->text());
451 m_runProcess->setEnvironment(env);
454 args << QString::fromAscii("-b"); // make stdout unbuffered
455 args << QString::fromAscii("-"); // read config from stdin
457 m_outputLog->clear();
458 m_runProcess->start(doxygenPath + QString::fromAscii("doxygen"), args);
460 if (!m_runProcess->waitForStarted())
462 m_outputLog->append(QString::fromAscii("*** Failed to run doxygen\n"));
465 QTextStream t(m_runProcess);
466 m_expert->writeConfig(t,false);
467 m_runProcess->closeWriteChannel();
469 if (m_runProcess->state() == QProcess::NotRunning)
471 m_outputLog->append(QString::fromAscii("*** Failed to run doxygen\n"));
475 m_saveLog->setEnabled(false);
477 m_run->setText(tr("Stop doxygen"));
478 m_runStatus->setText(tr("Status: running"));
479 m_timer->start(1000);
485 m_run->setText(tr("Run doxygen"));
486 m_runStatus->setText(tr("Status: not running"));
487 m_runProcess->kill();
489 //updateRunnable(m_workingDir->text());
493 void MainWindow::readStdout()
497 QByteArray data = m_runProcess->readAllStandardOutput();
498 QString text = QString::fromUtf8(data);
501 m_outputLog->append(text.trimmed());
506 void MainWindow::runComplete()
510 m_outputLog->append(tr("*** Doxygen has finished\n"));
514 m_outputLog->append(tr("*** Cancelled by user\n"));
516 m_outputLog->ensureCursorVisible();
517 m_run->setText(tr("Run doxygen"));
518 m_runStatus->setText(tr("Status: not running"));
520 updateLaunchButtonState();
521 //updateRunnable(m_workingDir->text());
522 m_saveLog->setEnabled(true);
525 void MainWindow::updateLaunchButtonState()
527 m_launchHtml->setEnabled(m_expert->htmlOutputPresent(m_workingDir->text()));
529 m_launchPdf->setEnabled(m_expert->pdfOutputPresent(m_workingDir->text()));
533 void MainWindow::showHtmlOutput()
535 QString indexFile = m_expert->getHtmlOutputIndex(m_workingDir->text());
536 QFileInfo fi(indexFile);
537 // TODO: the following doesn't seem to work with IE
539 //QString indexUrl(QString::fromAscii("file:///"));
540 ShellExecute(NULL, L"open", (LPCWSTR)fi.absoluteFilePath().utf16(), NULL, NULL, SW_SHOWNORMAL);
542 QString indexUrl(QString::fromAscii("file://"));
543 indexUrl+=fi.absoluteFilePath();
544 QDesktopServices::openUrl(QUrl(indexUrl));
548 void MainWindow::saveLog()
550 QString fn = QFileDialog::getSaveFileName(this, tr("Save log file"),
551 m_workingDir->text()+
552 QString::fromAscii("/doxygen_log.txt"));
556 if (f.open(QIODevice::WriteOnly))
559 t << m_outputLog->toPlainText();
560 statusBar()->showMessage(tr("Output log saved"),messageTimeout);
564 QMessageBox::warning(0,tr("Warning"),
565 tr("Cannot open file ")+fn+tr(" for writing. Nothing saved!"),tr("ok"));
570 void MainWindow::showSettings()
573 QTextStream t(&text);
574 m_expert->writeConfig(t,true);
575 m_outputLog->clear();
576 m_outputLog->append(text);
577 m_outputLog->ensureCursorVisible();
578 m_saveLog->setEnabled(true);
581 void MainWindow::configChanged()
587 void MainWindow::updateTitle()
589 QString title = tr("Doxygen GUI frontend");
592 title+=QString::fromAscii(" +");
594 if (!m_fileName.isEmpty())
596 title+=QString::fromAscii(" (")+m_fileName+QString::fromAscii(")");
598 setWindowTitle(title);
601 bool MainWindow::discardUnsavedChanges(bool saveOption)
605 QMessageBox::StandardButton button;
608 button = QMessageBox::question(this,
609 tr("Unsaved changes"),
610 tr("Unsaved changes will be lost! Do you want to save the configuration file?"),
612 QMessageBox::Discard |
615 if (button==QMessageBox::Save)
622 button = QMessageBox::question(this,
623 tr("Unsaved changes"),
624 tr("Unsaved changes will be lost! Do you want to continue?"),
625 QMessageBox::Discard |
629 return button==QMessageBox::Discard;
634 //-----------------------------------------------------------------------
635 int main(int argc,char **argv)
637 QApplication a(argc,argv);
640 if (!qstrcmp(argv[1],"--help"))
643 msgBox.setText(QString().sprintf("Usage: %s [config file]",argv[0]));
651 msgBox.setText(QString().sprintf("Too many arguments specified\n\nUsage: %s [config file]",argv[0]));
657 MainWindow &main = MainWindow::instance();
658 if (argc==2 && argv[1][0]!='-') // name of config file as an argument
660 main.loadConfigFromFile(QString::fromLocal8Bit(argv[1]));