CMakeSetupDialog.cxx 32 KB

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