CMakeSetupDialog.cxx 35 KB

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