cmCursesMainForm.cxx 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324
  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 "../cmSystemTools.h"
  11. #include "../cmVersion.h"
  12. #include "../cmake.h"
  13. #include "cmCursesMainForm.h"
  14. #include "cmCursesStringWidget.h"
  15. #include "cmCursesLabelWidget.h"
  16. #include "cmCursesBoolWidget.h"
  17. #include "cmCursesPathWidget.h"
  18. #include "cmCursesFilePathWidget.h"
  19. #include "cmCursesDummyWidget.h"
  20. #include "cmCursesCacheEntryComposite.h"
  21. #include "cmCursesLongMessageForm.h"
  22. #include "cmAlgorithms.h"
  23. #include "cmState.h"
  24. inline int ctrl(int z)
  25. {
  26. return (z&037);
  27. }
  28. cmCursesMainForm::cmCursesMainForm(std::vector<std::string> const& args,
  29. int initWidth) :
  30. Args(args), InitialWidth(initWidth)
  31. {
  32. this->NumberOfPages = 0;
  33. this->Fields = 0;
  34. this->Entries = 0;
  35. this->AdvancedMode = false;
  36. this->NumberOfVisibleEntries = 0;
  37. this->OkToGenerate = false;
  38. this->HelpMessage.push_back("Welcome to ccmake, curses based user interface for CMake.");
  39. this->HelpMessage.push_back("");
  40. this->HelpMessage.push_back(s_ConstHelpMessage);
  41. this->CMakeInstance = new cmake;
  42. this->CMakeInstance->SetCMakeEditCommand(
  43. cmSystemTools::GetCMakeCursesCommand());
  44. // create the arguments for the cmake object
  45. std::string whereCMake = cmSystemTools::GetProgramPath(this->Args[0]);
  46. whereCMake += "/cmake";
  47. this->Args[0] = whereCMake;
  48. this->CMakeInstance->SetArgs(this->Args);
  49. this->SearchString = "";
  50. this->OldSearchString = "";
  51. this->SearchMode = false;
  52. }
  53. cmCursesMainForm::~cmCursesMainForm()
  54. {
  55. if (this->Form)
  56. {
  57. unpost_form(this->Form);
  58. free_form(this->Form);
  59. this->Form = 0;
  60. }
  61. delete[] this->Fields;
  62. // Clean-up composites
  63. if (this->Entries)
  64. {
  65. cmDeleteAll(*this->Entries);
  66. }
  67. delete this->Entries;
  68. if (this->CMakeInstance)
  69. {
  70. delete this->CMakeInstance;
  71. this->CMakeInstance = 0;
  72. }
  73. }
  74. // See if a cache entry is in the list of entries in the ui.
  75. bool cmCursesMainForm::LookForCacheEntry(const std::string& key)
  76. {
  77. if (!this->Entries)
  78. {
  79. return false;
  80. }
  81. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  82. for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
  83. {
  84. if (key == (*it)->Key)
  85. {
  86. return true;
  87. }
  88. }
  89. return false;
  90. }
  91. // Create new cmCursesCacheEntryComposite entries from the cache
  92. void cmCursesMainForm::InitializeUI()
  93. {
  94. // Create a vector of cmCursesCacheEntryComposite's
  95. // which contain labels, entries and new entry markers
  96. std::vector<cmCursesCacheEntryComposite*>* newEntries =
  97. new std::vector<cmCursesCacheEntryComposite*>;
  98. std::vector<std::string> cacheKeys =
  99. this->CMakeInstance->GetState()->GetCacheEntryKeys();
  100. newEntries->reserve(cacheKeys.size());
  101. // Count non-internal and non-static entries
  102. int count=0;
  103. for(std::vector<std::string>::const_iterator it = cacheKeys.begin();
  104. it != cacheKeys.end(); ++it)
  105. {
  106. cmState::CacheEntryType t = this->CMakeInstance->GetState()
  107. ->GetCacheEntryType(*it);
  108. if (t != cmState::INTERNAL &&
  109. t != cmState::STATIC &&
  110. t != cmState::UNINITIALIZED)
  111. {
  112. ++count;
  113. }
  114. }
  115. int entrywidth = this->InitialWidth - 35;
  116. cmCursesCacheEntryComposite* comp;
  117. if ( count == 0 )
  118. {
  119. // If cache is empty, display a label saying so and a
  120. // dummy entry widget (does not respond to input)
  121. comp = new cmCursesCacheEntryComposite("EMPTY CACHE", 30, 30);
  122. comp->Entry = new cmCursesDummyWidget(1, 1, 1, 1);
  123. newEntries->push_back(comp);
  124. }
  125. else
  126. {
  127. // Create the composites.
  128. // First add entries which are new
  129. for(std::vector<std::string>::const_iterator it = cacheKeys.begin();
  130. it != cacheKeys.end(); ++it)
  131. {
  132. std::string key = *it;
  133. cmState::CacheEntryType t = this->CMakeInstance->GetState()
  134. ->GetCacheEntryType(*it);
  135. if (t == cmState::INTERNAL ||
  136. t == cmState::STATIC ||
  137. t == cmState::UNINITIALIZED )
  138. {
  139. continue;
  140. }
  141. if (!this->LookForCacheEntry(key))
  142. {
  143. newEntries->push_back(new cmCursesCacheEntryComposite(key,
  144. this->CMakeInstance,
  145. true, 30,
  146. entrywidth));
  147. this->OkToGenerate = false;
  148. }
  149. }
  150. // then add entries which are old
  151. for(std::vector<std::string>::const_iterator it = cacheKeys.begin();
  152. it != cacheKeys.end(); ++it)
  153. {
  154. std::string key = *it;
  155. cmState::CacheEntryType t = this->CMakeInstance->GetState()
  156. ->GetCacheEntryType(*it);
  157. if (t == cmState::INTERNAL ||
  158. t == cmState::STATIC ||
  159. t == cmState::UNINITIALIZED )
  160. {
  161. continue;
  162. }
  163. if (this->LookForCacheEntry(key))
  164. {
  165. newEntries->push_back(new cmCursesCacheEntryComposite(key,
  166. this->CMakeInstance,
  167. false, 30,
  168. entrywidth));
  169. }
  170. }
  171. }
  172. // Clean old entries
  173. if (this->Entries)
  174. {
  175. cmDeleteAll(*this->Entries);
  176. }
  177. delete this->Entries;
  178. this->Entries = newEntries;
  179. // Compute fields from composites
  180. this->RePost();
  181. }
  182. void cmCursesMainForm::RePost()
  183. {
  184. // Create the fields to be passed to the form.
  185. if (this->Form)
  186. {
  187. unpost_form(this->Form);
  188. free_form(this->Form);
  189. this->Form = 0;
  190. }
  191. delete[] this->Fields;
  192. if (this->AdvancedMode)
  193. {
  194. this->NumberOfVisibleEntries = this->Entries->size();
  195. }
  196. else
  197. {
  198. // If normal mode, count only non-advanced entries
  199. this->NumberOfVisibleEntries = 0;
  200. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  201. for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
  202. {
  203. const char* existingValue =
  204. this->CMakeInstance->GetState()
  205. ->GetCacheEntryValue((*it)->GetValue());
  206. bool advanced =
  207. this->CMakeInstance->GetState()
  208. ->GetCacheEntryPropertyAsBool((*it)->GetValue(), "ADVANCED");
  209. if (!existingValue || (!this->AdvancedMode && advanced))
  210. {
  211. continue;
  212. }
  213. this->NumberOfVisibleEntries++;
  214. }
  215. }
  216. // there is always one even if it is the dummy one
  217. if(this->NumberOfVisibleEntries == 0)
  218. {
  219. this->NumberOfVisibleEntries = 1;
  220. }
  221. // Assign the fields: 3 for each entry: label, new entry marker
  222. // ('*' or ' ') and entry widget
  223. this->Fields = new FIELD*[3*this->NumberOfVisibleEntries+1];
  224. size_t cc;
  225. for ( cc = 0; cc < 3 * this->NumberOfVisibleEntries+1; cc ++ )
  226. {
  227. this->Fields[cc] = 0;
  228. }
  229. // Assign fields
  230. int j=0;
  231. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  232. for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
  233. {
  234. const char* existingValue =
  235. this->CMakeInstance->GetState()
  236. ->GetCacheEntryValue((*it)->GetValue());
  237. bool advanced =
  238. this->CMakeInstance->GetState()
  239. ->GetCacheEntryPropertyAsBool((*it)->GetValue(), "ADVANCED");
  240. if (!existingValue || (!this->AdvancedMode && advanced))
  241. {
  242. continue;
  243. }
  244. this->Fields[3*j] = (*it)->Label->Field;
  245. this->Fields[3*j+1] = (*it)->IsNewLabel->Field;
  246. this->Fields[3*j+2] = (*it)->Entry->Field;
  247. j++;
  248. }
  249. // if no cache entries there should still be one dummy field
  250. if(j == 0)
  251. {
  252. it = this->Entries->begin();
  253. this->Fields[0] = (*it)->Label->Field;
  254. this->Fields[1] = (*it)->IsNewLabel->Field;
  255. this->Fields[2] = (*it)->Entry->Field;
  256. this->NumberOfVisibleEntries = 1;
  257. }
  258. // Has to be null terminated.
  259. this->Fields[3*this->NumberOfVisibleEntries] = 0;
  260. }
  261. void cmCursesMainForm::Render(int left, int top, int width, int height)
  262. {
  263. if (this->Form)
  264. {
  265. FIELD* currentField = current_field(this->Form);
  266. cmCursesWidget* cw = reinterpret_cast<cmCursesWidget*>
  267. (field_userptr(currentField));
  268. // If in edit mode, get out of it
  269. if ( cw->GetType() == cmState::STRING ||
  270. cw->GetType() == cmState::PATH ||
  271. cw->GetType() == cmState::FILEPATH )
  272. {
  273. cmCursesStringWidget* sw = static_cast<cmCursesStringWidget*>(cw);
  274. sw->SetInEdit(false);
  275. }
  276. // Delete the previous form
  277. unpost_form(this->Form);
  278. free_form(this->Form);
  279. this->Form = 0;
  280. }
  281. // Wrong window size
  282. if ( width < cmCursesMainForm::MIN_WIDTH ||
  283. width < this->InitialWidth ||
  284. height < cmCursesMainForm::MIN_HEIGHT )
  285. {
  286. return;
  287. }
  288. // Leave room for toolbar
  289. height -= 7;
  290. if (this->AdvancedMode)
  291. {
  292. this->NumberOfVisibleEntries = this->Entries->size();
  293. }
  294. else
  295. {
  296. // If normal, display only non-advanced entries
  297. this->NumberOfVisibleEntries = 0;
  298. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  299. for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
  300. {
  301. const char* existingValue =
  302. this->CMakeInstance->GetState()
  303. ->GetCacheEntryValue((*it)->GetValue());
  304. bool advanced =
  305. this->CMakeInstance->GetState()
  306. ->GetCacheEntryPropertyAsBool((*it)->GetValue(), "ADVANCED");
  307. if (!existingValue || (!this->AdvancedMode && advanced))
  308. {
  309. continue;
  310. }
  311. this->NumberOfVisibleEntries++;
  312. }
  313. }
  314. // Re-adjust the fields according to their place
  315. this->NumberOfPages = 1;
  316. if (height > 0)
  317. {
  318. bool isNewPage;
  319. int i=0;
  320. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  321. for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
  322. {
  323. const char* existingValue =
  324. this->CMakeInstance->GetState()
  325. ->GetCacheEntryValue((*it)->GetValue());
  326. bool advanced =
  327. this->CMakeInstance->GetState()
  328. ->GetCacheEntryPropertyAsBool((*it)->GetValue(), "ADVANCED");
  329. if (!existingValue || (!this->AdvancedMode && advanced))
  330. {
  331. continue;
  332. }
  333. int row = (i % height) + 1;
  334. int page = (i / height) + 1;
  335. isNewPage = ( page > 1 ) && ( row == 1 );
  336. if (isNewPage)
  337. {
  338. this->NumberOfPages++;
  339. }
  340. (*it)->Label->Move(left, top+row-1, isNewPage);
  341. (*it)->IsNewLabel->Move(left+32, top+row-1, false);
  342. (*it)->Entry->Move(left+33, top+row-1, false);
  343. (*it)->Entry->SetPage(this->NumberOfPages);
  344. i++;
  345. }
  346. }
  347. // Post the form
  348. this->Form = new_form(this->Fields);
  349. post_form(this->Form);
  350. // Update toolbar
  351. this->UpdateStatusBar();
  352. this->PrintKeys();
  353. touchwin(stdscr);
  354. refresh();
  355. }
  356. void cmCursesMainForm::PrintKeys(int process /* = 0 */)
  357. {
  358. int x,y;
  359. getmaxyx(stdscr, y, x);
  360. if ( x < cmCursesMainForm::MIN_WIDTH ||
  361. x < this->InitialWidth ||
  362. y < cmCursesMainForm::MIN_HEIGHT )
  363. {
  364. return;
  365. }
  366. // Give the current widget (if it exists), a chance to print keys
  367. cmCursesWidget* cw = 0;
  368. if (this->Form)
  369. {
  370. FIELD* currentField = current_field(this->Form);
  371. cw = reinterpret_cast<cmCursesWidget*>(field_userptr(currentField));
  372. }
  373. if (cw)
  374. {
  375. cw->PrintKeys();
  376. }
  377. // {
  378. // }
  379. // else
  380. // {
  381. char firstLine[512]="";
  382. char secondLine[512]="";
  383. char thirdLine[512]="";
  384. if (process)
  385. {
  386. const char* clearLine =
  387. " ";
  388. strcpy(firstLine, clearLine);
  389. strcpy(secondLine, clearLine);
  390. strcpy(thirdLine, clearLine);
  391. }
  392. else
  393. {
  394. if (this->OkToGenerate)
  395. {
  396. sprintf(firstLine,
  397. "Press [c] to configure Press [g] to generate and exit");
  398. }
  399. else
  400. {
  401. sprintf(firstLine, "Press [c] to configure ");
  402. }
  403. {
  404. const char* toggleKeyInstruction =
  405. "Press [t] to toggle advanced mode (Currently %s)";
  406. sprintf(thirdLine,
  407. toggleKeyInstruction,
  408. this->AdvancedMode ? "On" : "Off");
  409. }
  410. sprintf(secondLine,
  411. "Press [h] for help "
  412. "Press [q] to quit without generating");
  413. }
  414. curses_move(y-4,0);
  415. char fmt[512] = "Press [enter] to edit option";
  416. if ( process )
  417. {
  418. strcpy(fmt, " ");
  419. }
  420. printw(fmt);
  421. curses_move(y-3,0);
  422. printw(firstLine);
  423. curses_move(y-2,0);
  424. printw(secondLine);
  425. curses_move(y-1,0);
  426. printw(thirdLine);
  427. if (cw)
  428. {
  429. sprintf(firstLine, "Page %d of %d", cw->GetPage(), this->NumberOfPages);
  430. curses_move(0,65-static_cast<unsigned int>(strlen(firstLine))-1);
  431. printw(firstLine);
  432. }
  433. // }
  434. pos_form_cursor(this->Form);
  435. }
  436. // Print the key of the current entry and the CMake version
  437. // on the status bar. Designed for a width of 80 chars.
  438. void cmCursesMainForm::UpdateStatusBar(const char* message)
  439. {
  440. int x,y;
  441. getmaxyx(stdscr, y, x);
  442. // If window size is too small, display error and return
  443. if ( x < cmCursesMainForm::MIN_WIDTH ||
  444. x < this->InitialWidth ||
  445. y < cmCursesMainForm::MIN_HEIGHT )
  446. {
  447. curses_clear();
  448. curses_move(0,0);
  449. char fmt[] = "Window is too small. A size of at least %dx%d is required.";
  450. printw(fmt,
  451. (cmCursesMainForm::MIN_WIDTH < this->InitialWidth ?
  452. this->InitialWidth : cmCursesMainForm::MIN_WIDTH),
  453. cmCursesMainForm::MIN_HEIGHT);
  454. touchwin(stdscr);
  455. wrefresh(stdscr);
  456. return;
  457. }
  458. // Get the key of the current entry
  459. FIELD* cur = current_field(this->Form);
  460. int findex = field_index(cur);
  461. cmCursesWidget* lbl = 0;
  462. if ( findex >= 0 )
  463. {
  464. lbl = reinterpret_cast<cmCursesWidget*>(field_userptr(this->Fields[findex-2]));
  465. }
  466. char help[128] = "";
  467. const char* curField = "";
  468. if ( lbl )
  469. {
  470. curField = lbl->GetValue();
  471. // Get the help string of the current entry
  472. // and add it to the help string
  473. const char* existingValue =
  474. this->CMakeInstance->GetState()->GetCacheEntryValue(curField);
  475. if (existingValue)
  476. {
  477. const char* hs = this->CMakeInstance->GetState()
  478. ->GetCacheEntryProperty(curField, "HELPSTRING");
  479. if ( hs )
  480. {
  481. strncpy(help, hs, 127);
  482. help[127] = '\0';
  483. }
  484. else
  485. {
  486. help[0] = 0;
  487. }
  488. }
  489. else
  490. {
  491. sprintf(help," ");
  492. }
  493. }
  494. // Join the key, help string and pad with spaces
  495. // (or truncate) as necessary
  496. char bar[cmCursesMainForm::MAX_WIDTH];
  497. size_t i, curFieldLen = strlen(curField);
  498. size_t helpLen = strlen(help);
  499. size_t width;
  500. if (x < cmCursesMainForm::MAX_WIDTH )
  501. {
  502. width = x;
  503. }
  504. else
  505. {
  506. width = cmCursesMainForm::MAX_WIDTH;
  507. }
  508. if ( message )
  509. {
  510. curField = message;
  511. curFieldLen = strlen(message);
  512. if ( curFieldLen < width )
  513. {
  514. strcpy(bar, curField);
  515. for(i=curFieldLen; i < width; ++i)
  516. {
  517. bar[i] = ' ';
  518. }
  519. }
  520. else
  521. {
  522. strncpy(bar, curField, width);
  523. }
  524. }
  525. else
  526. {
  527. if (curFieldLen >= width)
  528. {
  529. strncpy(bar, curField, width);
  530. }
  531. else
  532. {
  533. strcpy(bar, curField);
  534. bar[curFieldLen] = ':';
  535. bar[curFieldLen+1] = ' ';
  536. if (curFieldLen + helpLen + 2 >= width)
  537. {
  538. strncpy(bar+curFieldLen+2, help, width
  539. - curFieldLen - 2);
  540. }
  541. else
  542. {
  543. strcpy(bar+curFieldLen+2, help);
  544. for(i=curFieldLen+helpLen+2; i < width; ++i)
  545. {
  546. bar[i] = ' ';
  547. }
  548. }
  549. }
  550. }
  551. bar[width] = '\0';
  552. // Display CMake version info on the next line
  553. // We want to display this on the right
  554. char version[cmCursesMainForm::MAX_WIDTH];
  555. char vertmp[128];
  556. sprintf(vertmp,"CMake Version %s", cmVersion::GetCMakeVersion());
  557. size_t sideSpace = (width-strlen(vertmp));
  558. for(i=0; i<sideSpace; i++) { version[i] = ' '; }
  559. sprintf(version+sideSpace, "%s", vertmp);
  560. version[width] = '\0';
  561. // Now print both lines
  562. curses_move(y-5,0);
  563. attron(A_STANDOUT);
  564. char format[] = "%s";
  565. printw(format, bar);
  566. attroff(A_STANDOUT);
  567. curses_move(y-4,0);
  568. printw(version);
  569. pos_form_cursor(this->Form);
  570. }
  571. void cmCursesMainForm::UpdateProgress(const char *msg, float prog, void* vp)
  572. {
  573. cmCursesMainForm* cm = static_cast<cmCursesMainForm*>(vp);
  574. if ( !cm )
  575. {
  576. return;
  577. }
  578. char tmp[1024];
  579. const char *cmsg = tmp;
  580. if ( prog >= 0 )
  581. {
  582. sprintf(tmp, "%s %i%%",msg,(int)(100*prog));
  583. }
  584. else
  585. {
  586. cmsg = msg;
  587. }
  588. cm->UpdateStatusBar(cmsg);
  589. cm->PrintKeys(1);
  590. curses_move(1,1);
  591. touchwin(stdscr);
  592. refresh();
  593. }
  594. int cmCursesMainForm::Configure(int noconfigure)
  595. {
  596. int xi,yi;
  597. getmaxyx(stdscr, yi, xi);
  598. curses_move(1,1);
  599. this->UpdateStatusBar("Configuring, please wait...");
  600. this->PrintKeys(1);
  601. touchwin(stdscr);
  602. refresh();
  603. this->CMakeInstance->SetProgressCallback(cmCursesMainForm::UpdateProgress, this);
  604. // always save the current gui values to disk
  605. this->FillCacheManagerFromUI();
  606. this->CMakeInstance->SaveCache(
  607. this->CMakeInstance->GetHomeOutputDirectory());
  608. this->LoadCache(0);
  609. // Get rid of previous errors
  610. this->Errors = std::vector<std::string>();
  611. // run the generate process
  612. this->OkToGenerate = true;
  613. int retVal;
  614. if ( noconfigure )
  615. {
  616. retVal = this->CMakeInstance->DoPreConfigureChecks();
  617. this->OkToGenerate = false;
  618. if ( retVal > 0 )
  619. {
  620. retVal = 0;
  621. }
  622. }
  623. else
  624. {
  625. retVal = this->CMakeInstance->Configure();
  626. }
  627. this->CMakeInstance->SetProgressCallback(0, 0);
  628. keypad(stdscr,TRUE); /* Use key symbols as
  629. KEY_DOWN*/
  630. if( retVal != 0 || !this->Errors.empty())
  631. {
  632. // see if there was an error
  633. if(cmSystemTools::GetErrorOccuredFlag())
  634. {
  635. this->OkToGenerate = false;
  636. }
  637. int xx,yy;
  638. getmaxyx(stdscr, yy, xx);
  639. cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(
  640. this->Errors,
  641. cmSystemTools::GetErrorOccuredFlag()
  642. ? "Errors occurred during the last pass." :
  643. "CMake produced the following output.");
  644. // reset error condition
  645. cmSystemTools::ResetErrorOccuredFlag();
  646. CurrentForm = msgs;
  647. msgs->Render(1,1,xx,yy);
  648. msgs->HandleInput();
  649. // If they typed the wrong source directory, we report
  650. // an error and exit
  651. if ( retVal == -2 )
  652. {
  653. return retVal;
  654. }
  655. CurrentForm = this;
  656. this->Render(1,1,xx,yy);
  657. }
  658. this->InitializeUI();
  659. this->Render(1, 1, xi, yi);
  660. return 0;
  661. }
  662. int cmCursesMainForm::Generate()
  663. {
  664. int xi,yi;
  665. getmaxyx(stdscr, yi, xi);
  666. curses_move(1,1);
  667. this->UpdateStatusBar("Generating, please wait...");
  668. this->PrintKeys(1);
  669. touchwin(stdscr);
  670. refresh();
  671. this->CMakeInstance->SetProgressCallback(cmCursesMainForm::UpdateProgress, this);
  672. // Get rid of previous errors
  673. this->Errors = std::vector<std::string>();
  674. // run the generate process
  675. int retVal = this->CMakeInstance->Generate();
  676. this->CMakeInstance->SetProgressCallback(0, 0);
  677. keypad(stdscr,TRUE); /* Use key symbols as
  678. KEY_DOWN*/
  679. if( retVal != 0 || !this->Errors.empty())
  680. {
  681. // see if there was an error
  682. if(cmSystemTools::GetErrorOccuredFlag())
  683. {
  684. this->OkToGenerate = false;
  685. }
  686. // reset error condition
  687. cmSystemTools::ResetErrorOccuredFlag();
  688. int xx,yy;
  689. getmaxyx(stdscr, yy, xx);
  690. const char* title = "Messages during last pass.";
  691. if(cmSystemTools::GetErrorOccuredFlag())
  692. {
  693. title = "Errors occurred during the last pass.";
  694. }
  695. cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(this->Errors,
  696. title);
  697. CurrentForm = msgs;
  698. msgs->Render(1,1,xx,yy);
  699. msgs->HandleInput();
  700. // If they typed the wrong source directory, we report
  701. // an error and exit
  702. if ( retVal == -2 )
  703. {
  704. return retVal;
  705. }
  706. CurrentForm = this;
  707. this->Render(1,1,xx,yy);
  708. }
  709. this->InitializeUI();
  710. this->Render(1, 1, xi, yi);
  711. return 0;
  712. }
  713. void cmCursesMainForm::AddError(const char* message, const char*)
  714. {
  715. this->Errors.push_back(message);
  716. }
  717. void cmCursesMainForm::RemoveEntry(const char* value)
  718. {
  719. if (!value)
  720. {
  721. return;
  722. }
  723. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  724. for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
  725. {
  726. const char* val = (*it)->GetValue();
  727. if ( val && !strcmp(value, val) )
  728. {
  729. this->CMakeInstance->UnwatchUnusedCli(value);
  730. this->Entries->erase(it);
  731. break;
  732. }
  733. }
  734. }
  735. // copy from the list box to the cache manager
  736. void cmCursesMainForm::FillCacheManagerFromUI()
  737. {
  738. size_t size = this->Entries->size();
  739. for(size_t i=0; i < size; i++)
  740. {
  741. std::string cacheKey = (*this->Entries)[i]->Key;
  742. const char* existingValue = this->CMakeInstance->GetState()
  743. ->GetCacheEntryValue(cacheKey);
  744. if (existingValue)
  745. {
  746. std::string oldValue = existingValue;
  747. std::string newValue = (*this->Entries)[i]->Entry->GetValue();
  748. std::string fixedOldValue;
  749. std::string fixedNewValue;
  750. cmState::CacheEntryType t =
  751. this->CMakeInstance->GetState()
  752. ->GetCacheEntryType(cacheKey);
  753. this->FixValue(t, oldValue, fixedOldValue);
  754. this->FixValue(t, newValue, fixedNewValue);
  755. if(!(fixedOldValue == fixedNewValue))
  756. {
  757. // The user has changed the value. Mark it as modified.
  758. this->CMakeInstance->GetState()
  759. ->SetCacheEntryBoolProperty(cacheKey, "MODIFIED", true);
  760. this->CMakeInstance->GetState()
  761. ->SetCacheEntryValue(cacheKey, fixedNewValue);
  762. }
  763. }
  764. }
  765. }
  766. void cmCursesMainForm::FixValue(cmState::CacheEntryType type,
  767. const std::string& in, std::string& out) const
  768. {
  769. out = in.substr(0,in.find_last_not_of(" ")+1);
  770. if(type == cmState::PATH || type == cmState::FILEPATH)
  771. {
  772. cmSystemTools::ConvertToUnixSlashes(out);
  773. }
  774. if(type == cmState::BOOL)
  775. {
  776. if(cmSystemTools::IsOff(out.c_str()))
  777. {
  778. out = "OFF";
  779. }
  780. else
  781. {
  782. out = "ON";
  783. }
  784. }
  785. }
  786. #include <unistd.h>
  787. void cmCursesMainForm::HandleInput()
  788. {
  789. int x=0,y=0;
  790. if (!this->Form)
  791. {
  792. return;
  793. }
  794. FIELD* currentField;
  795. cmCursesWidget* currentWidget;
  796. char debugMessage[128];
  797. for(;;)
  798. {
  799. this->UpdateStatusBar();
  800. this->PrintKeys();
  801. if ( this->SearchMode )
  802. {
  803. std::string searchstr = "Search: " + this->SearchString;
  804. this->UpdateStatusBar( searchstr.c_str() );
  805. this->PrintKeys(1);
  806. curses_move(y-5,static_cast<unsigned int>(searchstr.size()));
  807. //curses_move(1,1);
  808. touchwin(stdscr);
  809. refresh();
  810. }
  811. int key = getch();
  812. getmaxyx(stdscr, y, x);
  813. // If window too small, handle 'q' only
  814. if ( x < cmCursesMainForm::MIN_WIDTH ||
  815. y < cmCursesMainForm::MIN_HEIGHT )
  816. {
  817. // quit
  818. if ( key == 'q' )
  819. {
  820. break;
  821. }
  822. else
  823. {
  824. continue;
  825. }
  826. }
  827. currentField = current_field(this->Form);
  828. currentWidget = reinterpret_cast<cmCursesWidget*>(field_userptr(
  829. currentField));
  830. bool widgetHandled=false;
  831. if ( this->SearchMode )
  832. {
  833. if ( key == 10 || key == KEY_ENTER )
  834. {
  835. this->SearchMode = false;
  836. if (!this->SearchString.empty())
  837. {
  838. this->JumpToCacheEntry(this->SearchString.c_str());
  839. this->OldSearchString = this->SearchString;
  840. }
  841. this->SearchString = "";
  842. }
  843. /*
  844. else if ( key == KEY_ESCAPE )
  845. {
  846. this->SearchMode = false;
  847. }
  848. */
  849. else if ((key >= 'a' && key <= 'z') ||
  850. (key >= 'A' && key <= 'Z') ||
  851. (key >= '0' && key <= '9') ||
  852. (key == '_' ))
  853. {
  854. if ( this->SearchString.size() < static_cast<std::string::size_type>(x-10) )
  855. {
  856. this->SearchString += static_cast<char>(key);
  857. }
  858. }
  859. else if ( key == ctrl('h') || key == KEY_BACKSPACE || key == KEY_DC )
  860. {
  861. if (!this->SearchString.empty())
  862. {
  863. this->SearchString.resize(this->SearchString.size()-1);
  864. }
  865. }
  866. }
  867. else if (currentWidget && !this->SearchMode)
  868. {
  869. // Ask the current widget if it wants to handle input
  870. widgetHandled = currentWidget->HandleInput(key, this, stdscr);
  871. if (widgetHandled)
  872. {
  873. this->OkToGenerate = false;
  874. this->UpdateStatusBar();
  875. this->PrintKeys();
  876. }
  877. }
  878. if ((!currentWidget || !widgetHandled) && !this->SearchMode)
  879. {
  880. // If the current widget does not want to handle input,
  881. // we handle it.
  882. sprintf(debugMessage, "Main form handling input, key: %d", key);
  883. cmCursesForm::LogMessage(debugMessage);
  884. // quit
  885. if ( key == 'q' )
  886. {
  887. break;
  888. }
  889. // if not end of page, next field otherwise next page
  890. // each entry consists of fields: label, isnew, value
  891. // therefore, the label field for the prev. entry is index-5
  892. // and the label field for the next entry is index+1
  893. // (index always corresponds to the value field)
  894. else if ( key == KEY_DOWN || key == ctrl('n') )
  895. {
  896. FIELD* cur = current_field(this->Form);
  897. size_t findex = field_index(cur);
  898. if ( findex == 3*this->NumberOfVisibleEntries-1 )
  899. {
  900. continue;
  901. }
  902. if (new_page(this->Fields[findex+1]))
  903. {
  904. form_driver(this->Form, REQ_NEXT_PAGE);
  905. }
  906. else
  907. {
  908. form_driver(this->Form, REQ_NEXT_FIELD);
  909. }
  910. }
  911. // if not beginning of page, previous field, otherwise previous page
  912. // each entry consists of fields: label, isnew, value
  913. // therefore, the label field for the prev. entry is index-5
  914. // and the label field for the next entry is index+1
  915. // (index always corresponds to the value field)
  916. else if ( key == KEY_UP || key == ctrl('p') )
  917. {
  918. FIELD* cur = current_field(this->Form);
  919. int findex = field_index(cur);
  920. if ( findex == 2 )
  921. {
  922. continue;
  923. }
  924. if ( new_page(this->Fields[findex-2]) )
  925. {
  926. form_driver(this->Form, REQ_PREV_PAGE);
  927. set_current_field(this->Form, this->Fields[findex-3]);
  928. }
  929. else
  930. {
  931. form_driver(this->Form, REQ_PREV_FIELD);
  932. }
  933. }
  934. // pg down
  935. else if ( key == KEY_NPAGE || key == ctrl('d') )
  936. {
  937. form_driver(this->Form, REQ_NEXT_PAGE);
  938. }
  939. // pg up
  940. else if ( key == KEY_PPAGE || key == ctrl('u') )
  941. {
  942. form_driver(this->Form, REQ_PREV_PAGE);
  943. }
  944. // configure
  945. else if ( key == 'c' )
  946. {
  947. this->Configure();
  948. }
  949. // display help
  950. else if ( key == 'h' )
  951. {
  952. getmaxyx(stdscr, y, x);
  953. FIELD* cur = current_field(this->Form);
  954. int findex = field_index(cur);
  955. cmCursesWidget* lbl = reinterpret_cast<cmCursesWidget*>(field_userptr(
  956. this->Fields[findex-2]));
  957. const char* curField = lbl->GetValue();
  958. const char* helpString = 0;
  959. const char* existingValue =
  960. this->CMakeInstance->GetState()
  961. ->GetCacheEntryValue(curField);
  962. if (existingValue)
  963. {
  964. helpString = this->CMakeInstance->GetState()
  965. ->GetCacheEntryProperty(curField, "HELPSTRING");
  966. }
  967. if (helpString)
  968. {
  969. char* message = new char[strlen(curField)+strlen(helpString)
  970. +strlen("Current option is: \n Help string for this option is: \n")+10];
  971. sprintf(message,"Current option is: %s\nHelp string for this option is: %s\n", curField, helpString);
  972. this->HelpMessage[1] = message;
  973. delete[] message;
  974. }
  975. else
  976. {
  977. this->HelpMessage[1] = "";
  978. }
  979. cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(this->HelpMessage,
  980. "Help.");
  981. CurrentForm = msgs;
  982. msgs->Render(1,1,x,y);
  983. msgs->HandleInput();
  984. CurrentForm = this;
  985. this->Render(1,1,x,y);
  986. set_current_field(this->Form, cur);
  987. }
  988. // display last errors
  989. else if ( key == 'l' )
  990. {
  991. getmaxyx(stdscr, y, x);
  992. cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(this->Errors,
  993. "Errors occurred during the last pass.");
  994. CurrentForm = msgs;
  995. msgs->Render(1,1,x,y);
  996. msgs->HandleInput();
  997. CurrentForm = this;
  998. this->Render(1,1,x,y);
  999. }
  1000. else if ( key == '/' )
  1001. {
  1002. this->SearchMode = true;
  1003. this->UpdateStatusBar("Search");
  1004. this->PrintKeys(1);
  1005. touchwin(stdscr);
  1006. refresh();
  1007. }
  1008. else if ( key == 'n' )
  1009. {
  1010. if (!this->OldSearchString.empty())
  1011. {
  1012. this->JumpToCacheEntry(this->OldSearchString.c_str());
  1013. }
  1014. }
  1015. // switch advanced on/off
  1016. else if ( key == 't' )
  1017. {
  1018. if (this->AdvancedMode)
  1019. {
  1020. this->AdvancedMode = false;
  1021. }
  1022. else
  1023. {
  1024. this->AdvancedMode = true;
  1025. }
  1026. getmaxyx(stdscr, y, x);
  1027. this->RePost();
  1028. this->Render(1, 1, x, y);
  1029. }
  1030. // generate and exit
  1031. else if ( key == 'g' )
  1032. {
  1033. if ( this->OkToGenerate )
  1034. {
  1035. this->Generate();
  1036. break;
  1037. }
  1038. }
  1039. // delete cache entry
  1040. else if ( key == 'd' && this->NumberOfVisibleEntries )
  1041. {
  1042. this->OkToGenerate = false;
  1043. FIELD* cur = current_field(this->Form);
  1044. size_t findex = field_index(cur);
  1045. // make the next or prev. current field after deletion
  1046. // each entry consists of fields: label, isnew, value
  1047. // therefore, the label field for the prev. entry is findex-5
  1048. // and the label field for the next entry is findex+1
  1049. // (findex always corresponds to the value field)
  1050. FIELD* nextCur;
  1051. if ( findex == 2 )
  1052. {
  1053. nextCur=0;
  1054. }
  1055. else if ( findex == 3*this->NumberOfVisibleEntries-1 )
  1056. {
  1057. nextCur = this->Fields[findex-5];
  1058. }
  1059. else
  1060. {
  1061. nextCur = this->Fields[findex+1];
  1062. }
  1063. // Get the label widget
  1064. // each entry consists of fields: label, isnew, value
  1065. // therefore, the label field for the is findex-2
  1066. // (findex always corresponds to the value field)
  1067. cmCursesWidget* lbl
  1068. = reinterpret_cast<cmCursesWidget*>(
  1069. field_userptr(this->Fields[findex-2]));
  1070. if ( lbl )
  1071. {
  1072. this->CMakeInstance->GetState()->RemoveCacheEntry(lbl->GetValue());
  1073. std::string nextVal;
  1074. if (nextCur)
  1075. {
  1076. nextVal = (reinterpret_cast<cmCursesWidget*>(field_userptr(nextCur))->GetValue());
  1077. }
  1078. getmaxyx(stdscr, y, x);
  1079. this->RemoveEntry(lbl->GetValue());
  1080. this->RePost();
  1081. this->Render(1, 1, x, y);
  1082. if (nextCur)
  1083. {
  1084. // make the next or prev. current field after deletion
  1085. nextCur = 0;
  1086. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  1087. for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
  1088. {
  1089. if (nextVal == (*it)->Key)
  1090. {
  1091. nextCur = (*it)->Entry->Field;
  1092. }
  1093. }
  1094. if (nextCur)
  1095. {
  1096. set_current_field(this->Form, nextCur);
  1097. }
  1098. }
  1099. }
  1100. }
  1101. }
  1102. touchwin(stdscr);
  1103. wrefresh(stdscr);
  1104. }
  1105. }
  1106. int cmCursesMainForm::LoadCache(const char *)
  1107. {
  1108. int r = this->CMakeInstance->LoadCache();
  1109. if(r < 0)
  1110. {
  1111. return r;
  1112. }
  1113. this->CMakeInstance->SetCacheArgs(this->Args);
  1114. this->CMakeInstance->PreLoadCMakeFiles();
  1115. return r;
  1116. }
  1117. void cmCursesMainForm::JumpToCacheEntry(const char* astr)
  1118. {
  1119. std::string str;
  1120. if ( astr )
  1121. {
  1122. str = cmSystemTools::LowerCase(astr);
  1123. }
  1124. if(str.empty())
  1125. {
  1126. return;
  1127. }
  1128. FIELD* cur = current_field(this->Form);
  1129. int start_index = field_index(cur);
  1130. int findex = start_index;
  1131. for(;;)
  1132. {
  1133. if (!str.empty())
  1134. {
  1135. cmCursesWidget* lbl = 0;
  1136. if ( findex >= 0 )
  1137. {
  1138. lbl = reinterpret_cast<cmCursesWidget*>(field_userptr(this->Fields[findex-2]));
  1139. }
  1140. if ( lbl )
  1141. {
  1142. const char* curField = lbl->GetValue();
  1143. if ( curField )
  1144. {
  1145. std::string cfld = cmSystemTools::LowerCase(curField);
  1146. if ( cfld.find(str) != cfld.npos && findex != start_index )
  1147. {
  1148. break;
  1149. }
  1150. }
  1151. }
  1152. }
  1153. if ( size_t(findex) >= 3* this->NumberOfVisibleEntries-1 )
  1154. {
  1155. set_current_field(this->Form, this->Fields[2]);
  1156. }
  1157. else if (new_page(this->Fields[findex+1]))
  1158. {
  1159. form_driver(this->Form, REQ_NEXT_PAGE);
  1160. }
  1161. else
  1162. {
  1163. form_driver(this->Form, REQ_NEXT_FIELD);
  1164. }
  1165. /*
  1166. char buffer[1024];
  1167. sprintf(buffer, "Line: %d != %d / %d\n", findex, idx, this->NumberOfVisibleEntries);
  1168. touchwin(stdscr);
  1169. refresh();
  1170. this->UpdateStatusBar( buffer );
  1171. usleep(100000);
  1172. */
  1173. cur = current_field(this->Form);
  1174. findex = field_index(cur);
  1175. if ( findex == start_index )
  1176. {
  1177. break;
  1178. }
  1179. }
  1180. }
  1181. const char* cmCursesMainForm::s_ConstHelpMessage =
  1182. "CMake is used to configure and generate build files for software projects. "
  1183. "The basic steps for configuring a project with ccmake are as follows:\n\n"
  1184. "1. Run ccmake in the directory where you want the object and executable files to be placed (build directory). If the source directory is not the same as this build directory, you have to specify it as an argument on the command line.\n\n"
  1185. "2. When ccmake is run, it will read the configuration files and display the current build options. "
  1186. "If you have run CMake before and have updated the configuration files since then, any new entries will be displayed on top and will be marked with a *. "
  1187. "On the other hand, the first time you run ccmake, all build options will be new and will be marked as such. "
  1188. "At this point, you can modify any options (see keys below) you want to change. "
  1189. "When you are satisfied with your changes, press 'c' to have CMake process the configuration files. "
  1190. "Please note that changing some options may cause new ones to appear. These will be shown on top and will be marked with *. "
  1191. "Repeat this procedure until you are satisfied with all the options and there are no new entries. "
  1192. "At this point, a new command will appear: G)enerate and Exit. You can now hit 'g' to have CMake generate all the build files (i.e. makefiles or project files) and exit. "
  1193. "At any point during the process, you can exit ccmake with 'q'. However, this will not generate/change any build files.\n\n"
  1194. "ccmake KEYS:\n\n"
  1195. "Navigation: "
  1196. "You can use the arrow keys and page up, down to navigate the options. Alternatively, you can use the following keys: \n"
  1197. " C-n : next option\n"
  1198. " C-p : previous options\n"
  1199. " C-d : down one page\n"
  1200. " C-u : up one page\n\n"
  1201. "Editing options: "
  1202. "To change an option press enter or return. If the current options is a boolean, this will toggle it's value. "
  1203. "Otherwise, ccmake will enter edit mode. In this mode you can edit an option using arrow keys and backspace. Alternatively, you can use the following keys:\n"
  1204. " C-b : back one character\n"
  1205. " C-f : forward one character\n"
  1206. " C-a : go to the beginning of the field\n"
  1207. " C-e : go to the end of the field\n"
  1208. " C-d : delete previous character\n"
  1209. " C-k : kill the rest of the field\n"
  1210. " Esc : Restore field (discard last changes)\n"
  1211. " Enter : Leave edit mode\n"
  1212. "You can also delete an option by pressing 'd'\n\n"
  1213. "Commands:\n"
  1214. " q : quit ccmake without generating build files\n"
  1215. " h : help, shows this screen\n"
  1216. " c : process the configuration files with the current options\n"
  1217. " g : generate build files and exit, only available when there are no "
  1218. "new options and no errors have been detected during last configuration.\n"
  1219. " l : shows last errors\n"
  1220. " t : toggles advanced mode. In normal mode, only the most important options are shown. In advanced mode, all options are shown. We recommend using normal mode unless you are an expert.\n"
  1221. " / : search for a variable name.\n";