CMakeSetupDialog.cxx 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154
  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(2000);
  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. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  476. "interrupt", Qt::QueuedConnection);
  477. }
  478. void CMakeSetupDialog::doSourceBrowse()
  479. {
  480. QString dir = QFileDialog::getExistingDirectory(this,
  481. tr("Enter Path to Source"), this->SourceDirectory->text());
  482. if(!dir.isEmpty())
  483. {
  484. this->setSourceDirectory(dir);
  485. }
  486. }
  487. void CMakeSetupDialog::updateSourceDirectory(const QString& dir)
  488. {
  489. if(this->SourceDirectory->text() != dir)
  490. {
  491. this->SourceDirectory->blockSignals(true);
  492. this->SourceDirectory->setText(dir);
  493. this->SourceDirectory->blockSignals(false);
  494. }
  495. }
  496. void CMakeSetupDialog::updateBinaryDirectory(const QString& dir)
  497. {
  498. if(this->BinaryDirectory->currentText() != dir)
  499. {
  500. this->BinaryDirectory->blockSignals(true);
  501. this->BinaryDirectory->setEditText(dir);
  502. this->BinaryDirectory->blockSignals(false);
  503. }
  504. }
  505. void CMakeSetupDialog::doBinaryBrowse()
  506. {
  507. QString dir = QFileDialog::getExistingDirectory(this,
  508. tr("Enter Path to Build"), this->BinaryDirectory->currentText());
  509. if(!dir.isEmpty() && dir != this->BinaryDirectory->currentText())
  510. {
  511. this->setBinaryDirectory(dir);
  512. }
  513. }
  514. void CMakeSetupDialog::setBinaryDirectory(const QString& dir)
  515. {
  516. this->BinaryDirectory->setEditText(dir);
  517. }
  518. void CMakeSetupDialog::onSourceDirectoryChanged(const QString& dir)
  519. {
  520. this->Output->clear();
  521. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  522. "setSourceDirectory", Qt::QueuedConnection, Q_ARG(QString, dir));
  523. }
  524. void CMakeSetupDialog::onBinaryDirectoryChanged(const QString& dir)
  525. {
  526. QString title = QString(tr("CMake %1 - %2"));
  527. title = title.arg(cmVersion::GetCMakeVersion());
  528. title = title.arg(dir);
  529. this->setWindowTitle(title);
  530. this->CacheModified = false;
  531. this->CacheValues->cacheModel()->clear();
  532. qobject_cast<QCMakeCacheModelDelegate*>(this->CacheValues->itemDelegate())->clearChanges();
  533. this->Output->clear();
  534. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  535. "setBinaryDirectory", Qt::QueuedConnection, Q_ARG(QString, dir));
  536. }
  537. void CMakeSetupDialog::setSourceDirectory(const QString& dir)
  538. {
  539. this->SourceDirectory->setText(dir);
  540. }
  541. void CMakeSetupDialog::showProgress(const QString& /*msg*/, float percent)
  542. {
  543. percent = (percent * ProgressFactor) + ProgressOffset;
  544. this->ProgressBar->setValue(qRound(percent * 100));
  545. }
  546. void CMakeSetupDialog::error(const QString& msg)
  547. {
  548. this->Output->setCurrentCharFormat(this->ErrorFormat);
  549. this->Output->append(msg);
  550. }
  551. void CMakeSetupDialog::message(const QString& msg)
  552. {
  553. this->Output->setCurrentCharFormat(this->MessageFormat);
  554. this->Output->append(msg);
  555. }
  556. void CMakeSetupDialog::setEnabledState(bool enabled)
  557. {
  558. // disable parts of the GUI during configure/generate
  559. this->CacheValues->cacheModel()->setEditEnabled(enabled);
  560. this->SourceDirectory->setEnabled(enabled);
  561. this->BrowseSourceDirectoryButton->setEnabled(enabled);
  562. this->BinaryDirectory->setEnabled(enabled);
  563. this->BrowseBinaryDirectoryButton->setEnabled(enabled);
  564. this->ReloadCacheAction->setEnabled(enabled);
  565. this->DeleteCacheAction->setEnabled(enabled);
  566. this->ExitAction->setEnabled(enabled);
  567. this->ConfigureAction->setEnabled(enabled);
  568. this->AddEntry->setEnabled(enabled);
  569. this->RemoveEntry->setEnabled(false); // let selection re-enable it
  570. }
  571. bool CMakeSetupDialog::setupFirstConfigure()
  572. {
  573. FirstConfigure dialog;
  574. // initialize dialog and restore saved settings
  575. // add generators
  576. dialog.setGenerators(this->CMakeThread->cmakeInstance()->availableGenerators());
  577. // restore from settings
  578. dialog.loadFromSettings();
  579. if(dialog.exec() == QDialog::Accepted)
  580. {
  581. dialog.saveToSettings();
  582. this->CMakeThread->cmakeInstance()->setGenerator(dialog.getGenerator());
  583. QCMakeCacheModel* m = this->CacheValues->cacheModel();
  584. if(dialog.compilerSetup())
  585. {
  586. QString fortranCompiler = dialog.getFortranCompiler();
  587. if(!fortranCompiler.isEmpty())
  588. {
  589. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_Fortran_COMPILER",
  590. "Fortran compiler.", fortranCompiler, false);
  591. }
  592. QString cxxCompiler = dialog.getCXXCompiler();
  593. if(!cxxCompiler.isEmpty())
  594. {
  595. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_CXX_COMPILER",
  596. "CXX compiler.", cxxCompiler, false);
  597. }
  598. QString cCompiler = dialog.getCCompiler();
  599. if(!cCompiler.isEmpty())
  600. {
  601. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_C_COMPILER",
  602. "C compiler.", cCompiler, false);
  603. }
  604. }
  605. else if(dialog.crossCompilerSetup())
  606. {
  607. QString fortranCompiler = dialog.getFortranCompiler();
  608. if(!fortranCompiler.isEmpty())
  609. {
  610. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_Fortran_COMPILER",
  611. "Fortran compiler.", fortranCompiler, false);
  612. }
  613. QString mode = dialog.getCrossIncludeMode();
  614. m->insertProperty(QCMakeProperty::STRING, "CMAKE_FIND_ROOT_PATH_MODE_INCLUDE",
  615. "CMake Find Include Mode", mode, false);
  616. mode = dialog.getCrossLibraryMode();
  617. m->insertProperty(QCMakeProperty::STRING, "CMAKE_FIND_ROOT_PATH_MODE_LIBRARY",
  618. "CMake Find Library Mode", mode, false);
  619. mode = dialog.getCrossProgramMode();
  620. m->insertProperty(QCMakeProperty::STRING, "CMAKE_FIND_ROOT_PATH_MODE_PROGRAM",
  621. "CMake Find Program Mode", mode, false);
  622. QString rootPath = dialog.getCrossRoot();
  623. m->insertProperty(QCMakeProperty::PATH, "CMAKE_FIND_ROOT_PATH",
  624. "CMake Find Root Path", rootPath, false);
  625. QString systemName = dialog.getSystemName();
  626. m->insertProperty(QCMakeProperty::STRING, "CMAKE_SYSTEM_NAME",
  627. "CMake System Name", systemName, false);
  628. QString cxxCompiler = dialog.getCXXCompiler();
  629. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_CXX_COMPILER",
  630. "CXX compiler.", cxxCompiler, false);
  631. QString cCompiler = dialog.getCCompiler();
  632. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_C_COMPILER",
  633. "C compiler.", cCompiler, false);
  634. }
  635. else if(dialog.crossCompilerToolChainFile())
  636. {
  637. QString toolchainFile = dialog.getCrossCompilerToolChainFile();
  638. m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_TOOLCHAIN_FILE",
  639. "Cross Compile ToolChain File", toolchainFile, false);
  640. }
  641. return true;
  642. }
  643. return false;
  644. }
  645. void CMakeSetupDialog::updateGeneratorLabel(const QString& gen)
  646. {
  647. QString str = tr("Current Generator: ");
  648. if(gen.isEmpty())
  649. {
  650. str += tr("None");
  651. }
  652. else
  653. {
  654. str += gen;
  655. }
  656. this->Generator->setText(str);
  657. }
  658. void CMakeSetupDialog::doReloadCache()
  659. {
  660. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  661. "reloadCache", Qt::QueuedConnection);
  662. }
  663. void CMakeSetupDialog::doDeleteCache()
  664. {
  665. QString title = tr("Delete Cache");
  666. QString msg = "Are you sure you want to delete the cache?";
  667. QMessageBox::StandardButton btn;
  668. btn = QMessageBox::information(this, title, msg,
  669. QMessageBox::Yes | QMessageBox::No);
  670. if(btn == QMessageBox::No)
  671. {
  672. return;
  673. }
  674. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  675. "deleteCache", Qt::QueuedConnection);
  676. }
  677. void CMakeSetupDialog::doAbout()
  678. {
  679. QString msg = "CMake %1\n"
  680. "Using Qt %2\n"
  681. "www.cmake.org";
  682. msg = msg.arg(cmVersion::GetCMakeVersion());
  683. msg = msg.arg(qVersion());
  684. QDialog dialog;
  685. dialog.setWindowTitle(tr("About"));
  686. QVBoxLayout* l = new QVBoxLayout(&dialog);
  687. QLabel* lab = new QLabel(&dialog);
  688. l->addWidget(lab);
  689. lab->setText(msg);
  690. lab->setWordWrap(true);
  691. QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Ok,
  692. Qt::Horizontal, &dialog);
  693. QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept()));
  694. l->addWidget(btns);
  695. dialog.exec();
  696. }
  697. void CMakeSetupDialog::setExitAfterGenerate(bool b)
  698. {
  699. this->ExitAfterGenerate = b;
  700. }
  701. void CMakeSetupDialog::addBinaryPath(const QString& path)
  702. {
  703. QString cleanpath = QDir::cleanPath(path);
  704. // update UI
  705. this->BinaryDirectory->blockSignals(true);
  706. int idx = this->BinaryDirectory->findText(cleanpath);
  707. if(idx != -1)
  708. {
  709. this->BinaryDirectory->removeItem(idx);
  710. }
  711. this->BinaryDirectory->insertItem(0, cleanpath);
  712. this->BinaryDirectory->setCurrentIndex(0);
  713. this->BinaryDirectory->blockSignals(false);
  714. // save to registry
  715. QStringList buildPaths = this->loadBuildPaths();
  716. buildPaths.removeAll(cleanpath);
  717. buildPaths.prepend(cleanpath);
  718. this->saveBuildPaths(buildPaths);
  719. }
  720. void CMakeSetupDialog::dragEnterEvent(QDragEnterEvent* e)
  721. {
  722. if(!(this->CurrentState == ReadyConfigure ||
  723. this->CurrentState == ReadyGenerate))
  724. {
  725. e->ignore();
  726. return;
  727. }
  728. const QMimeData* dat = e->mimeData();
  729. QList<QUrl> urls = dat->urls();
  730. QString file = urls.count() ? urls[0].toLocalFile() : QString();
  731. if(!file.isEmpty() &&
  732. (file.endsWith("CMakeCache.txt", Qt::CaseInsensitive) ||
  733. file.endsWith("CMakeLists.txt", Qt::CaseInsensitive) ) )
  734. {
  735. e->accept();
  736. }
  737. else
  738. {
  739. e->ignore();
  740. }
  741. }
  742. void CMakeSetupDialog::dropEvent(QDropEvent* e)
  743. {
  744. if(!(this->CurrentState == ReadyConfigure ||
  745. this->CurrentState == ReadyGenerate))
  746. {
  747. return;
  748. }
  749. const QMimeData* dat = e->mimeData();
  750. QList<QUrl> urls = dat->urls();
  751. QString file = urls.count() ? urls[0].toLocalFile() : QString();
  752. if(file.endsWith("CMakeCache.txt", Qt::CaseInsensitive))
  753. {
  754. QFileInfo info(file);
  755. if(this->CMakeThread->cmakeInstance()->binaryDirectory() != info.absolutePath())
  756. {
  757. this->setBinaryDirectory(info.absolutePath());
  758. }
  759. }
  760. else if(file.endsWith("CMakeLists.txt", Qt::CaseInsensitive))
  761. {
  762. QFileInfo info(file);
  763. if(this->CMakeThread->cmakeInstance()->binaryDirectory() != info.absolutePath())
  764. {
  765. this->setSourceDirectory(info.absolutePath());
  766. this->setBinaryDirectory(info.absolutePath());
  767. }
  768. }
  769. }
  770. QStringList CMakeSetupDialog::loadBuildPaths()
  771. {
  772. QSettings settings;
  773. settings.beginGroup("Settings/StartPath");
  774. QStringList buildPaths;
  775. for(int i=0; i<10; i++)
  776. {
  777. QString p = settings.value(QString("WhereBuild%1").arg(i)).toString();
  778. if(!p.isEmpty())
  779. {
  780. buildPaths.append(p);
  781. }
  782. }
  783. return buildPaths;
  784. }
  785. void CMakeSetupDialog::saveBuildPaths(const QStringList& paths)
  786. {
  787. QSettings settings;
  788. settings.beginGroup("Settings/StartPath");
  789. int num = paths.count();
  790. if(num > 10)
  791. {
  792. num = 10;
  793. }
  794. for(int i=0; i<num; i++)
  795. {
  796. settings.setValue(QString("WhereBuild%1").arg(i), paths[i]);
  797. }
  798. }
  799. void CMakeSetupDialog::setCacheModified()
  800. {
  801. this->CacheModified = true;
  802. this->enterState(ReadyConfigure);
  803. }
  804. void CMakeSetupDialog::removeSelectedCacheEntries()
  805. {
  806. QModelIndexList idxs = this->CacheValues->selectionModel()->selectedRows();
  807. QList<QPersistentModelIndex> pidxs;
  808. foreach(QModelIndex i, idxs)
  809. {
  810. pidxs.append(i);
  811. }
  812. foreach(QPersistentModelIndex pi, pidxs)
  813. {
  814. this->CacheValues->model()->removeRow(pi.row(), pi.parent());
  815. }
  816. }
  817. void CMakeSetupDialog::selectionChanged()
  818. {
  819. QModelIndexList idxs = this->CacheValues->selectionModel()->selectedRows();
  820. if(idxs.count() &&
  821. (this->CurrentState == ReadyConfigure ||
  822. this->CurrentState == ReadyGenerate) )
  823. {
  824. this->RemoveEntry->setEnabled(true);
  825. }
  826. else
  827. {
  828. this->RemoveEntry->setEnabled(false);
  829. }
  830. }
  831. void CMakeSetupDialog::enterState(CMakeSetupDialog::State s)
  832. {
  833. if(s == this->CurrentState)
  834. {
  835. return;
  836. }
  837. this->CurrentState = s;
  838. if(s == Interrupting)
  839. {
  840. this->ConfigureButton->setEnabled(false);
  841. this->GenerateButton->setEnabled(false);
  842. }
  843. else if(s == Configuring)
  844. {
  845. this->setEnabledState(false);
  846. this->GenerateButton->setEnabled(false);
  847. this->GenerateAction->setEnabled(false);
  848. this->ConfigureButton->setText(tr("&Stop"));
  849. }
  850. else if(s == Generating)
  851. {
  852. this->CacheModified = false;
  853. this->setEnabledState(false);
  854. this->ConfigureButton->setEnabled(false);
  855. this->GenerateAction->setEnabled(false);
  856. this->GenerateButton->setText(tr("&Stop"));
  857. }
  858. else if(s == ReadyConfigure)
  859. {
  860. this->setEnabledState(true);
  861. this->GenerateButton->setEnabled(true);
  862. this->GenerateAction->setEnabled(true);
  863. this->ConfigureButton->setEnabled(true);
  864. this->ConfigureButton->setText(tr("&Configure"));
  865. this->GenerateButton->setText(tr("&Generate"));
  866. }
  867. else if(s == ReadyGenerate)
  868. {
  869. this->setEnabledState(true);
  870. this->GenerateButton->setEnabled(true);
  871. this->GenerateAction->setEnabled(true);
  872. this->ConfigureButton->setEnabled(true);
  873. this->ConfigureButton->setText(tr("&Configure"));
  874. this->GenerateButton->setText(tr("&Generate"));
  875. }
  876. }
  877. void CMakeSetupDialog::addCacheEntry()
  878. {
  879. QDialog dialog(this);
  880. dialog.resize(400, 200);
  881. dialog.setWindowTitle(tr("Add Cache Entry"));
  882. QVBoxLayout* l = new QVBoxLayout(&dialog);
  883. AddCacheEntry* w = new AddCacheEntry(&dialog, this->AddVariableCompletions);
  884. QDialogButtonBox* btns = new QDialogButtonBox(
  885. QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
  886. Qt::Horizontal, &dialog);
  887. QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept()));
  888. QObject::connect(btns, SIGNAL(rejected()), &dialog, SLOT(reject()));
  889. l->addWidget(w);
  890. l->addStretch();
  891. l->addWidget(btns);
  892. if(QDialog::Accepted == dialog.exec())
  893. {
  894. QCMakeCacheModel* m = this->CacheValues->cacheModel();
  895. m->insertProperty(w->type(), w->name(), w->description(), w->value(), false);
  896. // only add variable names to the completion which are new
  897. if (!this->AddVariableCompletions.contains(w->name()))
  898. {
  899. this->AddVariableCompletions << w->name();
  900. // limit to at most 100 completion items
  901. if (this->AddVariableCompletions.size() > 100)
  902. {
  903. this->AddVariableCompletions.removeFirst();
  904. }
  905. // make sure CMAKE_INSTALL_PREFIX is always there
  906. if (!this->AddVariableCompletions.contains("CMAKE_INSTALL_PREFIX"))
  907. {
  908. this->AddVariableCompletions << QString("CMAKE_INSTALL_PREFIX");
  909. }
  910. QSettings settings;
  911. settings.beginGroup("Settings/StartPath");
  912. settings.setValue("AddVariableCompletionEntries",
  913. this->AddVariableCompletions);
  914. }
  915. }
  916. }
  917. void CMakeSetupDialog::startSearch()
  918. {
  919. this->Search->setFocus(Qt::OtherFocusReason);
  920. this->Search->selectAll();
  921. }
  922. void CMakeSetupDialog::setDebugOutput(bool flag)
  923. {
  924. QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(),
  925. "setDebugOutput", Qt::QueuedConnection, Q_ARG(bool, flag));
  926. }
  927. void CMakeSetupDialog::setGroupedView(bool v)
  928. {
  929. this->CacheValues->cacheModel()->setViewType(v ? QCMakeCacheModel::GroupView : QCMakeCacheModel::FlatView);
  930. this->CacheValues->setRootIsDecorated(v);
  931. QSettings settings;
  932. settings.beginGroup("Settings/StartPath");
  933. settings.setValue("GroupView", v);
  934. }
  935. void CMakeSetupDialog::setAdvancedView(bool v)
  936. {
  937. this->CacheValues->setShowAdvanced(v);
  938. QSettings settings;
  939. settings.beginGroup("Settings/StartPath");
  940. settings.setValue("AdvancedView", v);
  941. }
  942. void CMakeSetupDialog::showUserChanges()
  943. {
  944. QSet<QCMakeProperty> changes =
  945. qobject_cast<QCMakeCacheModelDelegate*>(this->CacheValues->itemDelegate())->changes();
  946. QDialog dialog(this);
  947. dialog.setWindowTitle(tr("My Changes"));
  948. dialog.resize(600, 400);
  949. QVBoxLayout* l = new QVBoxLayout(&dialog);
  950. QTextEdit* textedit = new QTextEdit(&dialog);
  951. textedit->setReadOnly(true);
  952. l->addWidget(textedit);
  953. QDialogButtonBox* btns = new QDialogButtonBox(QDialogButtonBox::Close,
  954. Qt::Horizontal, &dialog);
  955. QObject::connect(btns, SIGNAL(rejected()), &dialog, SLOT(accept()));
  956. l->addWidget(btns);
  957. QString command;
  958. QString cache;
  959. foreach(QCMakeProperty prop, changes)
  960. {
  961. QString type;
  962. switch(prop.Type)
  963. {
  964. case QCMakeProperty::BOOL:
  965. type = "BOOL";
  966. break;
  967. case QCMakeProperty::PATH:
  968. type = "PATH";
  969. break;
  970. case QCMakeProperty::FILEPATH:
  971. type = "FILEPATH";
  972. break;
  973. case QCMakeProperty::STRING:
  974. type = "STRING";
  975. break;
  976. }
  977. QString value;
  978. if(prop.Type == QCMakeProperty::BOOL)
  979. {
  980. value = prop.Value.toBool() ? "1" : "0";
  981. }
  982. else
  983. {
  984. value = prop.Value.toString();
  985. }
  986. QString line("%1:%2=");
  987. line = line.arg(prop.Key);
  988. line = line.arg(type);
  989. command += QString("-D%1\"%2\" ").arg(line).arg(value);
  990. cache += QString("%1%2\n").arg(line).arg(value);
  991. }
  992. textedit->append(tr("Commandline options:"));
  993. textedit->append(command);
  994. textedit->append("\n");
  995. textedit->append(tr("Cache file:"));
  996. textedit->append(cache);
  997. dialog.exec();
  998. }
  999. void CMakeSetupDialog::setSearchFilter(const QString& str)
  1000. {
  1001. this->CacheValues->selectionModel()->clear();
  1002. this->CacheValues->setSearchFilter(str);
  1003. }