CMakeSetupDialog.cxx 37 KB

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