CMakeSetupDialog.cxx 40 KB

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