CMakeSetupDialog.cxx 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
  4. Distributed under the OSI-approved BSD License (the "License");
  5. see accompanying file Copyright.txt for details.
  6. This software is distributed WITHOUT ANY WARRANTY; without even the
  7. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. See the License for more information.
  9. ============================================================================*/
  10. #include "CMakeSetupDialog.h"
  11. #include <QFileDialog>
  12. #include <QProgressBar>
  13. #include <QMessageBox>
  14. #include <QStatusBar>
  15. #include <QToolButton>
  16. #include <QDialogButtonBox>
  17. #include <QCloseEvent>
  18. #include <QCoreApplication>
  19. #include <QSettings>
  20. #include <QMenu>
  21. #include <QMenuBar>
  22. #include <QDragEnterEvent>
  23. #include <QMimeData>
  24. #include <QUrl>
  25. #include <QShortcut>
  26. #include <QMacInstallDialog.h>
  27. #include "QCMake.h"
  28. #include "QCMakeCacheView.h"
  29. #include "AddCacheEntry.h"
  30. #include "FirstConfigure.h"
  31. #include "cmVersion.h"
  32. QCMakeThread::QCMakeThread(QObject* p)
  33. : QThread(p), CMakeInstance(NULL)
  34. {
  35. }
  36. QCMake* QCMakeThread::cmakeInstance() const
  37. {
  38. return this->CMakeInstance;
  39. }
  40. void QCMakeThread::run()
  41. {
  42. this->CMakeInstance = new QCMake;
  43. // emit that this cmake thread is ready for use
  44. emit this->cmakeInitialized();
  45. this->exec();
  46. delete this->CMakeInstance;
  47. this->CMakeInstance = NULL;
  48. }
  49. CMakeSetupDialog::CMakeSetupDialog()
  50. : ExitAfterGenerate(true), CacheModified(false), CurrentState(Interrupting)
  51. {
  52. QString title = QString(tr("CMake %1"));
  53. title = title.arg(cmVersion::GetCMakeVersion());
  54. this->setWindowTitle(title);
  55. // create the GUI
  56. QSettings settings;
  57. settings.beginGroup("Settings/StartPath");
  58. int h = settings.value("Height", 500).toInt();
  59. int w = settings.value("Width", 700).toInt();
  60. this->resize(w, h);
  61. QWidget* cont = new QWidget(this);
  62. this->setupUi(cont);
  63. this->Splitter->setStretchFactor(0, 3);
  64. this->Splitter->setStretchFactor(1, 1);
  65. this->setCentralWidget(cont);
  66. this->ProgressBar->reset();
  67. this->RemoveEntry->setEnabled(false);
  68. this->AddEntry->setEnabled(false);
  69. QByteArray p = settings.value("SplitterSizes").toByteArray();
  70. this->Splitter->restoreState(p);
  71. bool groupView = settings.value("GroupView", false).toBool();
  72. if(groupView)
  73. {
  74. this->setViewType(2);
  75. this->ViewType->setCurrentIndex(2);
  76. }
  77. QMenu* FileMenu = this->menuBar()->addMenu(tr("&File"));
  78. this->ReloadCacheAction = FileMenu->addAction(tr("&Reload Cache"));
  79. QObject::connect(this->ReloadCacheAction, SIGNAL(triggered(bool)),
  80. this, SLOT(doReloadCache()));
  81. this->DeleteCacheAction = FileMenu->addAction(tr("&Delete Cache"));
  82. QObject::connect(this->DeleteCacheAction, SIGNAL(triggered(bool)),
  83. this, SLOT(doDeleteCache()));
  84. this->ExitAction = FileMenu->addAction(tr("E&xit"));
  85. QObject::connect(this->ExitAction, SIGNAL(triggered(bool)),
  86. this, SLOT(close()));
  87. QMenu* ToolsMenu = this->menuBar()->addMenu(tr("&Tools"));
  88. this->ConfigureAction = ToolsMenu->addAction(tr("&Configure"));
  89. // prevent merging with Preferences menu item on Mac OS X
  90. this->ConfigureAction->setMenuRole(QAction::NoRole);
  91. QObject::connect(this->ConfigureAction, SIGNAL(triggered(bool)),
  92. this, SLOT(doConfigure()));
  93. this->GenerateAction = ToolsMenu->addAction(tr("&Generate"));
  94. QObject::connect(this->GenerateAction, SIGNAL(triggered(bool)),
  95. this, SLOT(doGenerate()));
  96. QAction* showChangesAction = ToolsMenu->addAction(tr("&Show My Changes"));
  97. QObject::connect(showChangesAction, SIGNAL(triggered(bool)),
  98. this, SLOT(showUserChanges()));
  99. #if defined(Q_WS_MAC)
  100. this->InstallForCommandLineAction
  101. = ToolsMenu->addAction(tr("&Install For Command Line Use"));
  102. QObject::connect(this->InstallForCommandLineAction, SIGNAL(triggered(bool)),
  103. this, SLOT(doInstallForCommandLine()));
  104. #endif
  105. QMenu* OptionsMenu = this->menuBar()->addMenu(tr("&Options"));
  106. this->SuppressDevWarningsAction = OptionsMenu->addAction(tr("&Suppress dev Warnings (-Wno-dev)"));
  107. this->SuppressDevWarningsAction->setCheckable(true);
  108. QAction* debugAction = OptionsMenu->addAction(tr("&Debug Output"));
  109. debugAction->setCheckable(true);
  110. QObject::connect(debugAction, SIGNAL(toggled(bool)),
  111. this, SLOT(setDebugOutput(bool)));
  112. OptionsMenu->addSeparator();
  113. QAction* expandAction = OptionsMenu->addAction(tr("&Expand Grouped Entries"));
  114. QObject::connect(expandAction, SIGNAL(triggered(bool)),
  115. this->CacheValues, SLOT(expandAll()));
  116. QAction* collapseAction = OptionsMenu->addAction(tr("&Collapse Grouped Entries"));
  117. QObject::connect(collapseAction, SIGNAL(triggered(bool)),
  118. this->CacheValues, SLOT(collapseAll()));
  119. QMenu* HelpMenu = this->menuBar()->addMenu(tr("&Help"));
  120. QAction* a = HelpMenu->addAction(tr("About"));
  121. QObject::connect(a, SIGNAL(triggered(bool)),
  122. this, SLOT(doAbout()));
  123. a = HelpMenu->addAction(tr("Help"));
  124. QObject::connect(a, SIGNAL(triggered(bool)),
  125. this, SLOT(doHelp()));
  126. QShortcut* filterShortcut = new QShortcut(QKeySequence::Find, this);
  127. QObject::connect(filterShortcut, SIGNAL(activated()),
  128. this, SLOT(startSearch()));
  129. this->setAcceptDrops(true);
  130. // get the saved binary directories
  131. QStringList buildPaths = this->loadBuildPaths();
  132. this->BinaryDirectory->addItems(buildPaths);
  133. this->BinaryDirectory->setCompleter(new QCMakeFileCompleter(this, true));
  134. this->SourceDirectory->setCompleter(new QCMakeFileCompleter(this, true));
  135. // fixed pitch font in output window
  136. QFont outputFont("Courier");
  137. this->Output->setFont(outputFont);
  138. this->ErrorFormat.setForeground(QBrush(Qt::red));
  139. // start the cmake worker thread
  140. this->CMakeThread = new QCMakeThread(this);
  141. QObject::connect(this->CMakeThread, SIGNAL(cmakeInitialized()),
  142. this, SLOT(initialize()), Qt::QueuedConnection);
  143. this->CMakeThread->start();
  144. this->enterState(ReadyConfigure);
  145. }
  146. void CMakeSetupDialog::initialize()
  147. {
  148. // now the cmake worker thread is running, lets make our connections to it
  149. QObject::connect(this->CMakeThread->cmakeInstance(),
  150. SIGNAL(propertiesChanged(const QCMakePropertyList&)),
  151. this->CacheValues->cacheModel(),
  152. SLOT(setProperties(const QCMakePropertyList&)));
  153. QObject::connect(this->ConfigureButton, SIGNAL(clicked(bool)),
  154. this, SLOT(doConfigure()));
  155. QObject::connect(this->CMakeThread->cmakeInstance(),
  156. SIGNAL(configureDone(int)),
  157. this, SLOT(finishConfigure(int)));
  158. QObject::connect(this->CMakeThread->cmakeInstance(),
  159. SIGNAL(generateDone(int)),
  160. this, SLOT(finishGenerate(int)));
  161. QObject::connect(this->GenerateButton, SIGNAL(clicked(bool)),
  162. this, SLOT(doGenerate()));
  163. QObject::connect(this->BrowseSourceDirectoryButton, SIGNAL(clicked(bool)),
  164. this, SLOT(doSourceBrowse()));
  165. QObject::connect(this->BrowseBinaryDirectoryButton, SIGNAL(clicked(bool)),
  166. this, SLOT(doBinaryBrowse()));
  167. QObject::connect(this->BinaryDirectory, SIGNAL(editTextChanged(QString)),
  168. this, SLOT(onBinaryDirectoryChanged(QString)));
  169. QObject::connect(this->SourceDirectory, SIGNAL(textChanged(QString)),
  170. this, SLOT(onSourceDirectoryChanged(QString)));
  171. QObject::connect(this->CMakeThread->cmakeInstance(),
  172. SIGNAL(sourceDirChanged(QString)),
  173. this, SLOT(updateSourceDirectory(QString)));
  174. QObject::connect(this->CMakeThread->cmakeInstance(),
  175. SIGNAL(binaryDirChanged(QString)),
  176. this, SLOT(updateBinaryDirectory(QString)));
  177. QObject::connect(this->CMakeThread->cmakeInstance(),
  178. SIGNAL(progressChanged(QString, float)),
  179. this, SLOT(showProgress(QString,float)));
  180. QObject::connect(this->CMakeThread->cmakeInstance(),
  181. SIGNAL(errorMessage(QString)),
  182. this, SLOT(error(QString)));
  183. QObject::connect(this->CMakeThread->cmakeInstance(),
  184. SIGNAL(outputMessage(QString)),
  185. this, SLOT(message(QString)));
  186. QObject::connect(this->ViewType, SIGNAL(currentIndexChanged(int)),
  187. this, SLOT(setViewType(int)));
  188. QObject::connect(this->Search, SIGNAL(textChanged(QString)),
  189. this, SLOT(setSearchFilter(QString)));
  190. QObject::connect(this->CMakeThread->cmakeInstance(),
  191. SIGNAL(generatorChanged(QString)),
  192. this, SLOT(updateGeneratorLabel(QString)));
  193. this->updateGeneratorLabel(QString());
  194. QObject::connect(this->CacheValues->cacheModel(),
  195. SIGNAL(dataChanged(QModelIndex,QModelIndex)),
  196. this, SLOT(setCacheModified()));
  197. QObject::connect(this->CacheValues->selectionModel(),
  198. SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
  199. this, SLOT(selectionChanged()));
  200. QObject::connect(this->RemoveEntry, SIGNAL(clicked(bool)),
  201. this, SLOT(removeSelectedCacheEntries()));
  202. QObject::connect(this->AddEntry, SIGNAL(clicked(bool)),
  203. this, SLOT(addCacheEntry()));
  204. QObject::connect(this->SuppressDevWarningsAction, SIGNAL(triggered(bool)),
  205. this->CMakeThread->cmakeInstance(), SLOT(setSuppressDevWarnings(bool)));
  206. if(!this->SourceDirectory->text().isEmpty() ||
  207. !this->BinaryDirectory->lineEdit()->text().isEmpty())
  208. {
  209. this->onSourceDirectoryChanged(this->SourceDirectory->text());
  210. this->onBinaryDirectoryChanged(this->BinaryDirectory->lineEdit()->text());
  211. }
  212. else
  213. {
  214. this->onBinaryDirectoryChanged(this->BinaryDirectory->lineEdit()->text());
  215. }
  216. }
  217. CMakeSetupDialog::~CMakeSetupDialog()
  218. {
  219. QSettings settings;
  220. settings.beginGroup("Settings/StartPath");
  221. settings.setValue("Height", this->height());
  222. settings.setValue("Width", this->width());
  223. settings.setValue("SplitterSizes", this->Splitter->saveState());
  224. // wait for thread to stop
  225. this->CMakeThread->quit();
  226. this->CMakeThread->wait(2000);
  227. }
  228. void CMakeSetupDialog::doConfigure()
  229. {
  230. if(this->CurrentState == Configuring)
  231. {
  232. // stop configure
  233. doInterrupt();
  234. return;
  235. }
  236. // make sure build directory exists
  237. QString bindir = this->CMakeThread->cmakeInstance()->binaryDirectory();
  238. QDir dir(bindir);
  239. if(!dir.exists())
  240. {
  241. QString msg = tr("Build directory does not exist, "
  242. "should I create it?")
  243. + "\n\n"
  244. + tr("Directory: ");
  245. msg += bindir;
  246. QString title = tr("Create Directory");
  247. QMessageBox::StandardButton btn;
  248. btn = QMessageBox::information(this, title, msg,
  249. QMessageBox::Yes | QMessageBox::No);
  250. if(btn == QMessageBox::No)
  251. {
  252. return;
  253. }
  254. dir.mkpath(".");
  255. }
  256. // if no generator, prompt for it and other setup stuff
  257. if(this->CMakeThread->cmakeInstance()->generator().isEmpty())
  258. {
  259. if(!this->setupFirstConfigure())
  260. {
  261. return;
  262. }
  263. }
  264. // remember path
  265. this->addBinaryPath(dir.absolutePath());
  266. this->enterState(Configuring);
  267. this->CacheValues->selectionModel()->clear();
  268. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  269. "setProperties", Qt::QueuedConnection,
  270. Q_ARG(QCMakePropertyList,
  271. this->CacheValues->cacheModel()->properties()));
  272. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  273. "configure", Qt::QueuedConnection);
  274. }
  275. void CMakeSetupDialog::finishConfigure(int err)
  276. {
  277. if(0 == err && !this->CacheValues->cacheModel()->newPropertyCount())
  278. {
  279. this->enterState(ReadyGenerate);
  280. }
  281. else
  282. {
  283. this->enterState(ReadyConfigure);
  284. this->CacheValues->scrollToTop();
  285. }
  286. if(err != 0)
  287. {
  288. QMessageBox::critical(this, tr("Error"),
  289. tr("Error in configuration process, project files may be invalid"),
  290. QMessageBox::Ok);
  291. }
  292. }
  293. void CMakeSetupDialog::finishGenerate(int err)
  294. {
  295. this->enterState(ReadyConfigure);
  296. if(err != 0)
  297. {
  298. QMessageBox::critical(this, tr("Error"),
  299. tr("Error in generation process, project files may be invalid"),
  300. QMessageBox::Ok);
  301. }
  302. }
  303. void CMakeSetupDialog::doInstallForCommandLine()
  304. {
  305. QMacInstallDialog setupdialog(0);
  306. setupdialog.exec();
  307. }
  308. void CMakeSetupDialog::doGenerate()
  309. {
  310. if(this->CurrentState == Generating)
  311. {
  312. // stop generate
  313. doInterrupt();
  314. return;
  315. }
  316. this->enterState(Generating);
  317. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  318. "generate", Qt::QueuedConnection);
  319. }
  320. void CMakeSetupDialog::closeEvent(QCloseEvent* e)
  321. {
  322. // prompt for close if there are unsaved changes, and we're not busy
  323. if(this->CacheModified)
  324. {
  325. QString msg = tr("You have changed options but not rebuilt, "
  326. "are you sure you want to exit?");
  327. QString title = tr("Confirm Exit");
  328. QMessageBox::StandardButton btn;
  329. btn = QMessageBox::critical(this, title, msg,
  330. QMessageBox::Yes | QMessageBox::No);
  331. if(btn == QMessageBox::No)
  332. {
  333. e->ignore();
  334. }
  335. }
  336. // don't close if we're busy, unless the user really wants to
  337. if(this->CurrentState == Configuring)
  338. {
  339. QString msg = "You are in the middle of a Configure.\n"
  340. "If you Exit now the configure information will be lost.\n"
  341. "Are you sure you want to Exit?";
  342. QString title = tr("Confirm Exit");
  343. QMessageBox::StandardButton btn;
  344. btn = QMessageBox::critical(this, title, msg,
  345. QMessageBox::Yes | QMessageBox::No);
  346. if(btn == QMessageBox::No)
  347. {
  348. e->ignore();
  349. }
  350. else
  351. {
  352. this->doInterrupt();
  353. }
  354. }
  355. // let the generate finish
  356. if(this->CurrentState == Generating)
  357. {
  358. e->ignore();
  359. }
  360. }
  361. void CMakeSetupDialog::doHelp()
  362. {
  363. QString msg = tr("CMake is used to configure and generate build files for "
  364. "software projects. The basic steps for configuring a project are as "
  365. "follows:\r\n\r\n1. Select the source directory for the project. This should "
  366. "contain the CMakeLists.txt files for the project.\r\n\r\n2. Select the build "
  367. "directory for the project. This is the directory where the project will be "
  368. "built. It can be the same or a different directory than the source "
  369. "directory. For easy clean up, a separate build directory is recommended. "
  370. "CMake will create the directory if it does not exist.\r\n\r\n3. Once the "
  371. "source and binary directories are selected, it is time to press the "
  372. "Configure button. This will cause CMake to read all of the input files and "
  373. "discover all the variables used by the project. The first time a variable "
  374. "is displayed it will be in Red. Users should inspect red variables making "
  375. "sure the values are correct. For some projects the Configure process can "
  376. "be iterative, so continue to press the Configure button until there are no "
  377. "longer red entries.\r\n\r\n4. Once there are no longer red entries, you "
  378. "should click the Generate button. This will write the build files to the build "
  379. "directory.");
  380. QDialog dialog;
  381. QFontMetrics met(this->font());
  382. int msgWidth = met.width(msg);
  383. dialog.setMinimumSize(msgWidth/15,20);
  384. dialog.setWindowTitle(tr("Help"));
  385. QVBoxLayout* l = new QVBoxLayout(&dialog);
  386. QLabel* lab = new QLabel(&dialog);
  387. lab->setText(msg);
  388. lab->setWordWrap(true);
  389. QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Ok,
  390. Qt::Horizontal, &dialog);
  391. QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept()));
  392. l->addWidget(lab);
  393. l->addWidget(btns);
  394. dialog.exec();
  395. }
  396. void CMakeSetupDialog::doInterrupt()
  397. {
  398. this->enterState(Interrupting);
  399. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  400. "interrupt", Qt::QueuedConnection);
  401. }
  402. void CMakeSetupDialog::doSourceBrowse()
  403. {
  404. QString dir = QFileDialog::getExistingDirectory(this,
  405. tr("Enter Path to Source"), this->SourceDirectory->text());
  406. if(!dir.isEmpty())
  407. {
  408. this->setSourceDirectory(dir);
  409. }
  410. }
  411. void CMakeSetupDialog::updateSourceDirectory(const QString& dir)
  412. {
  413. if(this->SourceDirectory->text() != dir)
  414. {
  415. this->SourceDirectory->blockSignals(true);
  416. this->SourceDirectory->setText(dir);
  417. this->SourceDirectory->blockSignals(false);
  418. }
  419. }
  420. void CMakeSetupDialog::updateBinaryDirectory(const QString& dir)
  421. {
  422. if(this->BinaryDirectory->currentText() != dir)
  423. {
  424. this->BinaryDirectory->blockSignals(true);
  425. this->BinaryDirectory->setEditText(dir);
  426. this->BinaryDirectory->blockSignals(false);
  427. }
  428. }
  429. void CMakeSetupDialog::doBinaryBrowse()
  430. {
  431. QString dir = QFileDialog::getExistingDirectory(this,
  432. tr("Enter Path to Build"), this->BinaryDirectory->currentText());
  433. if(!dir.isEmpty() && dir != this->BinaryDirectory->currentText())
  434. {
  435. this->setBinaryDirectory(dir);
  436. }
  437. }
  438. void CMakeSetupDialog::setBinaryDirectory(const QString& dir)
  439. {
  440. this->BinaryDirectory->setEditText(dir);
  441. }
  442. void CMakeSetupDialog::onSourceDirectoryChanged(const QString& dir)
  443. {
  444. this->Output->clear();
  445. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  446. "setSourceDirectory", Qt::QueuedConnection, Q_ARG(QString, dir));
  447. }
  448. void CMakeSetupDialog::onBinaryDirectoryChanged(const QString& dir)
  449. {
  450. QString title = QString(tr("CMake %1 - %2"));
  451. title = title.arg(cmVersion::GetCMakeVersion());
  452. title = title.arg(dir);
  453. this->setWindowTitle(title);
  454. this->CacheModified = false;
  455. this->CacheValues->cacheModel()->clear();
  456. qobject_cast<QCMakeCacheModelDelegate*>(this->CacheValues->itemDelegate())->clearChanges();
  457. this->Output->clear();
  458. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  459. "setBinaryDirectory", Qt::QueuedConnection, Q_ARG(QString, dir));
  460. }
  461. void CMakeSetupDialog::setSourceDirectory(const QString& dir)
  462. {
  463. this->SourceDirectory->setText(dir);
  464. }
  465. void CMakeSetupDialog::showProgress(const QString& /*msg*/, float percent)
  466. {
  467. this->ProgressBar->setValue(qRound(percent * 100));
  468. }
  469. void CMakeSetupDialog::error(const QString& msg)
  470. {
  471. this->Output->setCurrentCharFormat(this->ErrorFormat);
  472. this->Output->append(msg);
  473. }
  474. void CMakeSetupDialog::message(const QString& msg)
  475. {
  476. this->Output->setCurrentCharFormat(this->MessageFormat);
  477. this->Output->append(msg);
  478. }
  479. void CMakeSetupDialog::setEnabledState(bool enabled)
  480. {
  481. // disable parts of the GUI during configure/generate
  482. this->CacheValues->cacheModel()->setEditEnabled(enabled);
  483. this->SourceDirectory->setEnabled(enabled);
  484. this->BrowseSourceDirectoryButton->setEnabled(enabled);
  485. this->BinaryDirectory->setEnabled(enabled);
  486. this->BrowseBinaryDirectoryButton->setEnabled(enabled);
  487. this->ReloadCacheAction->setEnabled(enabled);
  488. this->DeleteCacheAction->setEnabled(enabled);
  489. this->ExitAction->setEnabled(enabled);
  490. this->ConfigureAction->setEnabled(enabled);
  491. this->AddEntry->setEnabled(enabled);
  492. this->RemoveEntry->setEnabled(false); // let selection re-enable it
  493. }
  494. bool CMakeSetupDialog::setupFirstConfigure()
  495. {
  496. FirstConfigure dialog;
  497. // initialize dialog and restore saved settings
  498. // add generators
  499. dialog.setGenerators(this->CMakeThread->cmakeInstance()->availableGenerators());
  500. // restore from settings
  501. dialog.loadFromSettings();
  502. if(dialog.exec() == QDialog::Accepted)
  503. {
  504. dialog.saveToSettings();
  505. this->CMakeThread->cmakeInstance()->setGenerator(dialog.getGenerator());
  506. QCMakeCacheModel* m = this->CacheValues->cacheModel();
  507. if(dialog.compilerSetup())
  508. {
  509. QString fortranCompiler = dialog.getFortranCompiler();
  510. if(!fortranCompiler.isEmpty())
  511. {
  512. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_Fortran_COMPILER",
  513. "Fortran compiler.", fortranCompiler, false);
  514. }
  515. QString cxxCompiler = dialog.getCXXCompiler();
  516. if(!cxxCompiler.isEmpty())
  517. {
  518. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_CXX_COMPILER",
  519. "CXX compiler.", cxxCompiler, false);
  520. }
  521. QString cCompiler = dialog.getCCompiler();
  522. if(!cCompiler.isEmpty())
  523. {
  524. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_C_COMPILER",
  525. "C compiler.", cCompiler, false);
  526. }
  527. }
  528. else if(dialog.crossCompilerSetup())
  529. {
  530. QString fortranCompiler = dialog.getFortranCompiler();
  531. if(!fortranCompiler.isEmpty())
  532. {
  533. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_Fortran_COMPILER",
  534. "Fortran compiler.", fortranCompiler, false);
  535. }
  536. QString mode = dialog.getCrossIncludeMode();
  537. m->insertProperty(QCMakeProperty::STRING, "CMAKE_FIND_ROOT_PATH_MODE_INCLUDE",
  538. "CMake Find Include Mode", mode, false);
  539. mode = dialog.getCrossLibraryMode();
  540. m->insertProperty(QCMakeProperty::STRING, "CMAKE_FIND_ROOT_PATH_MODE_LIBRARY",
  541. "CMake Find Library Mode", mode, false);
  542. mode = dialog.getCrossProgramMode();
  543. m->insertProperty(QCMakeProperty::STRING, "CMAKE_FIND_ROOT_PATH_MODE_PROGRAM",
  544. "CMake Find Program Mode", mode, false);
  545. QString rootPath = dialog.getCrossRoot();
  546. m->insertProperty(QCMakeProperty::PATH, "CMAKE_FIND_ROOT_PATH",
  547. "CMake Find Root Path", rootPath, false);
  548. QString systemName = dialog.getSystemName();
  549. m->insertProperty(QCMakeProperty::STRING, "CMAKE_SYSTEM_NAME",
  550. "CMake System Name", systemName, false);
  551. QString cxxCompiler = dialog.getCXXCompiler();
  552. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_CXX_COMPILER",
  553. "CXX compiler.", cxxCompiler, false);
  554. QString cCompiler = dialog.getCCompiler();
  555. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_C_COMPILER",
  556. "C compiler.", cCompiler, false);
  557. }
  558. else if(dialog.crossCompilerToolChainFile())
  559. {
  560. QString toolchainFile = dialog.getCrossCompilerToolChainFile();
  561. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_TOOLCHAIN_FILE",
  562. "Cross Compile ToolChain File", toolchainFile, false);
  563. }
  564. return true;
  565. }
  566. return false;
  567. }
  568. void CMakeSetupDialog::updateGeneratorLabel(const QString& gen)
  569. {
  570. QString str = tr("Current Generator: ");
  571. if(gen.isEmpty())
  572. {
  573. str += tr("None");
  574. }
  575. else
  576. {
  577. str += gen;
  578. }
  579. this->Generator->setText(str);
  580. }
  581. void CMakeSetupDialog::doReloadCache()
  582. {
  583. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  584. "reloadCache", Qt::QueuedConnection);
  585. }
  586. void CMakeSetupDialog::doDeleteCache()
  587. {
  588. QString title = tr("Delete Cache");
  589. QString msg = "Are you sure you want to delete the cache?";
  590. QMessageBox::StandardButton btn;
  591. btn = QMessageBox::information(this, title, msg,
  592. QMessageBox::Yes | QMessageBox::No);
  593. if(btn == QMessageBox::No)
  594. {
  595. return;
  596. }
  597. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  598. "deleteCache", Qt::QueuedConnection);
  599. }
  600. void CMakeSetupDialog::doAbout()
  601. {
  602. QString msg = "CMake %1\n"
  603. "Using Qt %2\n"
  604. "www.cmake.org";
  605. msg = msg.arg(cmVersion::GetCMakeVersion());
  606. msg = msg.arg(qVersion());
  607. QDialog dialog;
  608. dialog.setWindowTitle(tr("About"));
  609. QVBoxLayout* l = new QVBoxLayout(&dialog);
  610. QLabel* lab = new QLabel(&dialog);
  611. l->addWidget(lab);
  612. lab->setText(msg);
  613. lab->setWordWrap(true);
  614. QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Ok,
  615. Qt::Horizontal, &dialog);
  616. QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept()));
  617. l->addWidget(btns);
  618. dialog.exec();
  619. }
  620. void CMakeSetupDialog::setExitAfterGenerate(bool b)
  621. {
  622. this->ExitAfterGenerate = b;
  623. }
  624. void CMakeSetupDialog::addBinaryPath(const QString& path)
  625. {
  626. QString cleanpath = QDir::cleanPath(path);
  627. // update UI
  628. this->BinaryDirectory->blockSignals(true);
  629. int idx = this->BinaryDirectory->findText(cleanpath);
  630. if(idx != -1)
  631. {
  632. this->BinaryDirectory->removeItem(idx);
  633. }
  634. this->BinaryDirectory->insertItem(0, cleanpath);
  635. this->BinaryDirectory->setCurrentIndex(0);
  636. this->BinaryDirectory->blockSignals(false);
  637. // save to registry
  638. QStringList buildPaths = this->loadBuildPaths();
  639. buildPaths.removeAll(cleanpath);
  640. buildPaths.prepend(cleanpath);
  641. this->saveBuildPaths(buildPaths);
  642. }
  643. void CMakeSetupDialog::dragEnterEvent(QDragEnterEvent* e)
  644. {
  645. if(!(this->CurrentState == ReadyConfigure ||
  646. this->CurrentState == ReadyGenerate))
  647. {
  648. e->ignore();
  649. return;
  650. }
  651. const QMimeData* dat = e->mimeData();
  652. QList<QUrl> urls = dat->urls();
  653. QString file = urls.count() ? urls[0].toLocalFile() : QString();
  654. if(!file.isEmpty() &&
  655. (file.endsWith("CMakeCache.txt", Qt::CaseInsensitive) ||
  656. file.endsWith("CMakeLists.txt", Qt::CaseInsensitive) ) )
  657. {
  658. e->accept();
  659. }
  660. else
  661. {
  662. e->ignore();
  663. }
  664. }
  665. void CMakeSetupDialog::dropEvent(QDropEvent* e)
  666. {
  667. if(!(this->CurrentState == ReadyConfigure ||
  668. this->CurrentState == ReadyGenerate))
  669. {
  670. return;
  671. }
  672. const QMimeData* dat = e->mimeData();
  673. QList<QUrl> urls = dat->urls();
  674. QString file = urls.count() ? urls[0].toLocalFile() : QString();
  675. if(file.endsWith("CMakeCache.txt", Qt::CaseInsensitive))
  676. {
  677. QFileInfo info(file);
  678. if(this->CMakeThread->cmakeInstance()->binaryDirectory() != info.absolutePath())
  679. {
  680. this->setBinaryDirectory(info.absolutePath());
  681. }
  682. }
  683. else if(file.endsWith("CMakeLists.txt", Qt::CaseInsensitive))
  684. {
  685. QFileInfo info(file);
  686. if(this->CMakeThread->cmakeInstance()->binaryDirectory() != info.absolutePath())
  687. {
  688. this->setSourceDirectory(info.absolutePath());
  689. this->setBinaryDirectory(info.absolutePath());
  690. }
  691. }
  692. }
  693. QStringList CMakeSetupDialog::loadBuildPaths()
  694. {
  695. QSettings settings;
  696. settings.beginGroup("Settings/StartPath");
  697. QStringList buildPaths;
  698. for(int i=0; i<10; i++)
  699. {
  700. QString p = settings.value(QString("WhereBuild%1").arg(i)).toString();
  701. if(!p.isEmpty())
  702. {
  703. buildPaths.append(p);
  704. }
  705. }
  706. return buildPaths;
  707. }
  708. void CMakeSetupDialog::saveBuildPaths(const QStringList& paths)
  709. {
  710. QSettings settings;
  711. settings.beginGroup("Settings/StartPath");
  712. int num = paths.count();
  713. if(num > 10)
  714. {
  715. num = 10;
  716. }
  717. for(int i=0; i<num; i++)
  718. {
  719. settings.setValue(QString("WhereBuild%1").arg(i), paths[i]);
  720. }
  721. }
  722. void CMakeSetupDialog::setCacheModified()
  723. {
  724. this->CacheModified = true;
  725. this->enterState(ReadyConfigure);
  726. }
  727. void CMakeSetupDialog::removeSelectedCacheEntries()
  728. {
  729. QModelIndexList idxs = this->CacheValues->selectionModel()->selectedRows();
  730. QList<QPersistentModelIndex> pidxs;
  731. foreach(QModelIndex i, idxs)
  732. {
  733. pidxs.append(i);
  734. }
  735. foreach(QPersistentModelIndex pi, pidxs)
  736. {
  737. this->CacheValues->model()->removeRow(pi.row(), pi.parent());
  738. }
  739. }
  740. void CMakeSetupDialog::selectionChanged()
  741. {
  742. QModelIndexList idxs = this->CacheValues->selectionModel()->selectedRows();
  743. if(idxs.count() &&
  744. (this->CurrentState == ReadyConfigure ||
  745. this->CurrentState == ReadyGenerate) )
  746. {
  747. this->RemoveEntry->setEnabled(true);
  748. }
  749. else
  750. {
  751. this->RemoveEntry->setEnabled(false);
  752. }
  753. }
  754. void CMakeSetupDialog::enterState(CMakeSetupDialog::State s)
  755. {
  756. if(s == this->CurrentState)
  757. {
  758. return;
  759. }
  760. this->CurrentState = s;
  761. if(s == Interrupting)
  762. {
  763. this->ConfigureButton->setEnabled(false);
  764. this->GenerateButton->setEnabled(false);
  765. }
  766. else if(s == Configuring)
  767. {
  768. this->Output->clear();
  769. this->setEnabledState(false);
  770. this->GenerateButton->setEnabled(false);
  771. this->GenerateAction->setEnabled(false);
  772. this->ConfigureButton->setText(tr("&Stop"));
  773. }
  774. else if(s == Generating)
  775. {
  776. this->CacheModified = false;
  777. this->setEnabledState(false);
  778. this->ConfigureButton->setEnabled(false);
  779. this->GenerateAction->setEnabled(false);
  780. this->GenerateButton->setText(tr("&Stop"));
  781. }
  782. else if(s == ReadyConfigure)
  783. {
  784. this->ProgressBar->reset();
  785. this->setEnabledState(true);
  786. this->GenerateButton->setEnabled(false);
  787. this->GenerateAction->setEnabled(false);
  788. this->ConfigureButton->setEnabled(true);
  789. this->ConfigureButton->setText(tr("&Configure"));
  790. this->GenerateButton->setText(tr("&Generate"));
  791. }
  792. else if(s == ReadyGenerate)
  793. {
  794. this->ProgressBar->reset();
  795. this->setEnabledState(true);
  796. this->GenerateButton->setEnabled(true);
  797. this->GenerateAction->setEnabled(true);
  798. this->ConfigureButton->setEnabled(true);
  799. this->ConfigureButton->setText(tr("&Configure"));
  800. this->GenerateButton->setText(tr("&Generate"));
  801. }
  802. }
  803. void CMakeSetupDialog::addCacheEntry()
  804. {
  805. QDialog dialog(this);
  806. dialog.resize(400, 200);
  807. dialog.setWindowTitle(tr("Add Cache Entry"));
  808. QVBoxLayout* l = new QVBoxLayout(&dialog);
  809. AddCacheEntry* w = new AddCacheEntry(&dialog);
  810. QDialogButtonBox* btns = new QDialogButtonBox(
  811. QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
  812. Qt::Horizontal, &dialog);
  813. QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept()));
  814. QObject::connect(btns, SIGNAL(rejected()), &dialog, SLOT(reject()));
  815. l->addWidget(w);
  816. l->addStretch();
  817. l->addWidget(btns);
  818. if(QDialog::Accepted == dialog.exec())
  819. {
  820. QCMakeCacheModel* m = this->CacheValues->cacheModel();
  821. m->insertProperty(w->type(), w->name(), w->description(), w->value(), false);
  822. }
  823. }
  824. void CMakeSetupDialog::startSearch()
  825. {
  826. this->Search->setFocus(Qt::OtherFocusReason);
  827. this->Search->selectAll();
  828. }
  829. void CMakeSetupDialog::setDebugOutput(bool flag)
  830. {
  831. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  832. "setDebugOutput", Qt::QueuedConnection, Q_ARG(bool, flag));
  833. }
  834. void CMakeSetupDialog::setViewType(int v)
  835. {
  836. if(v == 0) // simple view
  837. {
  838. this->CacheValues->cacheModel()->setViewType(QCMakeCacheModel::FlatView);
  839. this->CacheValues->setRootIsDecorated(false);
  840. this->CacheValues->setShowAdvanced(false);
  841. }
  842. else if(v == 1) // advanced view
  843. {
  844. this->CacheValues->cacheModel()->setViewType(QCMakeCacheModel::FlatView);
  845. this->CacheValues->setRootIsDecorated(false);
  846. this->CacheValues->setShowAdvanced(true);
  847. }
  848. else if(v == 2) // grouped view
  849. {
  850. this->CacheValues->cacheModel()->setViewType(QCMakeCacheModel::GroupView);
  851. this->CacheValues->setRootIsDecorated(true);
  852. this->CacheValues->setShowAdvanced(true);
  853. }
  854. QSettings settings;
  855. settings.beginGroup("Settings/StartPath");
  856. settings.setValue("GroupView", v == 2);
  857. }
  858. void CMakeSetupDialog::showUserChanges()
  859. {
  860. QSet<QCMakeProperty> changes =
  861. qobject_cast<QCMakeCacheModelDelegate*>(this->CacheValues->itemDelegate())->changes();
  862. QDialog dialog(this);
  863. dialog.setWindowTitle(tr("My Changes"));
  864. dialog.resize(600, 400);
  865. QVBoxLayout* l = new QVBoxLayout(&dialog);
  866. QTextEdit* textedit = new QTextEdit(&dialog);
  867. textedit->setReadOnly(true);
  868. l->addWidget(textedit);
  869. QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Close,
  870. Qt::Horizontal, &dialog);
  871. QObject::connect(btns, SIGNAL(rejected()), &dialog, SLOT(accept()));
  872. l->addWidget(btns);
  873. QString command;
  874. QString cache;
  875. foreach(QCMakeProperty prop, changes)
  876. {
  877. QString type;
  878. switch(prop.Type)
  879. {
  880. case QCMakeProperty::BOOL:
  881. type = "BOOL";
  882. break;
  883. case QCMakeProperty::PATH:
  884. type = "PATH";
  885. break;
  886. case QCMakeProperty::FILEPATH:
  887. type = "FILEPATH";
  888. break;
  889. case QCMakeProperty::STRING:
  890. type = "STRING";
  891. break;
  892. }
  893. QString value;
  894. if(prop.Type == QCMakeProperty::BOOL)
  895. {
  896. value = prop.Value.toBool() ? "1" : "0";
  897. }
  898. else
  899. {
  900. value = prop.Value.toString();
  901. }
  902. QString line("%1:%2=");
  903. line = line.arg(prop.Key);
  904. line = line.arg(type);
  905. command += QString("-D%1\"%2\" ").arg(line).arg(value);
  906. cache += QString("%1%2\n").arg(line).arg(value);
  907. }
  908. textedit->append(tr("Commandline options:"));
  909. textedit->append(command);
  910. textedit->append("\n");
  911. textedit->append(tr("Cache file:"));
  912. textedit->append(cache);
  913. dialog.exec();
  914. }
  915. void CMakeSetupDialog::setSearchFilter(const QString& str)
  916. {
  917. this->CacheValues->selectionModel()->clear();
  918. this->CacheValues->setSearchFilter(str);
  919. }