cmCursesMainForm.cxx 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808
  1. #include "../cmCacheManager.h"
  2. #include "../cmSystemTools.h"
  3. #include "../cmake.h"
  4. #include "cmCursesMainForm.h"
  5. #include "cmCursesStringWidget.h"
  6. #include "cmCursesLabelWidget.h"
  7. #include "cmCursesBoolWidget.h"
  8. #include "cmCursesPathWidget.h"
  9. #include "cmCursesFilePathWidget.h"
  10. #include "cmCursesDummyWidget.h"
  11. #include "cmCursesCacheEntryComposite.h"
  12. #include "cmCursesLongMessageForm.h"
  13. inline int ctrl(int z)
  14. {
  15. return (z&037);
  16. }
  17. cmCursesMainForm::cmCursesMainForm(std::vector<std::string> const& args) :
  18. m_Args(args)
  19. {
  20. m_Fields = 0;
  21. m_Height = 0;
  22. m_Entries = 0;
  23. m_AdvancedMode = false;
  24. m_NumberOfVisibleEntries = 0;
  25. m_OkToGenerate = false;
  26. m_HelpMessage.push_back("Welcome to ccmake, curses based user interface for CMake.");
  27. m_HelpMessage.push_back("");
  28. m_HelpMessage.push_back(s_ConstHelpMessage);
  29. }
  30. cmCursesMainForm::~cmCursesMainForm()
  31. {
  32. if (m_Form)
  33. {
  34. unpost_form(m_Form);
  35. free_form(m_Form);
  36. m_Form = 0;
  37. }
  38. delete[] m_Fields;
  39. // Clean-up composites
  40. if (m_Entries)
  41. {
  42. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  43. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  44. {
  45. delete *it;
  46. }
  47. }
  48. delete m_Entries;
  49. }
  50. bool cmCursesMainForm::LookForCacheEntry(const char* key)
  51. {
  52. if (!key || !m_Entries)
  53. {
  54. return false;
  55. }
  56. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  57. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  58. {
  59. if (!strcmp(key, (*it)->m_Key.c_str()))
  60. {
  61. return true;
  62. }
  63. }
  64. return false;
  65. }
  66. void cmCursesMainForm::InitializeUI()
  67. {
  68. // Get the cache entries.
  69. const cmCacheManager::CacheEntryMap &cache =
  70. cmCacheManager::GetInstance()->GetCacheMap();
  71. std::vector<cmCursesCacheEntryComposite*>* newEntries =
  72. new std::vector<cmCursesCacheEntryComposite*>;
  73. newEntries->reserve(cache.size());
  74. // Count non-internal and non-static entries
  75. int count=0;
  76. for(cmCacheManager::CacheEntryMap::const_iterator i = cache.begin();
  77. i != cache.end(); ++i)
  78. {
  79. const cmCacheManager::CacheEntry& value = i->second;
  80. if ( value.m_Type != cmCacheManager::INTERNAL &&
  81. value.m_Type != cmCacheManager::STATIC )
  82. {
  83. ++count;
  84. }
  85. }
  86. cmCursesCacheEntryComposite* comp;
  87. if ( count == 0 )
  88. {
  89. // If cache is empty, display a label saying so and a
  90. // dummy entry widget (does not respond to input)
  91. comp = new cmCursesCacheEntryComposite("EMPTY CACHE");
  92. comp->m_Entry = new cmCursesDummyWidget(1, 1, 1, 1);
  93. newEntries->push_back(comp);
  94. }
  95. else
  96. {
  97. // Create the composites.
  98. // First add entries which are new
  99. for(cmCacheManager::CacheEntryMap::const_iterator i = cache.begin();
  100. i != cache.end(); ++i)
  101. {
  102. const char* key = i->first.c_str();
  103. const cmCacheManager::CacheEntry& value = i->second;
  104. if ( value.m_Type == cmCacheManager::INTERNAL ||
  105. value.m_Type == cmCacheManager::STATIC )
  106. {
  107. continue;
  108. }
  109. if (!this->LookForCacheEntry(key))
  110. {
  111. newEntries->push_back(new cmCursesCacheEntryComposite(key, value,
  112. true));
  113. m_OkToGenerate = false;
  114. }
  115. }
  116. // then add entries which are old
  117. for(cmCacheManager::CacheEntryMap::const_iterator i = cache.begin();
  118. i != cache.end(); ++i)
  119. {
  120. const char* key = i->first.c_str();
  121. const cmCacheManager::CacheEntry& value = i->second;
  122. if ( value.m_Type == cmCacheManager::INTERNAL ||
  123. value.m_Type == cmCacheManager::STATIC )
  124. {
  125. continue;
  126. }
  127. if (this->LookForCacheEntry(key))
  128. {
  129. newEntries->push_back(new cmCursesCacheEntryComposite(key, value,
  130. false));
  131. }
  132. }
  133. }
  134. if (m_Entries)
  135. {
  136. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  137. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  138. {
  139. delete *it;
  140. }
  141. }
  142. delete m_Entries;
  143. m_Entries = newEntries;
  144. this->RePost();
  145. }
  146. void cmCursesMainForm::RePost()
  147. {
  148. // Create the fields to be passed to the form.
  149. if (m_Form)
  150. {
  151. unpost_form(m_Form);
  152. free_form(m_Form);
  153. m_Form = 0;
  154. }
  155. delete[] m_Fields;
  156. if (m_AdvancedMode)
  157. {
  158. m_NumberOfVisibleEntries = m_Entries->size();
  159. }
  160. else
  161. {
  162. m_NumberOfVisibleEntries = 0;
  163. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  164. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  165. {
  166. if (!m_AdvancedMode && cmCacheManager::GetInstance()->IsAdvanced(
  167. (*it)->GetValue()))
  168. {
  169. continue;
  170. }
  171. m_NumberOfVisibleEntries++;
  172. }
  173. }
  174. m_Fields = new FIELD*[3*m_NumberOfVisibleEntries+1];
  175. int j=0;
  176. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  177. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  178. {
  179. if (!m_AdvancedMode && cmCacheManager::GetInstance()->IsAdvanced(
  180. (*it)->GetValue()))
  181. {
  182. continue;
  183. }
  184. m_Fields[3*j] = (*it)->m_Label->m_Field;
  185. m_Fields[3*j+1] = (*it)->m_IsNewLabel->m_Field;
  186. m_Fields[3*j+2] = (*it)->m_Entry->m_Field;
  187. j++;
  188. }
  189. // Has to be null terminated.
  190. m_Fields[3*m_NumberOfVisibleEntries] = 0;
  191. }
  192. void cmCursesMainForm::Render(int left, int top, int width, int height)
  193. {
  194. if (m_Form)
  195. {
  196. FIELD* currentField = current_field(m_Form);
  197. cmCursesWidget* cw = reinterpret_cast<cmCursesWidget*>
  198. (field_userptr(currentField));
  199. if ( cw->GetType() == cmCacheManager::STRING ||
  200. cw->GetType() == cmCacheManager::PATH ||
  201. cw->GetType() == cmCacheManager::FILEPATH )
  202. {
  203. cmCursesStringWidget* sw = static_cast<cmCursesStringWidget*>(cw);
  204. sw->SetInEdit(false);
  205. }
  206. unpost_form(m_Form);
  207. free_form(m_Form);
  208. m_Form = 0;
  209. }
  210. if ( width < cmCursesMainForm::MIN_WIDTH ||
  211. height < cmCursesMainForm::MIN_HEIGHT )
  212. {
  213. return;
  214. }
  215. height -= 6;
  216. m_Height = height;
  217. if (m_AdvancedMode)
  218. {
  219. m_NumberOfVisibleEntries = m_Entries->size();
  220. }
  221. else
  222. {
  223. m_NumberOfVisibleEntries = 0;
  224. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  225. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  226. {
  227. if (!m_AdvancedMode && cmCacheManager::GetInstance()->IsAdvanced(
  228. (*it)->GetValue()))
  229. {
  230. continue;
  231. }
  232. m_NumberOfVisibleEntries++;
  233. }
  234. }
  235. bool isNewPage;
  236. int i=0;
  237. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  238. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  239. {
  240. if (!m_AdvancedMode && cmCacheManager::GetInstance()->IsAdvanced(
  241. (*it)->GetValue()))
  242. {
  243. continue;
  244. }
  245. int row = (i % height) + 1;
  246. int page = (i / height) + 1;
  247. isNewPage = ( page > 1 ) && ( row == 1 );
  248. (*it)->m_Label->Move(left, top+row-1, isNewPage);
  249. (*it)->m_IsNewLabel->Move(left+32, top+row-1, false);
  250. (*it)->m_Entry->Move(left+33, top+row-1, false);
  251. i++;
  252. }
  253. m_Form = new_form(m_Fields);
  254. post_form(m_Form);
  255. this->UpdateStatusBar();
  256. this->PrintKeys();
  257. touchwin(stdscr);
  258. refresh();
  259. }
  260. void cmCursesMainForm::PrintKeys()
  261. {
  262. int x,y;
  263. getmaxyx(stdscr, y, x);
  264. if ( x < cmCursesMainForm::MIN_WIDTH ||
  265. y < cmCursesMainForm::MIN_HEIGHT )
  266. {
  267. return;
  268. }
  269. char firstLine[512], secondLine[512];
  270. if (m_OkToGenerate)
  271. {
  272. sprintf(firstLine, "C)onfigure G)enerate and Exit H)elp");
  273. }
  274. else
  275. {
  276. sprintf(firstLine, "C)onfigure H)elp");
  277. }
  278. if (m_AdvancedMode)
  279. {
  280. sprintf(secondLine, "Q)uit Without Generating T)oggle Advanced Mode (On)");
  281. }
  282. else
  283. {
  284. sprintf(secondLine, "Q)uit Without Generating T)oggle Advanced Mode (Off)");
  285. }
  286. curses_move(y-2,0);
  287. printw(firstLine);
  288. curses_move(y-1,0);
  289. printw(secondLine);
  290. pos_form_cursor(m_Form);
  291. }
  292. // Print the key of the current entry and the CMake version
  293. // on the status bar. Designed for a width of 80 chars.
  294. void cmCursesMainForm::UpdateStatusBar()
  295. {
  296. int x,y;
  297. getmaxyx(stdscr, y, x);
  298. if ( x < cmCursesMainForm::MIN_WIDTH ||
  299. y < cmCursesMainForm::MIN_HEIGHT )
  300. {
  301. curses_clear();
  302. curses_move(0,0);
  303. printw("Window is too small. A size of at least %dx%d is required.",
  304. cmCursesMainForm::MIN_WIDTH, cmCursesMainForm::MIN_HEIGHT);
  305. touchwin(stdscr);
  306. wrefresh(stdscr);
  307. return;
  308. }
  309. FIELD* cur = current_field(m_Form);
  310. int index = field_index(cur);
  311. cmCursesWidget* lbl = reinterpret_cast<cmCursesWidget*>(field_userptr(
  312. m_Fields[index-2]));
  313. const char* curField = lbl->GetValue();
  314. // We want to display this on the right
  315. char help[128];
  316. const char* helpString;
  317. cmCacheManager::CacheEntry *entry =
  318. cmCacheManager::GetInstance()->GetCacheEntry(curField);
  319. if (entry)
  320. {
  321. helpString = entry->m_HelpString.c_str();
  322. if (strlen(helpString) > 127)
  323. {
  324. sprintf(help,"%127s", helpString);
  325. }
  326. else
  327. {
  328. sprintf(help,"%s", helpString);
  329. }
  330. }
  331. else
  332. {
  333. sprintf(help," ");
  334. }
  335. char bar[cmCursesMainForm::MAX_WIDTH];
  336. int i, curFieldLen = strlen(curField);
  337. int helpLen = strlen(help);
  338. int width;
  339. if (x < cmCursesMainForm::MAX_WIDTH )
  340. {
  341. width = x;
  342. }
  343. else
  344. {
  345. width = cmCursesMainForm::MAX_WIDTH;
  346. }
  347. if (curFieldLen >= width)
  348. {
  349. strncpy(bar, curField, width);
  350. }
  351. else
  352. {
  353. strcpy(bar, curField);
  354. bar[curFieldLen] = ':';
  355. bar[curFieldLen+1] = ' ';
  356. if (curFieldLen + helpLen + 2 >= width)
  357. {
  358. strncpy(bar+curFieldLen+2, help, width
  359. - curFieldLen - 2);
  360. }
  361. else
  362. {
  363. strcpy(bar+curFieldLen+2, help);
  364. for(i=curFieldLen+helpLen+2; i < width; ++i)
  365. {
  366. bar[i] = ' ';
  367. }
  368. }
  369. }
  370. bar[width] = '\0';
  371. char version[cmCursesMainForm::MAX_WIDTH];
  372. char vertmp[128];
  373. sprintf(vertmp,"CMake Version %d.%d", cmMakefile::GetMajorVersion(),
  374. cmMakefile::GetMinorVersion());
  375. int sideSpace = (width-strlen(vertmp));
  376. for(i=0; i<sideSpace; i++) { version[i] = ' '; }
  377. sprintf(version+sideSpace, "%s", vertmp);
  378. version[width] = '\0';
  379. curses_move(y-4,0);
  380. attron(A_STANDOUT);
  381. printw(bar);
  382. attroff(A_STANDOUT);
  383. curses_move(y-3,0);
  384. printw(version);
  385. pos_form_cursor(m_Form);
  386. }
  387. void cmCursesMainForm::RunCMake(bool generateMakefiles)
  388. {
  389. int x,y;
  390. getmaxyx(stdscr, y, x);
  391. curses_clear();
  392. curses_move(1,1);
  393. printw("Running CMake, please wait.");
  394. touchwin(stdscr);
  395. refresh();
  396. endwin();
  397. std::cerr << "Running CMake, please wait...\n\r";
  398. // always save the current gui values to disk
  399. this->FillCacheManagerFromUI();
  400. cmCacheManager::GetInstance()->SaveCache(cmSystemTools::GetCurrentWorkingDirectory().c_str());
  401. // create a cmake object
  402. cmake make;
  403. // create the arguments for the cmake object
  404. std::string whereCMake = cmSystemTools::GetProgramPath(m_Args[0].c_str());
  405. whereCMake += "/cmake";
  406. m_Args[0] = whereCMake;
  407. // Get rid of previous errors
  408. m_Errors = std::vector<std::string>();
  409. // run the generate process
  410. m_OkToGenerate = true;
  411. if(make.Generate(m_Args, generateMakefiles) != 0 || !m_Errors.empty())
  412. {
  413. m_OkToGenerate = false;
  414. cmSystemTools::ResetErrorOccuredFlag();
  415. int x,y;
  416. getmaxyx(stdscr, y, x);
  417. cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(m_Errors,
  418. "Errors which during last pass.");
  419. CurrentForm = msgs;
  420. msgs->Render(1,1,x,y);
  421. msgs->HandleInput();
  422. CurrentForm = this;
  423. this->Render(1,1,x,y);
  424. }
  425. initscr(); /* Initialization */
  426. noecho(); /* Echo off */
  427. cbreak(); /* nl- or cr not needed */
  428. keypad(stdscr,TRUE); /* Use key symbols as
  429. KEY_DOWN*/
  430. this->InitializeUI();
  431. this->Render(1, 1, x, y);
  432. }
  433. void cmCursesMainForm::AddError(const char* message, const char* title)
  434. {
  435. m_Errors.push_back(message);
  436. }
  437. void cmCursesMainForm::RemoveEntry(const char* value)
  438. {
  439. if (!value)
  440. {
  441. return;
  442. }
  443. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  444. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  445. {
  446. const char* val = (*it)->GetValue();
  447. if ( val && !strcmp(value, val) )
  448. {
  449. m_Entries->erase(it);
  450. break;
  451. }
  452. }
  453. }
  454. // copy from the list box to the cache manager
  455. void cmCursesMainForm::FillCacheManagerFromUI()
  456. {
  457. std::string tmpString;
  458. cmCacheManager::GetInstance()->GetCacheMap();
  459. int size = m_Entries->size();
  460. for(int i=0; i < size; i++)
  461. {
  462. cmCacheManager::CacheEntry *entry =
  463. cmCacheManager::GetInstance()->GetCacheEntry(
  464. (*m_Entries)[i]->m_Key.c_str());
  465. if (entry)
  466. {
  467. tmpString = (*m_Entries)[i]->m_Entry->GetValue();
  468. // Remove trailing spaces
  469. entry->m_Value = tmpString.substr(0,tmpString.find_last_not_of(" ")+1);
  470. }
  471. }
  472. }
  473. void cmCursesMainForm::HandleInput()
  474. {
  475. if (!m_Form)
  476. {
  477. return;
  478. }
  479. FIELD* currentField;
  480. cmCursesWidget* currentWidget;
  481. while(1)
  482. {
  483. this->UpdateStatusBar();
  484. this->PrintKeys();
  485. int key = getch();
  486. currentField = current_field(m_Form);
  487. currentWidget = reinterpret_cast<cmCursesWidget*>(field_userptr(
  488. currentField));
  489. if (!currentWidget || !currentWidget->HandleInput(key, m_Form, stdscr))
  490. {
  491. // quit
  492. if ( key == 'q' )
  493. {
  494. break;
  495. }
  496. // if not end of page, next field otherwise next page
  497. // each entry consists of fields: label, isnew, value
  498. // therefore, the label field for the prev. entry is index-5
  499. // and the label field for the next entry is index+1
  500. // (index always corresponds to the value field)
  501. else if ( key == KEY_DOWN || key == ctrl('n') )
  502. {
  503. FIELD* cur = current_field(m_Form);
  504. int index = field_index(cur);
  505. if ( index == 3*m_NumberOfVisibleEntries-1 )
  506. {
  507. continue;
  508. }
  509. if (new_page(m_Fields[index+1]))
  510. {
  511. form_driver(m_Form, REQ_NEXT_PAGE);
  512. }
  513. else
  514. {
  515. form_driver(m_Form, REQ_NEXT_FIELD);
  516. }
  517. }
  518. // if not beginning of page, previous field, otherwise previous page
  519. // each entry consists of fields: label, isnew, value
  520. // therefore, the label field for the prev. entry is index-5
  521. // and the label field for the next entry is index+1
  522. // (index always corresponds to the value field)
  523. else if ( key == KEY_UP || key == ctrl('p') )
  524. {
  525. FIELD* cur = current_field(m_Form);
  526. int index = field_index(cur);
  527. if ( index == 2 )
  528. {
  529. continue;
  530. }
  531. if ( new_page(m_Fields[index-2]) )
  532. {
  533. form_driver(m_Form, REQ_PREV_PAGE);
  534. set_current_field(m_Form, m_Fields[index-3]);
  535. }
  536. else
  537. {
  538. form_driver(m_Form, REQ_PREV_FIELD);
  539. }
  540. }
  541. // pg down
  542. else if ( key == KEY_NPAGE || key == ctrl('d') )
  543. {
  544. form_driver(m_Form, REQ_NEXT_PAGE);
  545. }
  546. // pg up
  547. else if ( key == KEY_PPAGE || key == ctrl('u') )
  548. {
  549. form_driver(m_Form, REQ_PREV_PAGE);
  550. }
  551. // configure
  552. else if ( key == 'c' )
  553. {
  554. this->RunCMake(false);
  555. }
  556. // display help
  557. else if ( key == 'h' )
  558. {
  559. int x,y;
  560. getmaxyx(stdscr, y, x);
  561. FIELD* cur = current_field(m_Form);
  562. int index = field_index(cur);
  563. cmCursesWidget* lbl = reinterpret_cast<cmCursesWidget*>(field_userptr(
  564. m_Fields[index-2]));
  565. const char* curField = lbl->GetValue();
  566. const char* helpString=0;
  567. cmCacheManager::CacheEntry *entry =
  568. cmCacheManager::GetInstance()->GetCacheEntry(curField);
  569. if (entry)
  570. {
  571. helpString = entry->m_HelpString.c_str();
  572. }
  573. if (helpString)
  574. {
  575. char* message = new char[strlen(curField)+strlen(helpString)
  576. +strlen("Current option is: \n Help string for this option is: \n")+10];
  577. sprintf(message,"Current option is: %s\nHelp string for this option is: %s\n", curField, helpString);
  578. m_HelpMessage[1] = message;
  579. delete[] message;
  580. }
  581. else
  582. {
  583. m_HelpMessage[1] = "";
  584. }
  585. cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(m_HelpMessage,
  586. "Help.");
  587. CurrentForm = msgs;
  588. msgs->Render(1,1,x,y);
  589. msgs->HandleInput();
  590. CurrentForm = this;
  591. this->Render(1,1,x,y);
  592. }
  593. // display last errors
  594. else if ( key == 'l' )
  595. {
  596. int x,y;
  597. getmaxyx(stdscr, y, x);
  598. cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(m_Errors,
  599. "Errors which during last pass.");
  600. CurrentForm = msgs;
  601. msgs->Render(1,1,x,y);
  602. msgs->HandleInput();
  603. CurrentForm = this;
  604. this->Render(1,1,x,y);
  605. }
  606. // switch advanced on/off
  607. else if ( key == 't' )
  608. {
  609. if (m_AdvancedMode)
  610. {
  611. m_AdvancedMode = false;
  612. }
  613. else
  614. {
  615. m_AdvancedMode = true;
  616. }
  617. int x,y;
  618. getmaxyx(stdscr, y, x);
  619. this->RePost();
  620. this->Render(1, 1, x, y);
  621. }
  622. // generate and exit
  623. else if ( key == 'g' )
  624. {
  625. if ( m_OkToGenerate )
  626. {
  627. this->RunCMake(true);
  628. break;
  629. }
  630. }
  631. // delete cache entry
  632. else if ( key == 'd' )
  633. {
  634. FIELD* cur = current_field(m_Form);
  635. int index = field_index(cur);
  636. // make the next or prev. current field after deletion
  637. // each entry consists of fields: label, isnew, value
  638. // therefore, the label field for the prev. entry is index-5
  639. // and the label field for the next entry is index+1
  640. // (index always corresponds to the value field)
  641. FIELD* nextCur;
  642. if ( index == 2 )
  643. {
  644. nextCur=0;
  645. }
  646. else if ( index == 3*m_NumberOfVisibleEntries-1 )
  647. {
  648. nextCur = m_Fields[index-5];
  649. }
  650. else
  651. {
  652. nextCur = m_Fields[index+1];
  653. }
  654. // Get the label widget
  655. // each entry consists of fields: label, isnew, value
  656. // therefore, the label field for the is index-2
  657. // (index always corresponds to the value field)
  658. cmCursesWidget* lbl = reinterpret_cast<cmCursesWidget*>(field_userptr(
  659. m_Fields[index-2]));
  660. cmCacheManager::GetInstance()->RemoveCacheEntry(lbl->GetValue());
  661. std::string nextVal;
  662. if (nextCur)
  663. {
  664. nextVal = (reinterpret_cast<cmCursesWidget*>(field_userptr(nextCur))->GetValue());
  665. }
  666. int x,y;
  667. getmaxyx(stdscr, y, x);
  668. this->RemoveEntry(lbl->GetValue());
  669. this->RePost();
  670. this->Render(1, 1, x, y);
  671. if (nextCur)
  672. {
  673. // make the next or prev. current field after deletion
  674. nextCur = 0;
  675. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  676. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  677. {
  678. if (nextVal == (*it)->m_Key)
  679. {
  680. nextCur = (*it)->m_Entry->m_Field;
  681. }
  682. }
  683. if (nextCur)
  684. {
  685. set_current_field(m_Form, nextCur);
  686. }
  687. }
  688. }
  689. }
  690. touchwin(stdscr);
  691. wrefresh(stdscr);
  692. }
  693. }
  694. const char* cmCursesMainForm::s_ConstHelpMessage =
  695. "CMake is used to configure and generate build files for software projects. "
  696. "The basic steps for configuring a project with ccmake are as follows:\n\n"
  697. "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"
  698. "2. When ccmake is run, it will read the configuration files and display the current build options. "
  699. "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 *. "
  700. "On the other hand, the first time you run ccmake, all build options will be new and will be marked as such. "
  701. "At this point, you can modify any options (see keys below) you want to change. "
  702. "When you are satisfied with your changes, press 'c' to have CMake process the configuration files. "
  703. "Please note that changing some options may cause new ones to appear. These will be shown on top and will be marked with *. "
  704. "Repeat this procedure until you are satisfied with all the options and there are no new entries. "
  705. "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. "
  706. "At any point during the process, you can exit ccmake with 'q'. However, this will not generate/change any build files.\n\n"
  707. "ccmake KEYS:\n\n"
  708. "Navigation: "
  709. "You can use the arrow keys and page up, down to navigate the options. Alternatively, you can use the following keys: \n"
  710. " C-n : next option\n"
  711. " C-p : previous options\n"
  712. " C-d : down one page\n"
  713. " C-u : up one page\n\n"
  714. "Editing options: "
  715. "To change an option press enter or return. If the current options is a boolean, this will toggle it's value. "
  716. "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"
  717. " C-b : back one character\n"
  718. " C-f : forward one character\n"
  719. " C-a : go to the beginning of the field\n"
  720. " C-e : go to the end of the field\n"
  721. " C-d : delete previous character\n"
  722. " C-k : kill the rest of the field\n"
  723. " Esc : Restore field (discard last changes)\n"
  724. "You can also delete an option by pressing 'd'\n\n"
  725. "Commands:\n"
  726. " q : quit ccmake without generating build files\n"
  727. " h : help, shows this screen\n"
  728. " c : process the configuration files with the current options\n"
  729. " g : generate build files and exit, only available when there are no "
  730. "new options and no errors have been detected during last configuration.\n"
  731. " l : shows last errors\n"
  732. " 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";