CMake/Source/QtDialog/CMakeSetupDialog.cxx

1376 lines
43 KiB
C++

/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "CMakeSetupDialog.h"
#include <QCloseEvent>
#include <QCoreApplication>
#include <QDialogButtonBox>
#include <QDragEnterEvent>
#include <QFileDialog>
#include <QInputDialog>
#include <QKeySequence>
#include <QMenu>
#include <QMenuBar>
#include <QMessageBox>
#include <QMimeData>
#include <QProgressBar>
#include <QSettings>
#include <QShortcut>
#include <QStatusBar>
#include <QToolButton>
#include <QUrl>
#include "AddCacheEntry.h"
#include "FirstConfigure.h"
#include "QCMake.h"
#include "QCMakeCacheView.h"
#include "RegexExplorer.h"
#include "WarningMessagesDialog.h"
#include "cmSystemTools.h"
#include "cmVersion.h"
QCMakeThread::QCMakeThread(QObject* p)
: QThread(p), CMakeInstance(NULL)
{
}
QCMake* QCMakeThread::cmakeInstance() const
{
return this->CMakeInstance;
}
void QCMakeThread::run()
{
this->CMakeInstance = new QCMake;
// emit that this cmake thread is ready for use
emit this->cmakeInitialized();
this->exec();
delete this->CMakeInstance;
this->CMakeInstance = NULL;
}
CMakeSetupDialog::CMakeSetupDialog()
: ExitAfterGenerate(true), CacheModified(false), ConfigureNeeded(true), CurrentState(Interrupting)
{
QString title = QString(tr("CMake %1"));
title = title.arg(cmVersion::GetCMakeVersion());
this->setWindowTitle(title);
// create the GUI
QSettings settings;
settings.beginGroup("Settings/StartPath");
restoreGeometry(settings.value("geometry").toByteArray());
restoreState(settings.value("windowState").toByteArray());
this->AddVariableNames = settings.value("AddVariableNames",
QStringList("CMAKE_INSTALL_PREFIX")).toStringList();
this->AddVariableTypes = settings.value("AddVariableTypes",
QStringList("PATH")).toStringList();
QWidget* cont = new QWidget(this);
this->setupUi(cont);
this->Splitter->setStretchFactor(0, 3);
this->Splitter->setStretchFactor(1, 1);
this->setCentralWidget(cont);
this->ProgressBar->reset();
this->RemoveEntry->setEnabled(false);
this->AddEntry->setEnabled(false);
QByteArray p = settings.value("SplitterSizes").toByteArray();
this->Splitter->restoreState(p);
bool groupView = settings.value("GroupView", false).toBool();
this->setGroupedView(groupView);
this->groupedCheck->setCheckState(groupView ? Qt::Checked : Qt::Unchecked);
bool advancedView = settings.value("AdvancedView", false).toBool();
this->setAdvancedView(advancedView);
this->advancedCheck->setCheckState(advancedView?Qt::Checked : Qt::Unchecked);
QMenu* FileMenu = this->menuBar()->addMenu(tr("&File"));
this->ReloadCacheAction = FileMenu->addAction(tr("&Reload Cache"));
QObject::connect(this->ReloadCacheAction, SIGNAL(triggered(bool)),
this, SLOT(doReloadCache()));
this->DeleteCacheAction = FileMenu->addAction(tr("&Delete Cache"));
QObject::connect(this->DeleteCacheAction, SIGNAL(triggered(bool)),
this, SLOT(doDeleteCache()));
this->ExitAction = FileMenu->addAction(tr("E&xit"));
this->ExitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
QObject::connect(this->ExitAction, SIGNAL(triggered(bool)),
this, SLOT(close()));
QMenu* ToolsMenu = this->menuBar()->addMenu(tr("&Tools"));
this->ConfigureAction = ToolsMenu->addAction(tr("&Configure"));
// prevent merging with Preferences menu item on Mac OS X
this->ConfigureAction->setMenuRole(QAction::NoRole);
QObject::connect(this->ConfigureAction, SIGNAL(triggered(bool)),
this, SLOT(doConfigure()));
this->GenerateAction = ToolsMenu->addAction(tr("&Generate"));
QObject::connect(this->GenerateAction, SIGNAL(triggered(bool)),
this, SLOT(doGenerate()));
QAction* showChangesAction = ToolsMenu->addAction(tr("&Show My Changes"));
QObject::connect(showChangesAction, SIGNAL(triggered(bool)),
this, SLOT(showUserChanges()));
#if defined(Q_WS_MAC) || defined(Q_OS_MAC)
this->InstallForCommandLineAction
= ToolsMenu->addAction(tr("&How to Install For Command Line Use"));
QObject::connect(this->InstallForCommandLineAction, SIGNAL(triggered(bool)),
this, SLOT(doInstallForCommandLine()));
#endif
ToolsMenu->addSeparator();
ToolsMenu->addAction(tr("Regular Expression Explorer..."),
this, SLOT(doRegexExplorerDialog()));
ToolsMenu->addSeparator();
ToolsMenu->addAction(tr("&Find in Output..."),
this, SLOT(doOutputFindDialog()),
QKeySequence::Find);
ToolsMenu->addAction(tr("Find Next"),
this, SLOT(doOutputFindNext()),
QKeySequence::FindNext);
ToolsMenu->addAction(tr("Find Previous"),
this, SLOT(doOutputFindPrev()),
QKeySequence::FindPrevious);
ToolsMenu->addAction(tr("Goto Next Error"),
this, SLOT(doOutputErrorNext()),
QKeySequence(Qt::Key_F8)); // in Visual Studio
new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Period),
this, SLOT(doOutputErrorNext())); // in Eclipse
QMenu* OptionsMenu = this->menuBar()->addMenu(tr("&Options"));
OptionsMenu->addAction(tr("Warning Messages..."),
this, SLOT(doWarningMessagesDialog()));
this->WarnUninitializedAction =
OptionsMenu->addAction(tr("&Warn Uninitialized (--warn-uninitialized)"));
this->WarnUninitializedAction->setCheckable(true);
this->WarnUnusedAction =
OptionsMenu->addAction(tr("&Warn Unused (--warn-unused-vars)"));
this->WarnUnusedAction->setCheckable(true);
QAction* debugAction = OptionsMenu->addAction(tr("&Debug Output"));
debugAction->setCheckable(true);
QObject::connect(debugAction, SIGNAL(toggled(bool)),
this, SLOT(setDebugOutput(bool)));
OptionsMenu->addSeparator();
QAction* expandAction = OptionsMenu->addAction(tr("&Expand Grouped Entries"));
QObject::connect(expandAction, SIGNAL(triggered(bool)),
this->CacheValues, SLOT(expandAll()));
QAction* collapseAction = OptionsMenu->addAction(tr("&Collapse Grouped Entries"));
QObject::connect(collapseAction, SIGNAL(triggered(bool)),
this->CacheValues, SLOT(collapseAll()));
QMenu* HelpMenu = this->menuBar()->addMenu(tr("&Help"));
QAction* a = HelpMenu->addAction(tr("About"));
QObject::connect(a, SIGNAL(triggered(bool)),
this, SLOT(doAbout()));
a = HelpMenu->addAction(tr("Help"));
QObject::connect(a, SIGNAL(triggered(bool)),
this, SLOT(doHelp()));
this->setAcceptDrops(true);
// get the saved binary directories
QStringList buildPaths = this->loadBuildPaths();
this->BinaryDirectory->addItems(buildPaths);
this->BinaryDirectory->setCompleter(new QCMakeFileCompleter(this, true));
this->SourceDirectory->setCompleter(new QCMakeFileCompleter(this, true));
// fixed pitch font in output window
QFont outputFont("Courier");
this->Output->setFont(outputFont);
this->ErrorFormat.setForeground(QBrush(Qt::red));
this->Output->setContextMenuPolicy(Qt::CustomContextMenu);
connect(this->Output, SIGNAL(customContextMenuRequested(const QPoint&)),
this, SLOT(doOutputContextMenu(const QPoint &)));
// start the cmake worker thread
this->CMakeThread = new QCMakeThread(this);
QObject::connect(this->CMakeThread, SIGNAL(cmakeInitialized()),
this, SLOT(initialize()), Qt::QueuedConnection);
this->CMakeThread->start();
this->enterState(ReadyConfigure);
ProgressOffset = 0.0;
ProgressFactor = 1.0;
}
void CMakeSetupDialog::initialize()
{
// now the cmake worker thread is running, lets make our connections to it
QObject::connect(this->CMakeThread->cmakeInstance(),
SIGNAL(propertiesChanged(const QCMakePropertyList&)),
this->CacheValues->cacheModel(),
SLOT(setProperties(const QCMakePropertyList&)));
QObject::connect(this->ConfigureButton, SIGNAL(clicked(bool)),
this, SLOT(doConfigure()));
QObject::connect(this->CMakeThread->cmakeInstance(), SIGNAL(configureDone(int)),
this, SLOT(exitLoop(int)));
QObject::connect(this->CMakeThread->cmakeInstance(), SIGNAL(generateDone(int)),
this, SLOT(exitLoop(int)));
QObject::connect(this->GenerateButton, SIGNAL(clicked(bool)),
this, SLOT(doGenerate()));
QObject::connect(this->BrowseSourceDirectoryButton, SIGNAL(clicked(bool)),
this, SLOT(doSourceBrowse()));
QObject::connect(this->BrowseBinaryDirectoryButton, SIGNAL(clicked(bool)),
this, SLOT(doBinaryBrowse()));
QObject::connect(this->BinaryDirectory, SIGNAL(editTextChanged(QString)),
this, SLOT(onBinaryDirectoryChanged(QString)));
QObject::connect(this->SourceDirectory, SIGNAL(textChanged(QString)),
this, SLOT(onSourceDirectoryChanged(QString)));
QObject::connect(this->CMakeThread->cmakeInstance(),
SIGNAL(sourceDirChanged(QString)),
this, SLOT(updateSourceDirectory(QString)));
QObject::connect(this->CMakeThread->cmakeInstance(),
SIGNAL(binaryDirChanged(QString)),
this, SLOT(updateBinaryDirectory(QString)));
QObject::connect(this->CMakeThread->cmakeInstance(),
SIGNAL(progressChanged(QString, float)),
this, SLOT(showProgress(QString,float)));
QObject::connect(this->CMakeThread->cmakeInstance(),
SIGNAL(errorMessage(QString)),
this, SLOT(error(QString)));
QObject::connect(this->CMakeThread->cmakeInstance(),
SIGNAL(outputMessage(QString)),
this, SLOT(message(QString)));
QObject::connect(this->groupedCheck, SIGNAL(toggled(bool)),
this, SLOT(setGroupedView(bool)));
QObject::connect(this->advancedCheck, SIGNAL(toggled(bool)),
this, SLOT(setAdvancedView(bool)));
QObject::connect(this->Search, SIGNAL(textChanged(QString)),
this, SLOT(setSearchFilter(QString)));
QObject::connect(this->CMakeThread->cmakeInstance(),
SIGNAL(generatorChanged(QString)),
this, SLOT(updateGeneratorLabel(QString)));
this->updateGeneratorLabel(QString());
QObject::connect(this->CacheValues->cacheModel(),
SIGNAL(dataChanged(QModelIndex,QModelIndex)),
this, SLOT(setCacheModified()));
QObject::connect(this->CacheValues->selectionModel(),
SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SLOT(selectionChanged()));
QObject::connect(this->RemoveEntry, SIGNAL(clicked(bool)),
this, SLOT(removeSelectedCacheEntries()));
QObject::connect(this->AddEntry, SIGNAL(clicked(bool)),
this, SLOT(addCacheEntry()));
QObject::connect(this->WarnUninitializedAction, SIGNAL(triggered(bool)),
this->CMakeThread->cmakeInstance(),
SLOT(setWarnUninitializedMode(bool)));
QObject::connect(this->WarnUnusedAction, SIGNAL(triggered(bool)),
this->CMakeThread->cmakeInstance(),
SLOT(setWarnUnusedMode(bool)));
if(!this->SourceDirectory->text().isEmpty() ||
!this->BinaryDirectory->lineEdit()->text().isEmpty())
{
this->onSourceDirectoryChanged(this->SourceDirectory->text());
this->onBinaryDirectoryChanged(this->BinaryDirectory->lineEdit()->text());
}
else
{
this->onBinaryDirectoryChanged(this->BinaryDirectory->lineEdit()->text());
}
}
CMakeSetupDialog::~CMakeSetupDialog()
{
QSettings settings;
settings.beginGroup("Settings/StartPath");
settings.setValue("windowState", QVariant(saveState()));
settings.setValue("geometry", QVariant(saveGeometry()));
settings.setValue("SplitterSizes", this->Splitter->saveState());
// wait for thread to stop
this->CMakeThread->quit();
this->CMakeThread->wait();
}
bool CMakeSetupDialog::prepareConfigure()
{
// make sure build directory exists
QString bindir = this->CMakeThread->cmakeInstance()->binaryDirectory();
QDir dir(bindir);
if(!dir.exists())
{
QString msg = tr("Build directory does not exist, "
"should I create it?\n\n"
"Directory: ");
msg += bindir;
QString title = tr("Create Directory");
QMessageBox::StandardButton btn;
btn = QMessageBox::information(this, title, msg,
QMessageBox::Yes | QMessageBox::No);
if(btn == QMessageBox::No)
{
return false;
}
if(!dir.mkpath("."))
{
QMessageBox::information(this, tr("Create Directory Failed"),
QString(tr("Failed to create directory %1")).arg(dir.path()),
QMessageBox::Ok);
return false;
}
}
// if no generator, prompt for it and other setup stuff
if(this->CMakeThread->cmakeInstance()->generator().isEmpty())
{
if(!this->setupFirstConfigure())
{
return false;
}
}
// remember path
this->addBinaryPath(dir.absolutePath());
return true;
}
void CMakeSetupDialog::exitLoop(int err)
{
this->LocalLoop.exit(err);
}
void CMakeSetupDialog::doConfigure()
{
if(this->CurrentState == Configuring)
{
// stop configure
doInterrupt();
return;
}
if(!prepareConfigure())
{
return;
}
this->enterState(Configuring);
bool ret = doConfigureInternal();
if(ret)
{
this->ConfigureNeeded = false;
}
if(ret && !this->CacheValues->cacheModel()->newPropertyCount())
{
this->enterState(ReadyGenerate);
}
else
{
this->enterState(ReadyConfigure);
this->CacheValues->scrollToTop();
}
this->ProgressBar->reset();
}
bool CMakeSetupDialog::doConfigureInternal()
{
this->Output->clear();
this->CacheValues->selectionModel()->clear();
QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
"setProperties", Qt::QueuedConnection,
Q_ARG(QCMakePropertyList,
this->CacheValues->cacheModel()->properties()));
QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
"configure", Qt::QueuedConnection);
int err = this->LocalLoop.exec();
if(err != 0)
{
QMessageBox::critical(this, tr("Error"),
tr("Error in configuration process, project files may be invalid"),
QMessageBox::Ok);
}
return 0 == err;
}
void CMakeSetupDialog::doInstallForCommandLine()
{
QString title = tr("How to Install For Command Line Use");
QString msg = tr(
"One may add CMake to the PATH:\n"
"\n"
" PATH=\"%1\":\"$PATH\"\n"
"\n"
"Or, to install symlinks to '/usr/local/bin', run:\n"
"\n"
" sudo \"%2\" --install\n"
"\n"
"Or, to install symlinks to another directory, run:\n"
"\n"
" sudo \"%3\" --install=/path/to/bin\n"
);
msg = msg.arg(cmSystemTools::GetFilenamePath(
cmSystemTools::GetCMakeCommand()).c_str());
msg = msg.arg(cmSystemTools::GetCMakeGUICommand().c_str());
msg = msg.arg(cmSystemTools::GetCMakeGUICommand().c_str());
QDialog dialog;
dialog.setWindowTitle(title);
QVBoxLayout* l = new QVBoxLayout(&dialog);
QLabel* lab = new QLabel(&dialog);
l->addWidget(lab);
lab->setText(msg);
lab->setWordWrap(false);
lab->setTextInteractionFlags(Qt::TextSelectableByMouse);
QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Ok,
Qt::Horizontal, &dialog);
QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept()));
l->addWidget(btns);
dialog.exec();
}
bool CMakeSetupDialog::doGenerateInternal()
{
QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
"generate", Qt::QueuedConnection);
int err = this->LocalLoop.exec();
if(err != 0)
{
QMessageBox::critical(this, tr("Error"),
tr("Error in generation process, project files may be invalid"),
QMessageBox::Ok);
}
return 0 == err;
}
void CMakeSetupDialog::doGenerate()
{
if(this->CurrentState == Generating)
{
// stop generate
doInterrupt();
return;
}
// see if we need to configure
// we'll need to configure if:
// the configure step hasn't been done yet
// generate was the last step done
if(this->ConfigureNeeded)
{
if(!prepareConfigure())
{
return;
}
}
this->enterState(Generating);
bool config_passed = true;
if(this->ConfigureNeeded)
{
this->CacheValues->cacheModel()->setShowNewProperties(false);
this->ProgressFactor = 0.5;
config_passed = doConfigureInternal();
this->ProgressOffset = 0.5;
}
if(config_passed)
{
doGenerateInternal();
}
this->ProgressOffset = 0.0;
this->ProgressFactor = 1.0;
this->CacheValues->cacheModel()->setShowNewProperties(true);
this->enterState(ReadyConfigure);
this->ProgressBar->reset();
this->ConfigureNeeded = true;
}
void CMakeSetupDialog::closeEvent(QCloseEvent* e)
{
// prompt for close if there are unsaved changes, and we're not busy
if(this->CacheModified)
{
QString msg = tr("You have changed options but not rebuilt, "
"are you sure you want to exit?");
QString title = tr("Confirm Exit");
QMessageBox::StandardButton btn;
btn = QMessageBox::critical(this, title, msg,
QMessageBox::Yes | QMessageBox::No);
if(btn == QMessageBox::No)
{
e->ignore();
}
}
// don't close if we're busy, unless the user really wants to
if(this->CurrentState == Configuring)
{
QString msg = tr("You are in the middle of a Configure.\n"
"If you Exit now the configure information will be lost.\n"
"Are you sure you want to Exit?");
QString title = tr("Confirm Exit");
QMessageBox::StandardButton btn;
btn = QMessageBox::critical(this, title, msg,
QMessageBox::Yes | QMessageBox::No);
if(btn == QMessageBox::No)
{
e->ignore();
}
else
{
this->doInterrupt();
}
}
// let the generate finish
if(this->CurrentState == Generating)
{
e->ignore();
}
}
void CMakeSetupDialog::doHelp()
{
QString msg = tr("CMake is used to configure and generate build files for "
"software projects. The basic steps for configuring a project are as "
"follows:\r\n\r\n1. Select the source directory for the project. This should "
"contain the CMakeLists.txt files for the project.\r\n\r\n2. Select the build "
"directory for the project. This is the directory where the project will be "
"built. It can be the same or a different directory than the source "
"directory. For easy clean up, a separate build directory is recommended. "
"CMake will create the directory if it does not exist.\r\n\r\n3. Once the "
"source and binary directories are selected, it is time to press the "
"Configure button. This will cause CMake to read all of the input files and "
"discover all the variables used by the project. The first time a variable "
"is displayed it will be in Red. Users should inspect red variables making "
"sure the values are correct. For some projects the Configure process can "
"be iterative, so continue to press the Configure button until there are no "
"longer red entries.\r\n\r\n4. Once there are no longer red entries, you "
"should click the Generate button. This will write the build files to the build "
"directory.");
QDialog dialog;
QFontMetrics met(this->font());
int msgWidth = met.width(msg);
dialog.setMinimumSize(msgWidth/15,20);
dialog.setWindowTitle(tr("Help"));
QVBoxLayout* l = new QVBoxLayout(&dialog);
QLabel* lab = new QLabel(&dialog);
lab->setText(msg);
lab->setWordWrap(true);
QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Ok,
Qt::Horizontal, &dialog);
QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept()));
l->addWidget(lab);
l->addWidget(btns);
dialog.exec();
}
void CMakeSetupDialog::doInterrupt()
{
this->enterState(Interrupting);
this->CMakeThread->cmakeInstance()->interrupt();
}
void CMakeSetupDialog::doSourceBrowse()
{
QString dir = QFileDialog::getExistingDirectory(this,
tr("Enter Path to Source"), this->SourceDirectory->text(),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if(!dir.isEmpty())
{
this->setSourceDirectory(dir);
}
}
void CMakeSetupDialog::updateSourceDirectory(const QString& dir)
{
if(this->SourceDirectory->text() != dir)
{
this->SourceDirectory->blockSignals(true);
this->SourceDirectory->setText(dir);
this->SourceDirectory->blockSignals(false);
}
}
void CMakeSetupDialog::updateBinaryDirectory(const QString& dir)
{
if(this->BinaryDirectory->currentText() != dir)
{
this->BinaryDirectory->blockSignals(true);
this->BinaryDirectory->setEditText(dir);
this->BinaryDirectory->blockSignals(false);
}
}
void CMakeSetupDialog::doBinaryBrowse()
{
QString dir = QFileDialog::getExistingDirectory(this,
tr("Enter Path to Build"), this->BinaryDirectory->currentText(),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if(!dir.isEmpty() && dir != this->BinaryDirectory->currentText())
{
this->setBinaryDirectory(dir);
}
}
void CMakeSetupDialog::setBinaryDirectory(const QString& dir)
{
this->BinaryDirectory->setEditText(dir);
}
void CMakeSetupDialog::onSourceDirectoryChanged(const QString& dir)
{
this->Output->clear();
QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
"setSourceDirectory", Qt::QueuedConnection, Q_ARG(QString, dir));
}
void CMakeSetupDialog::onBinaryDirectoryChanged(const QString& dir)
{
QString title = QString(tr("CMake %1 - %2"));
title = title.arg(cmVersion::GetCMakeVersion());
title = title.arg(dir);
this->setWindowTitle(title);
this->CacheModified = false;
this->CacheValues->cacheModel()->clear();
qobject_cast<QCMakeCacheModelDelegate*>(this->CacheValues->itemDelegate())->clearChanges();
this->Output->clear();
QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
"setBinaryDirectory", Qt::QueuedConnection, Q_ARG(QString, dir));
}
void CMakeSetupDialog::setSourceDirectory(const QString& dir)
{
this->SourceDirectory->setText(dir);
}
void CMakeSetupDialog::showProgress(const QString& /*msg*/, float percent)
{
percent = (percent * ProgressFactor) + ProgressOffset;
this->ProgressBar->setValue(qRound(percent * 100));
}
void CMakeSetupDialog::error(const QString& msg)
{
this->Output->setCurrentCharFormat(this->ErrorFormat);
//QTextEdit will terminate the msg with a ParagraphSeparator, but it also replaces
//all newlines with ParagraphSeparators. By replacing the newlines by ourself, one
//error msg will be one paragraph.
QString paragraph(msg);
paragraph.replace(QLatin1Char('\n'), QChar::LineSeparator);
this->Output->append(paragraph);
}
void CMakeSetupDialog::message(const QString& msg)
{
this->Output->setCurrentCharFormat(this->MessageFormat);
this->Output->append(msg);
}
void CMakeSetupDialog::setEnabledState(bool enabled)
{
// disable parts of the GUI during configure/generate
this->CacheValues->cacheModel()->setEditEnabled(enabled);
this->SourceDirectory->setEnabled(enabled);
this->BrowseSourceDirectoryButton->setEnabled(enabled);
this->BinaryDirectory->setEnabled(enabled);
this->BrowseBinaryDirectoryButton->setEnabled(enabled);
this->ReloadCacheAction->setEnabled(enabled);
this->DeleteCacheAction->setEnabled(enabled);
this->ExitAction->setEnabled(enabled);
this->ConfigureAction->setEnabled(enabled);
this->AddEntry->setEnabled(enabled);
this->RemoveEntry->setEnabled(false); // let selection re-enable it
}
bool CMakeSetupDialog::setupFirstConfigure()
{
FirstConfigure dialog;
// initialize dialog and restore saved settings
// add generators
dialog.setGenerators(this->CMakeThread->cmakeInstance()->availableGenerators());
// restore from settings
dialog.loadFromSettings();
if(dialog.exec() == QDialog::Accepted)
{
dialog.saveToSettings();
this->CMakeThread->cmakeInstance()->setGenerator(dialog.getGenerator());
this->CMakeThread->cmakeInstance()->setToolset(dialog.getToolset());
QCMakeCacheModel* m = this->CacheValues->cacheModel();
if(dialog.compilerSetup())
{
QString fortranCompiler = dialog.getFortranCompiler();
if(!fortranCompiler.isEmpty())
{
m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_Fortran_COMPILER",
"Fortran compiler.", fortranCompiler, false);
}
QString cxxCompiler = dialog.getCXXCompiler();
if(!cxxCompiler.isEmpty())
{
m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_CXX_COMPILER",
"CXX compiler.", cxxCompiler, false);
}
QString cCompiler = dialog.getCCompiler();
if(!cCompiler.isEmpty())
{
m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_C_COMPILER",
"C compiler.", cCompiler, false);
}
}
else if(dialog.crossCompilerSetup())
{
QString fortranCompiler = dialog.getFortranCompiler();
if(!fortranCompiler.isEmpty())
{
m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_Fortran_COMPILER",
"Fortran compiler.", fortranCompiler, false);
}
QString mode = dialog.getCrossIncludeMode();
m->insertProperty(QCMakeProperty::STRING, "CMAKE_FIND_ROOT_PATH_MODE_INCLUDE",
tr("CMake Find Include Mode"), mode, false);
mode = dialog.getCrossLibraryMode();
m->insertProperty(QCMakeProperty::STRING, "CMAKE_FIND_ROOT_PATH_MODE_LIBRARY",
tr("CMake Find Library Mode"), mode, false);
mode = dialog.getCrossProgramMode();
m->insertProperty(QCMakeProperty::STRING, "CMAKE_FIND_ROOT_PATH_MODE_PROGRAM",
tr("CMake Find Program Mode"), mode, false);
QString rootPath = dialog.getCrossRoot();
m->insertProperty(QCMakeProperty::PATH, "CMAKE_FIND_ROOT_PATH",
tr("CMake Find Root Path"), rootPath, false);
QString systemName = dialog.getSystemName();
m->insertProperty(QCMakeProperty::STRING, "CMAKE_SYSTEM_NAME",
tr("CMake System Name"), systemName, false);
QString systemVersion = dialog.getSystemVersion();
m->insertProperty(QCMakeProperty::STRING, "CMAKE_SYSTEM_VERSION",
tr("CMake System Version"), systemVersion, false);
QString cxxCompiler = dialog.getCXXCompiler();
m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_CXX_COMPILER",
tr("CXX compiler."), cxxCompiler, false);
QString cCompiler = dialog.getCCompiler();
m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_C_COMPILER",
tr("C compiler."), cCompiler, false);
}
else if(dialog.crossCompilerToolChainFile())
{
QString toolchainFile = dialog.getCrossCompilerToolChainFile();
m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_TOOLCHAIN_FILE",
tr("Cross Compile ToolChain File"), toolchainFile, false);
}
return true;
}
return false;
}
void CMakeSetupDialog::updateGeneratorLabel(const QString& gen)
{
QString str = tr("Current Generator: ");
if(gen.isEmpty())
{
str += tr("None");
}
else
{
str += gen;
}
this->Generator->setText(str);
}
void CMakeSetupDialog::doReloadCache()
{
QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
"reloadCache", Qt::QueuedConnection);
}
void CMakeSetupDialog::doDeleteCache()
{
QString title = tr("Delete Cache");
QString msg = tr("Are you sure you want to delete the cache?");
QMessageBox::StandardButton btn;
btn = QMessageBox::information(this, title, msg,
QMessageBox::Yes | QMessageBox::No);
if(btn == QMessageBox::No)
{
return;
}
QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
"deleteCache", Qt::QueuedConnection);
}
void CMakeSetupDialog::doAbout()
{
QString msg = tr(
"CMake %1 (cmake.org).\n"
"CMake suite maintained and supported by Kitware (kitware.com/cmake).\n"
"Distributed under terms of the BSD 3-Clause License.\n"
"\n"
"CMake GUI maintained by csimsoft,\n"
"built using Qt %2 (qt-project.org).\n"
#ifdef CMake_GUI_DISTRIBUTE_WITH_Qt_LGPL
"\n"
"The Qt Toolkit is Copyright (C) Digia Plc and/or its subsidiary(-ies).\n"
"Qt is licensed under terms of the GNU LGPLv2.1, available at:\n"
" \"%3\""
#endif
);
msg = msg.arg(cmVersion::GetCMakeVersion());
msg = msg.arg(qVersion());
#ifdef CMake_GUI_DISTRIBUTE_WITH_Qt_LGPL
std::string lgpl = cmSystemTools::GetCMakeRoot()+"/Licenses/LGPLv2.1.txt";
msg = msg.arg(lgpl.c_str());
#endif
QDialog dialog;
dialog.setWindowTitle(tr("About"));
QVBoxLayout* l = new QVBoxLayout(&dialog);
QLabel* lab = new QLabel(&dialog);
l->addWidget(lab);
lab->setText(msg);
lab->setWordWrap(true);
QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Ok,
Qt::Horizontal, &dialog);
QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept()));
l->addWidget(btns);
dialog.exec();
}
void CMakeSetupDialog::setExitAfterGenerate(bool b)
{
this->ExitAfterGenerate = b;
}
void CMakeSetupDialog::addBinaryPath(const QString& path)
{
QString cleanpath = QDir::cleanPath(path);
// update UI
this->BinaryDirectory->blockSignals(true);
int idx = this->BinaryDirectory->findText(cleanpath);
if(idx != -1)
{
this->BinaryDirectory->removeItem(idx);
}
this->BinaryDirectory->insertItem(0, cleanpath);
this->BinaryDirectory->setCurrentIndex(0);
this->BinaryDirectory->blockSignals(false);
// save to registry
QStringList buildPaths = this->loadBuildPaths();
buildPaths.removeAll(cleanpath);
buildPaths.prepend(cleanpath);
this->saveBuildPaths(buildPaths);
}
void CMakeSetupDialog::dragEnterEvent(QDragEnterEvent* e)
{
if(!(this->CurrentState == ReadyConfigure ||
this->CurrentState == ReadyGenerate))
{
e->ignore();
return;
}
const QMimeData* dat = e->mimeData();
QList<QUrl> urls = dat->urls();
QString file = urls.count() ? urls[0].toLocalFile() : QString();
if(!file.isEmpty() &&
(file.endsWith("CMakeCache.txt", Qt::CaseInsensitive) ||
file.endsWith("CMakeLists.txt", Qt::CaseInsensitive) ) )
{
e->accept();
}
else
{
e->ignore();
}
}
void CMakeSetupDialog::dropEvent(QDropEvent* e)
{
if(!(this->CurrentState == ReadyConfigure ||
this->CurrentState == ReadyGenerate))
{
return;
}
const QMimeData* dat = e->mimeData();
QList<QUrl> urls = dat->urls();
QString file = urls.count() ? urls[0].toLocalFile() : QString();
if(file.endsWith("CMakeCache.txt", Qt::CaseInsensitive))
{
QFileInfo info(file);
if(this->CMakeThread->cmakeInstance()->binaryDirectory() != info.absolutePath())
{
this->setBinaryDirectory(info.absolutePath());
}
}
else if(file.endsWith("CMakeLists.txt", Qt::CaseInsensitive))
{
QFileInfo info(file);
if(this->CMakeThread->cmakeInstance()->binaryDirectory() != info.absolutePath())
{
this->setSourceDirectory(info.absolutePath());
this->setBinaryDirectory(info.absolutePath());
}
}
}
QStringList CMakeSetupDialog::loadBuildPaths()
{
QSettings settings;
settings.beginGroup("Settings/StartPath");
QStringList buildPaths;
for(int i=0; i<10; i++)
{
QString p = settings.value(QString("WhereBuild%1").arg(i)).toString();
if(!p.isEmpty())
{
buildPaths.append(p);
}
}
return buildPaths;
}
void CMakeSetupDialog::saveBuildPaths(const QStringList& paths)
{
QSettings settings;
settings.beginGroup("Settings/StartPath");
int num = paths.count();
if(num > 10)
{
num = 10;
}
for(int i=0; i<num; i++)
{
settings.setValue(QString("WhereBuild%1").arg(i), paths[i]);
}
}
void CMakeSetupDialog::setCacheModified()
{
this->CacheModified = true;
this->ConfigureNeeded = true;
this->enterState(ReadyConfigure);
}
void CMakeSetupDialog::removeSelectedCacheEntries()
{
QModelIndexList idxs = this->CacheValues->selectionModel()->selectedRows();
QList<QPersistentModelIndex> pidxs;
foreach(QModelIndex i, idxs)
{
pidxs.append(i);
}
foreach(QPersistentModelIndex pi, pidxs)
{
this->CacheValues->model()->removeRow(pi.row(), pi.parent());
}
}
void CMakeSetupDialog::selectionChanged()
{
QModelIndexList idxs = this->CacheValues->selectionModel()->selectedRows();
if(idxs.count() &&
(this->CurrentState == ReadyConfigure ||
this->CurrentState == ReadyGenerate) )
{
this->RemoveEntry->setEnabled(true);
}
else
{
this->RemoveEntry->setEnabled(false);
}
}
void CMakeSetupDialog::enterState(CMakeSetupDialog::State s)
{
if(s == this->CurrentState)
{
return;
}
this->CurrentState = s;
if(s == Interrupting)
{
this->ConfigureButton->setEnabled(false);
this->GenerateButton->setEnabled(false);
}
else if(s == Configuring)
{
this->setEnabledState(false);
this->GenerateButton->setEnabled(false);
this->GenerateAction->setEnabled(false);
this->ConfigureButton->setText(tr("&Stop"));
}
else if(s == Generating)
{
this->CacheModified = false;
this->setEnabledState(false);
this->ConfigureButton->setEnabled(false);
this->GenerateAction->setEnabled(false);
this->GenerateButton->setText(tr("&Stop"));
}
else if(s == ReadyConfigure)
{
this->setEnabledState(true);
this->GenerateButton->setEnabled(true);
this->GenerateAction->setEnabled(true);
this->ConfigureButton->setEnabled(true);
this->ConfigureButton->setText(tr("&Configure"));
this->GenerateButton->setText(tr("&Generate"));
}
else if(s == ReadyGenerate)
{
this->setEnabledState(true);
this->GenerateButton->setEnabled(true);
this->GenerateAction->setEnabled(true);
this->ConfigureButton->setEnabled(true);
this->ConfigureButton->setText(tr("&Configure"));
this->GenerateButton->setText(tr("&Generate"));
}
}
void CMakeSetupDialog::addCacheEntry()
{
QDialog dialog(this);
dialog.resize(400, 200);
dialog.setWindowTitle(tr("Add Cache Entry"));
QVBoxLayout* l = new QVBoxLayout(&dialog);
AddCacheEntry* w = new AddCacheEntry(&dialog, this->AddVariableNames,
this->AddVariableTypes);
QDialogButtonBox* btns = new QDialogButtonBox(
QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
Qt::Horizontal, &dialog);
QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept()));
QObject::connect(btns, SIGNAL(rejected()), &dialog, SLOT(reject()));
l->addWidget(w);
l->addStretch();
l->addWidget(btns);
if(QDialog::Accepted == dialog.exec())
{
QCMakeCacheModel* m = this->CacheValues->cacheModel();
m->insertProperty(w->type(), w->name(), w->description(), w->value(), false);
// only add variable names to the completion which are new
if (!this->AddVariableNames.contains(w->name()))
{
this->AddVariableNames << w->name();
this->AddVariableTypes << w->typeString();
// limit to at most 100 completion items
if (this->AddVariableNames.size() > 100)
{
this->AddVariableNames.removeFirst();
this->AddVariableTypes.removeFirst();
}
// make sure CMAKE_INSTALL_PREFIX is always there
if (!this->AddVariableNames.contains("CMAKE_INSTALL_PREFIX"))
{
this->AddVariableNames << "CMAKE_INSTALL_PREFIX";
this->AddVariableTypes << "PATH";
}
QSettings settings;
settings.beginGroup("Settings/StartPath");
settings.setValue("AddVariableNames", this->AddVariableNames);
settings.setValue("AddVariableTypes", this->AddVariableTypes);
}
}
}
void CMakeSetupDialog::startSearch()
{
this->Search->setFocus(Qt::OtherFocusReason);
this->Search->selectAll();
}
void CMakeSetupDialog::setDebugOutput(bool flag)
{
QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
"setDebugOutput", Qt::QueuedConnection, Q_ARG(bool, flag));
}
void CMakeSetupDialog::setGroupedView(bool v)
{
this->CacheValues->cacheModel()->setViewType(v ? QCMakeCacheModel::GroupView : QCMakeCacheModel::FlatView);
this->CacheValues->setRootIsDecorated(v);
QSettings settings;
settings.beginGroup("Settings/StartPath");
settings.setValue("GroupView", v);
}
void CMakeSetupDialog::setAdvancedView(bool v)
{
this->CacheValues->setShowAdvanced(v);
QSettings settings;
settings.beginGroup("Settings/StartPath");
settings.setValue("AdvancedView", v);
}
void CMakeSetupDialog::showUserChanges()
{
QSet<QCMakeProperty> changes =
qobject_cast<QCMakeCacheModelDelegate*>(this->CacheValues->itemDelegate())->changes();
QDialog dialog(this);
dialog.setWindowTitle(tr("My Changes"));
dialog.resize(600, 400);
QVBoxLayout* l = new QVBoxLayout(&dialog);
QTextEdit* textedit = new QTextEdit(&dialog);
textedit->setReadOnly(true);
l->addWidget(textedit);
QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Close,
Qt::Horizontal, &dialog);
QObject::connect(btns, SIGNAL(rejected()), &dialog, SLOT(accept()));
l->addWidget(btns);
QString command;
QString cache;
foreach(QCMakeProperty prop, changes)
{
QString type;
switch(prop.Type)
{
case QCMakeProperty::BOOL:
type = "BOOL";
break;
case QCMakeProperty::PATH:
type = "PATH";
break;
case QCMakeProperty::FILEPATH:
type = "FILEPATH";
break;
case QCMakeProperty::STRING:
type = "STRING";
break;
}
QString value;
if(prop.Type == QCMakeProperty::BOOL)
{
value = prop.Value.toBool() ? "1" : "0";
}
else
{
value = prop.Value.toString();
}
QString line("%1:%2=");
line = line.arg(prop.Key);
line = line.arg(type);
command += QString("-D%1\"%2\" ").arg(line).arg(value);
cache += QString("%1%2\n").arg(line).arg(value);
}
textedit->append(tr("Commandline options:"));
textedit->append(command);
textedit->append("\n");
textedit->append(tr("Cache file:"));
textedit->append(cache);
dialog.exec();
}
void CMakeSetupDialog::setSearchFilter(const QString& str)
{
this->CacheValues->selectionModel()->clear();
this->CacheValues->setSearchFilter(str);
}
void CMakeSetupDialog::doOutputContextMenu(const QPoint &pt)
{
QMenu *menu = this->Output->createStandardContextMenu();
menu->addSeparator();
menu->addAction(tr("Find..."),
this, SLOT(doOutputFindDialog()), QKeySequence::Find);
menu->addAction(tr("Find Next"),
this, SLOT(doOutputFindNext()), QKeySequence::FindNext);
menu->addAction(tr("Find Previous"),
this, SLOT(doOutputFindPrev()), QKeySequence::FindPrevious);
menu->addSeparator();
menu->addAction(tr("Goto Next Error"),
this, SLOT(doOutputErrorNext()), QKeySequence(Qt::Key_F8));
menu->exec(this->Output->mapToGlobal(pt));
delete menu;
}
void CMakeSetupDialog::doOutputFindDialog()
{
QStringList strings(this->FindHistory);
QString selection = this->Output->textCursor().selectedText();
if (!selection.isEmpty() &&
!selection.contains(QChar::ParagraphSeparator) &&
!selection.contains(QChar::LineSeparator))
{
strings.push_front(selection);
}
bool ok;
QString search = QInputDialog::getItem(this, tr("Find in Output"),
tr("Find:"), strings, 0, true, &ok);
if (ok && !search.isEmpty())
{
if (!this->FindHistory.contains(search))
{
this->FindHistory.push_front(search);
}
doOutputFindNext();
}
}
void CMakeSetupDialog::doRegexExplorerDialog()
{
RegexExplorer dialog(this);
dialog.exec();
}
void CMakeSetupDialog::doOutputFindPrev()
{
doOutputFindNext(false);
}
void CMakeSetupDialog::doOutputFindNext(bool directionForward)
{
if (this->FindHistory.isEmpty())
{
doOutputFindDialog(); //will re-call this function again
return;
}
QString search = this->FindHistory.front();
QTextCursor textCursor = this->Output->textCursor();
QTextDocument* document = this->Output->document();
QTextDocument::FindFlags flags;
if (!directionForward)
{
flags |= QTextDocument::FindBackward;
}
textCursor = document->find(search, textCursor, flags);
if (textCursor.isNull())
{
// first search found nothing, wrap around and search again
textCursor = this->Output->textCursor();
textCursor.movePosition(directionForward ? QTextCursor::Start
: QTextCursor::End);
textCursor = document->find(search, textCursor, flags);
}
if (textCursor.hasSelection())
{
this->Output->setTextCursor(textCursor);
}
}
void CMakeSetupDialog::doOutputErrorNext()
{
QTextCursor textCursor = this->Output->textCursor();
bool atEnd = false;
// move cursor out of current error-block
if (textCursor.blockCharFormat() == this->ErrorFormat)
{
atEnd = !textCursor.movePosition(QTextCursor::NextBlock);
}
// move cursor to next error-block
while (textCursor.blockCharFormat() != this->ErrorFormat && !atEnd)
{
atEnd = !textCursor.movePosition(QTextCursor::NextBlock);
}
if (atEnd)
{
// first search found nothing, wrap around and search again
atEnd = !textCursor.movePosition(QTextCursor::Start);
// move cursor to next error-block
while (textCursor.blockCharFormat() != this->ErrorFormat && !atEnd)
{
atEnd = !textCursor.movePosition(QTextCursor::NextBlock);
}
}
if (!atEnd)
{
textCursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
QTextCharFormat selectionFormat;
selectionFormat.setBackground(Qt::yellow);
QTextEdit::ExtraSelection extraSelection = {textCursor, selectionFormat};
this->Output->setExtraSelections(QList<QTextEdit::ExtraSelection>()
<< extraSelection);
// make the whole error-block visible
this->Output->setTextCursor(textCursor);
// remove the selection to see the extraSelection
textCursor.setPosition(textCursor.anchor());
this->Output->setTextCursor(textCursor);
}
}
void CMakeSetupDialog::doWarningMessagesDialog()
{
WarningMessagesDialog dialog(this, this->CMakeThread->cmakeInstance());
dialog.exec();
}