CMakeSetupDialog.cxx 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152
  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?\n\n"
  256. "Directory: ");
  257. msg += bindir;
  258. QString title = tr("Create Directory");
  259. QMessageBox::StandardButton btn;
  260. btn = QMessageBox::information(this, title, msg,
  261. QMessageBox::Yes | QMessageBox::No);
  262. if(btn == QMessageBox::No)
  263. {
  264. return false;
  265. }
  266. if(!dir.mkpath("."))
  267. {
  268. QMessageBox::information(this, tr("Create Directory Failed"),
  269. QString(tr("Failed to create directory %1")).arg(dir.path()),
  270. QMessageBox::Ok);
  271. return false;
  272. }
  273. }
  274. // if no generator, prompt for it and other setup stuff
  275. if(this->CMakeThread->cmakeInstance()->generator().isEmpty())
  276. {
  277. if(!this->setupFirstConfigure())
  278. {
  279. return false;
  280. }
  281. }
  282. // remember path
  283. this->addBinaryPath(dir.absolutePath());
  284. return true;
  285. }
  286. void CMakeSetupDialog::exitLoop(int err)
  287. {
  288. this->LocalLoop.exit(err);
  289. }
  290. void CMakeSetupDialog::doConfigure()
  291. {
  292. if(this->CurrentState == Configuring)
  293. {
  294. // stop configure
  295. doInterrupt();
  296. return;
  297. }
  298. if(!prepareConfigure())
  299. {
  300. return;
  301. }
  302. this->enterState(Configuring);
  303. bool ret = doConfigureInternal();
  304. if(ret)
  305. {
  306. this->ConfigureNeeded = false;
  307. }
  308. if(ret && !this->CacheValues->cacheModel()->newPropertyCount())
  309. {
  310. this->enterState(ReadyGenerate);
  311. }
  312. else
  313. {
  314. this->enterState(ReadyConfigure);
  315. this->CacheValues->scrollToTop();
  316. }
  317. this->ProgressBar->reset();
  318. }
  319. bool CMakeSetupDialog::doConfigureInternal()
  320. {
  321. this->Output->clear();
  322. this->CacheValues->selectionModel()->clear();
  323. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  324. "setProperties", Qt::QueuedConnection,
  325. Q_ARG(QCMakePropertyList,
  326. this->CacheValues->cacheModel()->properties()));
  327. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  328. "configure", Qt::QueuedConnection);
  329. int err = this->LocalLoop.exec();
  330. if(err != 0)
  331. {
  332. QMessageBox::critical(this, tr("Error"),
  333. tr("Error in configuration process, project files may be invalid"),
  334. QMessageBox::Ok);
  335. }
  336. return 0 == err;
  337. }
  338. void CMakeSetupDialog::doInstallForCommandLine()
  339. {
  340. QMacInstallDialog setupdialog(0);
  341. setupdialog.exec();
  342. }
  343. bool CMakeSetupDialog::doGenerateInternal()
  344. {
  345. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  346. "generate", Qt::QueuedConnection);
  347. int err = this->LocalLoop.exec();
  348. if(err != 0)
  349. {
  350. QMessageBox::critical(this, tr("Error"),
  351. tr("Error in generation process, project files may be invalid"),
  352. QMessageBox::Ok);
  353. }
  354. return 0 == err;
  355. }
  356. void CMakeSetupDialog::doGenerate()
  357. {
  358. if(this->CurrentState == Generating)
  359. {
  360. // stop generate
  361. doInterrupt();
  362. return;
  363. }
  364. // see if we need to configure
  365. // we'll need to configure if:
  366. // the configure step hasn't been done yet
  367. // generate was the last step done
  368. if(this->ConfigureNeeded)
  369. {
  370. if(!prepareConfigure())
  371. {
  372. return;
  373. }
  374. }
  375. this->enterState(Generating);
  376. bool config_passed = true;
  377. if(this->ConfigureNeeded)
  378. {
  379. this->CacheValues->cacheModel()->setShowNewProperties(false);
  380. this->ProgressFactor = 0.5;
  381. config_passed = doConfigureInternal();
  382. this->ProgressOffset = 0.5;
  383. }
  384. if(config_passed)
  385. {
  386. doGenerateInternal();
  387. }
  388. this->ProgressOffset = 0.0;
  389. this->ProgressFactor = 1.0;
  390. this->CacheValues->cacheModel()->setShowNewProperties(true);
  391. this->enterState(ReadyConfigure);
  392. this->ProgressBar->reset();
  393. this->ConfigureNeeded = true;
  394. }
  395. void CMakeSetupDialog::closeEvent(QCloseEvent* e)
  396. {
  397. // prompt for close if there are unsaved changes, and we're not busy
  398. if(this->CacheModified)
  399. {
  400. QString msg = tr("You have changed options but not rebuilt, "
  401. "are you sure you want to exit?");
  402. QString title = tr("Confirm Exit");
  403. QMessageBox::StandardButton btn;
  404. btn = QMessageBox::critical(this, title, msg,
  405. QMessageBox::Yes | QMessageBox::No);
  406. if(btn == QMessageBox::No)
  407. {
  408. e->ignore();
  409. }
  410. }
  411. // don't close if we're busy, unless the user really wants to
  412. if(this->CurrentState == Configuring)
  413. {
  414. QString msg = tr("You are in the middle of a Configure.\n"
  415. "If you Exit now the configure information will be lost.\n"
  416. "Are you sure you want to Exit?");
  417. QString title = tr("Confirm Exit");
  418. QMessageBox::StandardButton btn;
  419. btn = QMessageBox::critical(this, title, msg,
  420. QMessageBox::Yes | QMessageBox::No);
  421. if(btn == QMessageBox::No)
  422. {
  423. e->ignore();
  424. }
  425. else
  426. {
  427. this->doInterrupt();
  428. }
  429. }
  430. // let the generate finish
  431. if(this->CurrentState == Generating)
  432. {
  433. e->ignore();
  434. }
  435. }
  436. void CMakeSetupDialog::doHelp()
  437. {
  438. QString msg = tr("CMake is used to configure and generate build files for "
  439. "software projects. The basic steps for configuring a project are as "
  440. "follows:\r\n\r\n1. Select the source directory for the project. This should "
  441. "contain the CMakeLists.txt files for the project.\r\n\r\n2. Select the build "
  442. "directory for the project. This is the directory where the project will be "
  443. "built. It can be the same or a different directory than the source "
  444. "directory. For easy clean up, a separate build directory is recommended. "
  445. "CMake will create the directory if it does not exist.\r\n\r\n3. Once the "
  446. "source and binary directories are selected, it is time to press the "
  447. "Configure button. This will cause CMake to read all of the input files and "
  448. "discover all the variables used by the project. The first time a variable "
  449. "is displayed it will be in Red. Users should inspect red variables making "
  450. "sure the values are correct. For some projects the Configure process can "
  451. "be iterative, so continue to press the Configure button until there are no "
  452. "longer red entries.\r\n\r\n4. Once there are no longer red entries, you "
  453. "should click the Generate button. This will write the build files to the build "
  454. "directory.");
  455. QDialog dialog;
  456. QFontMetrics met(this->font());
  457. int msgWidth = met.width(msg);
  458. dialog.setMinimumSize(msgWidth/15,20);
  459. dialog.setWindowTitle(tr("Help"));
  460. QVBoxLayout* l = new QVBoxLayout(&dialog);
  461. QLabel* lab = new QLabel(&dialog);
  462. lab->setText(msg);
  463. lab->setWordWrap(true);
  464. QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Ok,
  465. Qt::Horizontal, &dialog);
  466. QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept()));
  467. l->addWidget(lab);
  468. l->addWidget(btns);
  469. dialog.exec();
  470. }
  471. void CMakeSetupDialog::doInterrupt()
  472. {
  473. this->enterState(Interrupting);
  474. this->CMakeThread->cmakeInstance()->interrupt();
  475. }
  476. void CMakeSetupDialog::doSourceBrowse()
  477. {
  478. QString dir = QFileDialog::getExistingDirectory(this,
  479. tr("Enter Path to Source"), this->SourceDirectory->text());
  480. if(!dir.isEmpty())
  481. {
  482. this->setSourceDirectory(dir);
  483. }
  484. }
  485. void CMakeSetupDialog::updateSourceDirectory(const QString& dir)
  486. {
  487. if(this->SourceDirectory->text() != dir)
  488. {
  489. this->SourceDirectory->blockSignals(true);
  490. this->SourceDirectory->setText(dir);
  491. this->SourceDirectory->blockSignals(false);
  492. }
  493. }
  494. void CMakeSetupDialog::updateBinaryDirectory(const QString& dir)
  495. {
  496. if(this->BinaryDirectory->currentText() != dir)
  497. {
  498. this->BinaryDirectory->blockSignals(true);
  499. this->BinaryDirectory->setEditText(dir);
  500. this->BinaryDirectory->blockSignals(false);
  501. }
  502. }
  503. void CMakeSetupDialog::doBinaryBrowse()
  504. {
  505. QString dir = QFileDialog::getExistingDirectory(this,
  506. tr("Enter Path to Build"), this->BinaryDirectory->currentText());
  507. if(!dir.isEmpty() && dir != this->BinaryDirectory->currentText())
  508. {
  509. this->setBinaryDirectory(dir);
  510. }
  511. }
  512. void CMakeSetupDialog::setBinaryDirectory(const QString& dir)
  513. {
  514. this->BinaryDirectory->setEditText(dir);
  515. }
  516. void CMakeSetupDialog::onSourceDirectoryChanged(const QString& dir)
  517. {
  518. this->Output->clear();
  519. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  520. "setSourceDirectory", Qt::QueuedConnection, Q_ARG(QString, dir));
  521. }
  522. void CMakeSetupDialog::onBinaryDirectoryChanged(const QString& dir)
  523. {
  524. QString title = QString(tr("CMake %1 - %2"));
  525. title = title.arg(cmVersion::GetCMakeVersion());
  526. title = title.arg(dir);
  527. this->setWindowTitle(title);
  528. this->CacheModified = false;
  529. this->CacheValues->cacheModel()->clear();
  530. qobject_cast<QCMakeCacheModelDelegate*>(this->CacheValues->itemDelegate())->clearChanges();
  531. this->Output->clear();
  532. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  533. "setBinaryDirectory", Qt::QueuedConnection, Q_ARG(QString, dir));
  534. }
  535. void CMakeSetupDialog::setSourceDirectory(const QString& dir)
  536. {
  537. this->SourceDirectory->setText(dir);
  538. }
  539. void CMakeSetupDialog::showProgress(const QString& /*msg*/, float percent)
  540. {
  541. percent = (percent * ProgressFactor) + ProgressOffset;
  542. this->ProgressBar->setValue(qRound(percent * 100));
  543. }
  544. void CMakeSetupDialog::error(const QString& msg)
  545. {
  546. this->Output->setCurrentCharFormat(this->ErrorFormat);
  547. this->Output->append(msg);
  548. }
  549. void CMakeSetupDialog::message(const QString& msg)
  550. {
  551. this->Output->setCurrentCharFormat(this->MessageFormat);
  552. this->Output->append(msg);
  553. }
  554. void CMakeSetupDialog::setEnabledState(bool enabled)
  555. {
  556. // disable parts of the GUI during configure/generate
  557. this->CacheValues->cacheModel()->setEditEnabled(enabled);
  558. this->SourceDirectory->setEnabled(enabled);
  559. this->BrowseSourceDirectoryButton->setEnabled(enabled);
  560. this->BinaryDirectory->setEnabled(enabled);
  561. this->BrowseBinaryDirectoryButton->setEnabled(enabled);
  562. this->ReloadCacheAction->setEnabled(enabled);
  563. this->DeleteCacheAction->setEnabled(enabled);
  564. this->ExitAction->setEnabled(enabled);
  565. this->ConfigureAction->setEnabled(enabled);
  566. this->AddEntry->setEnabled(enabled);
  567. this->RemoveEntry->setEnabled(false); // let selection re-enable it
  568. }
  569. bool CMakeSetupDialog::setupFirstConfigure()
  570. {
  571. FirstConfigure dialog;
  572. // initialize dialog and restore saved settings
  573. // add generators
  574. dialog.setGenerators(this->CMakeThread->cmakeInstance()->availableGenerators());
  575. // restore from settings
  576. dialog.loadFromSettings();
  577. if(dialog.exec() == QDialog::Accepted)
  578. {
  579. dialog.saveToSettings();
  580. this->CMakeThread->cmakeInstance()->setGenerator(dialog.getGenerator());
  581. QCMakeCacheModel* m = this->CacheValues->cacheModel();
  582. if(dialog.compilerSetup())
  583. {
  584. QString fortranCompiler = dialog.getFortranCompiler();
  585. if(!fortranCompiler.isEmpty())
  586. {
  587. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_Fortran_COMPILER",
  588. "Fortran compiler.", fortranCompiler, false);
  589. }
  590. QString cxxCompiler = dialog.getCXXCompiler();
  591. if(!cxxCompiler.isEmpty())
  592. {
  593. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_CXX_COMPILER",
  594. "CXX compiler.", cxxCompiler, false);
  595. }
  596. QString cCompiler = dialog.getCCompiler();
  597. if(!cCompiler.isEmpty())
  598. {
  599. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_C_COMPILER",
  600. "C compiler.", cCompiler, false);
  601. }
  602. }
  603. else if(dialog.crossCompilerSetup())
  604. {
  605. QString fortranCompiler = dialog.getFortranCompiler();
  606. if(!fortranCompiler.isEmpty())
  607. {
  608. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_Fortran_COMPILER",
  609. "Fortran compiler.", fortranCompiler, false);
  610. }
  611. QString mode = dialog.getCrossIncludeMode();
  612. m->insertProperty(QCMakeProperty::STRING, "CMAKE_FIND_ROOT_PATH_MODE_INCLUDE",
  613. tr("CMake Find Include Mode"), mode, false);
  614. mode = dialog.getCrossLibraryMode();
  615. m->insertProperty(QCMakeProperty::STRING, "CMAKE_FIND_ROOT_PATH_MODE_LIBRARY",
  616. tr("CMake Find Library Mode"), mode, false);
  617. mode = dialog.getCrossProgramMode();
  618. m->insertProperty(QCMakeProperty::STRING, "CMAKE_FIND_ROOT_PATH_MODE_PROGRAM",
  619. tr("CMake Find Program Mode"), mode, false);
  620. QString rootPath = dialog.getCrossRoot();
  621. m->insertProperty(QCMakeProperty::PATH, "CMAKE_FIND_ROOT_PATH",
  622. tr("CMake Find Root Path"), rootPath, false);
  623. QString systemName = dialog.getSystemName();
  624. m->insertProperty(QCMakeProperty::STRING, "CMAKE_SYSTEM_NAME",
  625. tr("CMake System Name"), systemName, false);
  626. QString cxxCompiler = dialog.getCXXCompiler();
  627. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_CXX_COMPILER",
  628. tr("CXX compiler."), cxxCompiler, false);
  629. QString cCompiler = dialog.getCCompiler();
  630. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_C_COMPILER",
  631. tr("C compiler."), cCompiler, false);
  632. }
  633. else if(dialog.crossCompilerToolChainFile())
  634. {
  635. QString toolchainFile = dialog.getCrossCompilerToolChainFile();
  636. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_TOOLCHAIN_FILE",
  637. tr("Cross Compile ToolChain File"), toolchainFile, false);
  638. }
  639. return true;
  640. }
  641. return false;
  642. }
  643. void CMakeSetupDialog::updateGeneratorLabel(const QString& gen)
  644. {
  645. QString str = tr("Current Generator: ");
  646. if(gen.isEmpty())
  647. {
  648. str += tr("None");
  649. }
  650. else
  651. {
  652. str += gen;
  653. }
  654. this->Generator->setText(str);
  655. }
  656. void CMakeSetupDialog::doReloadCache()
  657. {
  658. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  659. "reloadCache", Qt::QueuedConnection);
  660. }
  661. void CMakeSetupDialog::doDeleteCache()
  662. {
  663. QString title = tr("Delete Cache");
  664. QString msg = tr("Are you sure you want to delete the cache?");
  665. QMessageBox::StandardButton btn;
  666. btn = QMessageBox::information(this, title, msg,
  667. QMessageBox::Yes | QMessageBox::No);
  668. if(btn == QMessageBox::No)
  669. {
  670. return;
  671. }
  672. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  673. "deleteCache", Qt::QueuedConnection);
  674. }
  675. void CMakeSetupDialog::doAbout()
  676. {
  677. QString msg = tr("CMake %1\n"
  678. "Using Qt %2\n"
  679. "www.cmake.org");
  680. msg = msg.arg(cmVersion::GetCMakeVersion());
  681. msg = msg.arg(qVersion());
  682. QDialog dialog;
  683. dialog.setWindowTitle(tr("About"));
  684. QVBoxLayout* l = new QVBoxLayout(&dialog);
  685. QLabel* lab = new QLabel(&dialog);
  686. l->addWidget(lab);
  687. lab->setText(msg);
  688. lab->setWordWrap(true);
  689. QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Ok,
  690. Qt::Horizontal, &dialog);
  691. QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept()));
  692. l->addWidget(btns);
  693. dialog.exec();
  694. }
  695. void CMakeSetupDialog::setExitAfterGenerate(bool b)
  696. {
  697. this->ExitAfterGenerate = b;
  698. }
  699. void CMakeSetupDialog::addBinaryPath(const QString& path)
  700. {
  701. QString cleanpath = QDir::cleanPath(path);
  702. // update UI
  703. this->BinaryDirectory->blockSignals(true);
  704. int idx = this->BinaryDirectory->findText(cleanpath);
  705. if(idx != -1)
  706. {
  707. this->BinaryDirectory->removeItem(idx);
  708. }
  709. this->BinaryDirectory->insertItem(0, cleanpath);
  710. this->BinaryDirectory->setCurrentIndex(0);
  711. this->BinaryDirectory->blockSignals(false);
  712. // save to registry
  713. QStringList buildPaths = this->loadBuildPaths();
  714. buildPaths.removeAll(cleanpath);
  715. buildPaths.prepend(cleanpath);
  716. this->saveBuildPaths(buildPaths);
  717. }
  718. void CMakeSetupDialog::dragEnterEvent(QDragEnterEvent* e)
  719. {
  720. if(!(this->CurrentState == ReadyConfigure ||
  721. this->CurrentState == ReadyGenerate))
  722. {
  723. e->ignore();
  724. return;
  725. }
  726. const QMimeData* dat = e->mimeData();
  727. QList<QUrl> urls = dat->urls();
  728. QString file = urls.count() ? urls[0].toLocalFile() : QString();
  729. if(!file.isEmpty() &&
  730. (file.endsWith("CMakeCache.txt", Qt::CaseInsensitive) ||
  731. file.endsWith("CMakeLists.txt", Qt::CaseInsensitive) ) )
  732. {
  733. e->accept();
  734. }
  735. else
  736. {
  737. e->ignore();
  738. }
  739. }
  740. void CMakeSetupDialog::dropEvent(QDropEvent* e)
  741. {
  742. if(!(this->CurrentState == ReadyConfigure ||
  743. this->CurrentState == ReadyGenerate))
  744. {
  745. return;
  746. }
  747. const QMimeData* dat = e->mimeData();
  748. QList<QUrl> urls = dat->urls();
  749. QString file = urls.count() ? urls[0].toLocalFile() : QString();
  750. if(file.endsWith("CMakeCache.txt", Qt::CaseInsensitive))
  751. {
  752. QFileInfo info(file);
  753. if(this->CMakeThread->cmakeInstance()->binaryDirectory() != info.absolutePath())
  754. {
  755. this->setBinaryDirectory(info.absolutePath());
  756. }
  757. }
  758. else if(file.endsWith("CMakeLists.txt", Qt::CaseInsensitive))
  759. {
  760. QFileInfo info(file);
  761. if(this->CMakeThread->cmakeInstance()->binaryDirectory() != info.absolutePath())
  762. {
  763. this->setSourceDirectory(info.absolutePath());
  764. this->setBinaryDirectory(info.absolutePath());
  765. }
  766. }
  767. }
  768. QStringList CMakeSetupDialog::loadBuildPaths()
  769. {
  770. QSettings settings;
  771. settings.beginGroup("Settings/StartPath");
  772. QStringList buildPaths;
  773. for(int i=0; i<10; i++)
  774. {
  775. QString p = settings.value(QString("WhereBuild%1").arg(i)).toString();
  776. if(!p.isEmpty())
  777. {
  778. buildPaths.append(p);
  779. }
  780. }
  781. return buildPaths;
  782. }
  783. void CMakeSetupDialog::saveBuildPaths(const QStringList& paths)
  784. {
  785. QSettings settings;
  786. settings.beginGroup("Settings/StartPath");
  787. int num = paths.count();
  788. if(num > 10)
  789. {
  790. num = 10;
  791. }
  792. for(int i=0; i<num; i++)
  793. {
  794. settings.setValue(QString("WhereBuild%1").arg(i), paths[i]);
  795. }
  796. }
  797. void CMakeSetupDialog::setCacheModified()
  798. {
  799. this->CacheModified = true;
  800. this->enterState(ReadyConfigure);
  801. }
  802. void CMakeSetupDialog::removeSelectedCacheEntries()
  803. {
  804. QModelIndexList idxs = this->CacheValues->selectionModel()->selectedRows();
  805. QList<QPersistentModelIndex> pidxs;
  806. foreach(QModelIndex i, idxs)
  807. {
  808. pidxs.append(i);
  809. }
  810. foreach(QPersistentModelIndex pi, pidxs)
  811. {
  812. this->CacheValues->model()->removeRow(pi.row(), pi.parent());
  813. }
  814. }
  815. void CMakeSetupDialog::selectionChanged()
  816. {
  817. QModelIndexList idxs = this->CacheValues->selectionModel()->selectedRows();
  818. if(idxs.count() &&
  819. (this->CurrentState == ReadyConfigure ||
  820. this->CurrentState == ReadyGenerate) )
  821. {
  822. this->RemoveEntry->setEnabled(true);
  823. }
  824. else
  825. {
  826. this->RemoveEntry->setEnabled(false);
  827. }
  828. }
  829. void CMakeSetupDialog::enterState(CMakeSetupDialog::State s)
  830. {
  831. if(s == this->CurrentState)
  832. {
  833. return;
  834. }
  835. this->CurrentState = s;
  836. if(s == Interrupting)
  837. {
  838. this->ConfigureButton->setEnabled(false);
  839. this->GenerateButton->setEnabled(false);
  840. }
  841. else if(s == Configuring)
  842. {
  843. this->setEnabledState(false);
  844. this->GenerateButton->setEnabled(false);
  845. this->GenerateAction->setEnabled(false);
  846. this->ConfigureButton->setText(tr("&Stop"));
  847. }
  848. else if(s == Generating)
  849. {
  850. this->CacheModified = false;
  851. this->setEnabledState(false);
  852. this->ConfigureButton->setEnabled(false);
  853. this->GenerateAction->setEnabled(false);
  854. this->GenerateButton->setText(tr("&Stop"));
  855. }
  856. else if(s == ReadyConfigure)
  857. {
  858. this->setEnabledState(true);
  859. this->GenerateButton->setEnabled(true);
  860. this->GenerateAction->setEnabled(true);
  861. this->ConfigureButton->setEnabled(true);
  862. this->ConfigureButton->setText(tr("&Configure"));
  863. this->GenerateButton->setText(tr("&Generate"));
  864. }
  865. else if(s == ReadyGenerate)
  866. {
  867. this->setEnabledState(true);
  868. this->GenerateButton->setEnabled(true);
  869. this->GenerateAction->setEnabled(true);
  870. this->ConfigureButton->setEnabled(true);
  871. this->ConfigureButton->setText(tr("&Configure"));
  872. this->GenerateButton->setText(tr("&Generate"));
  873. }
  874. }
  875. void CMakeSetupDialog::addCacheEntry()
  876. {
  877. QDialog dialog(this);
  878. dialog.resize(400, 200);
  879. dialog.setWindowTitle(tr("Add Cache Entry"));
  880. QVBoxLayout* l = new QVBoxLayout(&dialog);
  881. AddCacheEntry* w = new AddCacheEntry(&dialog, this->AddVariableCompletions);
  882. QDialogButtonBox* btns = new QDialogButtonBox(
  883. QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
  884. Qt::Horizontal, &dialog);
  885. QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept()));
  886. QObject::connect(btns, SIGNAL(rejected()), &dialog, SLOT(reject()));
  887. l->addWidget(w);
  888. l->addStretch();
  889. l->addWidget(btns);
  890. if(QDialog::Accepted == dialog.exec())
  891. {
  892. QCMakeCacheModel* m = this->CacheValues->cacheModel();
  893. m->insertProperty(w->type(), w->name(), w->description(), w->value(), false);
  894. // only add variable names to the completion which are new
  895. if (!this->AddVariableCompletions.contains(w->name()))
  896. {
  897. this->AddVariableCompletions << w->name();
  898. // limit to at most 100 completion items
  899. if (this->AddVariableCompletions.size() > 100)
  900. {
  901. this->AddVariableCompletions.removeFirst();
  902. }
  903. // make sure CMAKE_INSTALL_PREFIX is always there
  904. if (!this->AddVariableCompletions.contains("CMAKE_INSTALL_PREFIX"))
  905. {
  906. this->AddVariableCompletions << QString("CMAKE_INSTALL_PREFIX");
  907. }
  908. QSettings settings;
  909. settings.beginGroup("Settings/StartPath");
  910. settings.setValue("AddVariableCompletionEntries",
  911. this->AddVariableCompletions);
  912. }
  913. }
  914. }
  915. void CMakeSetupDialog::startSearch()
  916. {
  917. this->Search->setFocus(Qt::OtherFocusReason);
  918. this->Search->selectAll();
  919. }
  920. void CMakeSetupDialog::setDebugOutput(bool flag)
  921. {
  922. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  923. "setDebugOutput", Qt::QueuedConnection, Q_ARG(bool, flag));
  924. }
  925. void CMakeSetupDialog::setGroupedView(bool v)
  926. {
  927. this->CacheValues->cacheModel()->setViewType(v ? QCMakeCacheModel::GroupView : QCMakeCacheModel::FlatView);
  928. this->CacheValues->setRootIsDecorated(v);
  929. QSettings settings;
  930. settings.beginGroup("Settings/StartPath");
  931. settings.setValue("GroupView", v);
  932. }
  933. void CMakeSetupDialog::setAdvancedView(bool v)
  934. {
  935. this->CacheValues->setShowAdvanced(v);
  936. QSettings settings;
  937. settings.beginGroup("Settings/StartPath");
  938. settings.setValue("AdvancedView", v);
  939. }
  940. void CMakeSetupDialog::showUserChanges()
  941. {
  942. QSet<QCMakeProperty> changes =
  943. qobject_cast<QCMakeCacheModelDelegate*>(this->CacheValues->itemDelegate())->changes();
  944. QDialog dialog(this);
  945. dialog.setWindowTitle(tr("My Changes"));
  946. dialog.resize(600, 400);
  947. QVBoxLayout* l = new QVBoxLayout(&dialog);
  948. QTextEdit* textedit = new QTextEdit(&dialog);
  949. textedit->setReadOnly(true);
  950. l->addWidget(textedit);
  951. QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Close,
  952. Qt::Horizontal, &dialog);
  953. QObject::connect(btns, SIGNAL(rejected()), &dialog, SLOT(accept()));
  954. l->addWidget(btns);
  955. QString command;
  956. QString cache;
  957. foreach(QCMakeProperty prop, changes)
  958. {
  959. QString type;
  960. switch(prop.Type)
  961. {
  962. case QCMakeProperty::BOOL:
  963. type = "BOOL";
  964. break;
  965. case QCMakeProperty::PATH:
  966. type = "PATH";
  967. break;
  968. case QCMakeProperty::FILEPATH:
  969. type = "FILEPATH";
  970. break;
  971. case QCMakeProperty::STRING:
  972. type = "STRING";
  973. break;
  974. }
  975. QString value;
  976. if(prop.Type == QCMakeProperty::BOOL)
  977. {
  978. value = prop.Value.toBool() ? "1" : "0";
  979. }
  980. else
  981. {
  982. value = prop.Value.toString();
  983. }
  984. QString line("%1:%2=");
  985. line = line.arg(prop.Key);
  986. line = line.arg(type);
  987. command += QString("-D%1\"%2\" ").arg(line).arg(value);
  988. cache += QString("%1%2\n").arg(line).arg(value);
  989. }
  990. textedit->append(tr("Commandline options:"));
  991. textedit->append(command);
  992. textedit->append("\n");
  993. textedit->append(tr("Cache file:"));
  994. textedit->append(cache);
  995. dialog.exec();
  996. }
  997. void CMakeSetupDialog::setSearchFilter(const QString& str)
  998. {
  999. this->CacheValues->selectionModel()->clear();
  1000. this->CacheValues->setSearchFilter(str);
  1001. }