CMakeSetupDialog.cxx 40 KB

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