cmCursesMainForm.cxx 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040
  1. /*=========================================================================
  2. Program: Insight Segmentation & Registration Toolkit
  3. Module: $RCSfile$
  4. Language: C++
  5. Date: $Date$
  6. Version: $Revision$
  7. Copyright (c) 2002 Insight Consortium. All rights reserved.
  8. See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
  9. This software is distributed WITHOUT ANY WARRANTY; without even
  10. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  11. PURPOSE. See the above copyright notices for more information.
  12. =========================================================================*/
  13. #include "../cmCacheManager.h"
  14. #include "../cmSystemTools.h"
  15. #include "../cmake.h"
  16. #include "cmCursesMainForm.h"
  17. #include "cmCursesStringWidget.h"
  18. #include "cmCursesLabelWidget.h"
  19. #include "cmCursesBoolWidget.h"
  20. #include "cmCursesPathWidget.h"
  21. #include "cmCursesFilePathWidget.h"
  22. #include "cmCursesDummyWidget.h"
  23. #include "cmCursesCacheEntryComposite.h"
  24. #include "cmCursesLongMessageForm.h"
  25. inline int ctrl(int z)
  26. {
  27. return (z&037);
  28. }
  29. cmCursesMainForm::cmCursesMainForm(std::vector<std::string> const& args,
  30. int initWidth) :
  31. m_Args(args), m_InitialWidth(initWidth)
  32. {
  33. m_NumberOfPages = 0;
  34. m_Fields = 0;
  35. m_Entries = 0;
  36. m_AdvancedMode = false;
  37. m_NumberOfVisibleEntries = 0;
  38. m_OkToGenerate = false;
  39. m_HelpMessage.push_back("Welcome to ccmake, curses based user interface for CMake.");
  40. m_HelpMessage.push_back("");
  41. m_HelpMessage.push_back(s_ConstHelpMessage);
  42. m_CMakeInstance = new cmake;
  43. // create the arguments for the cmake object
  44. std::string whereCMake = cmSystemTools::GetProgramPath(m_Args[0].c_str());
  45. whereCMake += "/cmake";
  46. m_Args[0] = whereCMake;
  47. m_CMakeInstance->SetArgs(m_Args);
  48. }
  49. cmCursesMainForm::~cmCursesMainForm()
  50. {
  51. if (m_Form)
  52. {
  53. unpost_form(m_Form);
  54. free_form(m_Form);
  55. m_Form = 0;
  56. }
  57. delete[] m_Fields;
  58. // Clean-up composites
  59. if (m_Entries)
  60. {
  61. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  62. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  63. {
  64. delete *it;
  65. }
  66. }
  67. delete m_Entries;
  68. if (this->m_CMakeInstance)
  69. {
  70. delete this->m_CMakeInstance;
  71. this->m_CMakeInstance = 0;
  72. }
  73. }
  74. // See if a cache entry is in the list of entries in the ui.
  75. bool cmCursesMainForm::LookForCacheEntry(const char* key)
  76. {
  77. if (!key || !m_Entries)
  78. {
  79. return false;
  80. }
  81. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  82. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  83. {
  84. if (!strcmp(key, (*it)->m_Key.c_str()))
  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. newEntries->reserve(this->m_CMakeInstance->GetCacheManager()->GetSize());
  99. // Count non-internal and non-static entries
  100. int count=0;
  101. for(cmCacheManager::CacheIterator i =
  102. this->m_CMakeInstance->GetCacheManager()->NewIterator();
  103. !i.IsAtEnd(); i.Next())
  104. {
  105. if ( i.GetType() != cmCacheManager::INTERNAL &&
  106. i.GetType() != cmCacheManager::STATIC &&
  107. i.GetType() != cmCacheManager::UNINITIALIZED)
  108. {
  109. ++count;
  110. }
  111. }
  112. int entrywidth = m_InitialWidth - 35;
  113. cmCursesCacheEntryComposite* comp;
  114. if ( count == 0 )
  115. {
  116. // If cache is empty, display a label saying so and a
  117. // dummy entry widget (does not respond to input)
  118. comp = new cmCursesCacheEntryComposite("EMPTY CACHE", 30, 30);
  119. comp->m_Entry = new cmCursesDummyWidget(1, 1, 1, 1);
  120. newEntries->push_back(comp);
  121. }
  122. else
  123. {
  124. // Create the composites.
  125. // First add entries which are new
  126. for(cmCacheManager::CacheIterator i =
  127. this->m_CMakeInstance->GetCacheManager()->NewIterator();
  128. !i.IsAtEnd(); i.Next())
  129. {
  130. const char* key = i.GetName();
  131. if ( i.GetType() == cmCacheManager::INTERNAL ||
  132. i.GetType() == cmCacheManager::STATIC ||
  133. i.GetType() == cmCacheManager::UNINITIALIZED )
  134. {
  135. continue;
  136. }
  137. if (!this->LookForCacheEntry(key))
  138. {
  139. newEntries->push_back(new cmCursesCacheEntryComposite(key, i,
  140. true, 30,
  141. entrywidth));
  142. m_OkToGenerate = false;
  143. }
  144. }
  145. // then add entries which are old
  146. for(cmCacheManager::CacheIterator i =
  147. this->m_CMakeInstance->GetCacheManager()->NewIterator();
  148. !i.IsAtEnd(); i.Next())
  149. {
  150. const char* key = i.GetName();
  151. if ( i.GetType() == cmCacheManager::INTERNAL ||
  152. i.GetType() == cmCacheManager::STATIC ||
  153. i.GetType() == cmCacheManager::UNINITIALIZED )
  154. {
  155. continue;
  156. }
  157. if (this->LookForCacheEntry(key))
  158. {
  159. newEntries->push_back(new cmCursesCacheEntryComposite(key, i,
  160. false, 30,
  161. entrywidth));
  162. }
  163. }
  164. }
  165. // Clean old entries
  166. if (m_Entries)
  167. {
  168. // Have to call delete on each pointer
  169. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  170. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  171. {
  172. delete *it;
  173. }
  174. }
  175. delete m_Entries;
  176. m_Entries = newEntries;
  177. // Compute fields from composites
  178. this->RePost();
  179. }
  180. void cmCursesMainForm::RePost()
  181. {
  182. // Create the fields to be passed to the form.
  183. if (m_Form)
  184. {
  185. unpost_form(m_Form);
  186. free_form(m_Form);
  187. m_Form = 0;
  188. }
  189. delete[] m_Fields;
  190. if (m_AdvancedMode)
  191. {
  192. m_NumberOfVisibleEntries = m_Entries->size();
  193. }
  194. else
  195. {
  196. // If normal mode, count only non-advanced entries
  197. m_NumberOfVisibleEntries = 0;
  198. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  199. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  200. {
  201. cmCacheManager::CacheIterator mit =
  202. this->m_CMakeInstance->GetCacheManager()->GetCacheIterator((*it)->GetValue());
  203. if (mit.IsAtEnd() || !m_AdvancedMode && mit.GetPropertyAsBool("ADVANCED"))
  204. {
  205. continue;
  206. }
  207. m_NumberOfVisibleEntries++;
  208. }
  209. }
  210. // Assign the fields: 3 for each entry: label, new entry marker
  211. // ('*' or ' ') and entry widget
  212. m_Fields = new FIELD*[3*m_NumberOfVisibleEntries+1];
  213. // Assign fields
  214. int j=0;
  215. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  216. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  217. {
  218. cmCacheManager::CacheIterator mit =
  219. this->m_CMakeInstance->GetCacheManager()->GetCacheIterator((*it)->GetValue());
  220. if (mit.IsAtEnd() || !m_AdvancedMode && mit.GetPropertyAsBool("ADVANCED"))
  221. {
  222. continue;
  223. }
  224. m_Fields[3*j] = (*it)->m_Label->m_Field;
  225. m_Fields[3*j+1] = (*it)->m_IsNewLabel->m_Field;
  226. m_Fields[3*j+2] = (*it)->m_Entry->m_Field;
  227. j++;
  228. }
  229. // Has to be null terminated.
  230. m_Fields[3*m_NumberOfVisibleEntries] = 0;
  231. }
  232. void cmCursesMainForm::Render(int left, int top, int width, int height)
  233. {
  234. if (m_Form)
  235. {
  236. FIELD* currentField = current_field(m_Form);
  237. cmCursesWidget* cw = reinterpret_cast<cmCursesWidget*>
  238. (field_userptr(currentField));
  239. // If in edit mode, get out of it
  240. if ( cw->GetType() == cmCacheManager::STRING ||
  241. cw->GetType() == cmCacheManager::PATH ||
  242. cw->GetType() == cmCacheManager::FILEPATH )
  243. {
  244. cmCursesStringWidget* sw = static_cast<cmCursesStringWidget*>(cw);
  245. sw->SetInEdit(false);
  246. }
  247. // Delete the previous form
  248. unpost_form(m_Form);
  249. free_form(m_Form);
  250. m_Form = 0;
  251. }
  252. // Wrong window size
  253. if ( width < cmCursesMainForm::MIN_WIDTH ||
  254. width < m_InitialWidth ||
  255. height < cmCursesMainForm::MIN_HEIGHT )
  256. {
  257. return;
  258. }
  259. // Leave room for toolbar
  260. height -= 7;
  261. if (m_AdvancedMode)
  262. {
  263. m_NumberOfVisibleEntries = m_Entries->size();
  264. }
  265. else
  266. {
  267. // If normal, display only non-advanced entries
  268. m_NumberOfVisibleEntries = 0;
  269. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  270. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  271. {
  272. cmCacheManager::CacheIterator mit =
  273. this->m_CMakeInstance->GetCacheManager()->GetCacheIterator((*it)->GetValue());
  274. if (mit.IsAtEnd() || !m_AdvancedMode && mit.GetPropertyAsBool("ADVANCED"))
  275. {
  276. continue;
  277. }
  278. m_NumberOfVisibleEntries++;
  279. }
  280. }
  281. // Re-adjust the fields according to their place
  282. bool isNewPage;
  283. int i=0;
  284. m_NumberOfPages = 1;
  285. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  286. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  287. {
  288. cmCacheManager::CacheIterator mit =
  289. this->m_CMakeInstance->GetCacheManager()->GetCacheIterator((*it)->GetValue());
  290. if (mit.IsAtEnd() || !m_AdvancedMode && mit.GetPropertyAsBool("ADVANCED"))
  291. {
  292. continue;
  293. }
  294. int row = (i % height) + 1;
  295. int page = (i / height) + 1;
  296. isNewPage = ( page > 1 ) && ( row == 1 );
  297. if (isNewPage)
  298. {
  299. m_NumberOfPages++;
  300. }
  301. (*it)->m_Label->Move(left, top+row-1, isNewPage);
  302. (*it)->m_IsNewLabel->Move(left+32, top+row-1, false);
  303. (*it)->m_Entry->Move(left+33, top+row-1, false);
  304. (*it)->m_Entry->SetPage(m_NumberOfPages);
  305. i++;
  306. }
  307. // Post the form
  308. m_Form = new_form(m_Fields);
  309. post_form(m_Form);
  310. // Update toolbar
  311. this->UpdateStatusBar();
  312. this->PrintKeys();
  313. touchwin(stdscr);
  314. refresh();
  315. }
  316. void cmCursesMainForm::PrintKeys()
  317. {
  318. int x,y;
  319. getmaxyx(stdscr, y, x);
  320. if ( x < cmCursesMainForm::MIN_WIDTH ||
  321. x < m_InitialWidth ||
  322. y < cmCursesMainForm::MIN_HEIGHT )
  323. {
  324. return;
  325. }
  326. // Give the current widget (if it exists), a chance to print keys
  327. cmCursesWidget* cw = 0;
  328. if (m_Form)
  329. {
  330. FIELD* currentField = current_field(m_Form);
  331. cw = reinterpret_cast<cmCursesWidget*>(field_userptr(currentField));
  332. }
  333. if (cw)
  334. {
  335. cw->PrintKeys();
  336. }
  337. // {
  338. // }
  339. // else
  340. // {
  341. char firstLine[512], secondLine[512], thirdLine[512];
  342. if (m_OkToGenerate)
  343. {
  344. sprintf(firstLine, "Press [c] to configure Press [g] to generate and exit");
  345. }
  346. else
  347. {
  348. sprintf(firstLine, "Press [c] to configure ");
  349. }
  350. if (m_AdvancedMode)
  351. {
  352. sprintf(thirdLine, "Press [t] to toggle advanced mode (Currently On)");
  353. }
  354. else
  355. {
  356. sprintf(thirdLine, "Press [t] to toggle advanced mode (Currently Off)");
  357. }
  358. sprintf(secondLine, "Press [h] for help Press [q] to quit without generating");
  359. curses_move(y-4,0);
  360. char fmt[] = "Press [enter] to edit option";
  361. printw(fmt);
  362. curses_move(y-3,0);
  363. printw(firstLine);
  364. curses_move(y-2,0);
  365. printw(secondLine);
  366. curses_move(y-1,0);
  367. printw(thirdLine);
  368. if (cw)
  369. {
  370. sprintf(firstLine, "Page %d of %d", cw->GetPage(), m_NumberOfPages);
  371. curses_move(0,65-strlen(firstLine)-1);
  372. printw(firstLine);
  373. }
  374. // }
  375. pos_form_cursor(m_Form);
  376. }
  377. // Print the key of the current entry and the CMake version
  378. // on the status bar. Designed for a width of 80 chars.
  379. void cmCursesMainForm::UpdateStatusBar()
  380. {
  381. int x,y;
  382. getmaxyx(stdscr, y, x);
  383. // If window size is too small, display error and return
  384. if ( x < cmCursesMainForm::MIN_WIDTH ||
  385. x < m_InitialWidth ||
  386. y < cmCursesMainForm::MIN_HEIGHT )
  387. {
  388. curses_clear();
  389. curses_move(0,0);
  390. char fmt[] = "Window is too small. A size of at least %dx%d is required.";
  391. printw(fmt,
  392. (cmCursesMainForm::MIN_WIDTH < m_InitialWidth ?
  393. m_InitialWidth : cmCursesMainForm::MIN_WIDTH),
  394. cmCursesMainForm::MIN_HEIGHT);
  395. touchwin(stdscr);
  396. wrefresh(stdscr);
  397. return;
  398. }
  399. // Get the key of the current entry
  400. FIELD* cur = current_field(m_Form);
  401. int index = field_index(cur);
  402. cmCursesWidget* lbl = 0;
  403. if ( index >= 0 )
  404. {
  405. lbl = reinterpret_cast<cmCursesWidget*>(field_userptr(m_Fields[index-2]));
  406. }
  407. char help[128] = "";
  408. const char* curField = "";
  409. if ( lbl )
  410. {
  411. curField = lbl->GetValue();
  412. // Get the help string of the current entry
  413. // and add it to the help string
  414. cmCacheManager::CacheIterator it =
  415. this->m_CMakeInstance->GetCacheManager()->GetCacheIterator(curField);
  416. if (!it.IsAtEnd())
  417. {
  418. const char* hs = it.GetProperty("HELPSTRING");
  419. if ( hs )
  420. {
  421. strncpy(help, hs, 127);
  422. help[127] = '\0';
  423. }
  424. else
  425. {
  426. help[0] = 0;
  427. }
  428. }
  429. else
  430. {
  431. sprintf(help," ");
  432. }
  433. }
  434. // Join the key, help string and pad with spaces
  435. // (or truncate) as necessary
  436. char bar[cmCursesMainForm::MAX_WIDTH];
  437. int i, curFieldLen = strlen(curField);
  438. int helpLen = strlen(help);
  439. int width;
  440. if (x < cmCursesMainForm::MAX_WIDTH )
  441. {
  442. width = x;
  443. }
  444. else
  445. {
  446. width = cmCursesMainForm::MAX_WIDTH;
  447. }
  448. if (curFieldLen >= width)
  449. {
  450. strncpy(bar, curField, width);
  451. }
  452. else
  453. {
  454. strcpy(bar, curField);
  455. bar[curFieldLen] = ':';
  456. bar[curFieldLen+1] = ' ';
  457. if (curFieldLen + helpLen + 2 >= width)
  458. {
  459. strncpy(bar+curFieldLen+2, help, width
  460. - curFieldLen - 2);
  461. }
  462. else
  463. {
  464. strcpy(bar+curFieldLen+2, help);
  465. for(i=curFieldLen+helpLen+2; i < width; ++i)
  466. {
  467. bar[i] = ' ';
  468. }
  469. }
  470. }
  471. bar[width] = '\0';
  472. // Display CMake version info on the next line
  473. // We want to display this on the right
  474. char version[cmCursesMainForm::MAX_WIDTH];
  475. char vertmp[128];
  476. sprintf(vertmp,"CMake Version %d.%d - %s", cmake::GetMajorVersion(),
  477. cmake::GetMinorVersion(),cmake::GetReleaseVersion());
  478. int sideSpace = (width-strlen(vertmp));
  479. for(i=0; i<sideSpace; i++) { version[i] = ' '; }
  480. sprintf(version+sideSpace, "%s", vertmp);
  481. version[width] = '\0';
  482. // Now print both lines
  483. curses_move(y-5,0);
  484. attron(A_STANDOUT);
  485. printw(bar);
  486. attroff(A_STANDOUT);
  487. curses_move(y-4,0);
  488. printw(version);
  489. pos_form_cursor(m_Form);
  490. }
  491. int cmCursesMainForm::Configure()
  492. {
  493. int x,y;
  494. getmaxyx(stdscr, y, x);
  495. curses_clear();
  496. curses_move(1,1);
  497. touchwin(stdscr);
  498. refresh();
  499. endwin();
  500. std::cerr << "Configuring, please wait...\n\r";
  501. // always save the current gui values to disk
  502. this->FillCacheManagerFromUI();
  503. this->m_CMakeInstance->GetCacheManager()->SaveCache(
  504. cmSystemTools::GetCurrentWorkingDirectory().c_str());
  505. // Get rid of previous errors
  506. m_Errors = std::vector<std::string>();
  507. // run the generate process
  508. m_OkToGenerate = true;
  509. int retVal = this->m_CMakeInstance->Configure(m_Args[0].c_str(), &m_Args);
  510. initscr(); /* Initialization */
  511. noecho(); /* Echo off */
  512. cbreak(); /* nl- or cr not needed */
  513. keypad(stdscr,TRUE); /* Use key symbols as
  514. KEY_DOWN*/
  515. if( retVal != 0 || !m_Errors.empty())
  516. {
  517. // see if there was an error
  518. if(cmSystemTools::GetErrorOccuredFlag())
  519. {
  520. m_OkToGenerate = false;
  521. }
  522. // reset error condition
  523. cmSystemTools::ResetErrorOccuredFlag();
  524. int x,y;
  525. getmaxyx(stdscr, y, x);
  526. cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(m_Errors,
  527. "Errors which during last pass.");
  528. CurrentForm = msgs;
  529. msgs->Render(1,1,x,y);
  530. msgs->HandleInput();
  531. // If they typed the wrong source directory, we report
  532. // an error and exit
  533. if ( retVal == -2 )
  534. {
  535. return retVal;
  536. }
  537. CurrentForm = this;
  538. this->Render(1,1,x,y);
  539. }
  540. this->InitializeUI();
  541. this->Render(1, 1, x, y);
  542. return 0;
  543. }
  544. int cmCursesMainForm::Generate()
  545. {
  546. int x,y;
  547. getmaxyx(stdscr, y, x);
  548. curses_clear();
  549. curses_move(1,1);
  550. touchwin(stdscr);
  551. refresh();
  552. endwin();
  553. std::cerr << "Generating, please wait...\n\r";
  554. // Get rid of previous errors
  555. m_Errors = std::vector<std::string>();
  556. // run the generate process
  557. int retVal = this->m_CMakeInstance->Generate();
  558. initscr(); /* Initialization */
  559. noecho(); /* Echo off */
  560. cbreak(); /* nl- or cr not needed */
  561. keypad(stdscr,TRUE); /* Use key symbols as
  562. KEY_DOWN*/
  563. if( retVal != 0 || !m_Errors.empty())
  564. {
  565. // see if there was an error
  566. if(cmSystemTools::GetErrorOccuredFlag())
  567. {
  568. m_OkToGenerate = false;
  569. }
  570. // reset error condition
  571. cmSystemTools::ResetErrorOccuredFlag();
  572. int x,y;
  573. getmaxyx(stdscr, y, x);
  574. cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(m_Errors,
  575. "Errors which during last pass.");
  576. CurrentForm = msgs;
  577. msgs->Render(1,1,x,y);
  578. msgs->HandleInput();
  579. // If they typed the wrong source directory, we report
  580. // an error and exit
  581. if ( retVal == -2 )
  582. {
  583. return retVal;
  584. }
  585. CurrentForm = this;
  586. this->Render(1,1,x,y);
  587. }
  588. this->InitializeUI();
  589. this->Render(1, 1, x, y);
  590. return 0;
  591. }
  592. void cmCursesMainForm::AddError(const char* message, const char*)
  593. {
  594. m_Errors.push_back(message);
  595. }
  596. void cmCursesMainForm::RemoveEntry(const char* value)
  597. {
  598. if (!value)
  599. {
  600. return;
  601. }
  602. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  603. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  604. {
  605. const char* val = (*it)->GetValue();
  606. if ( val && !strcmp(value, val) )
  607. {
  608. m_Entries->erase(it);
  609. break;
  610. }
  611. }
  612. }
  613. // copy from the list box to the cache manager
  614. void cmCursesMainForm::FillCacheManagerFromUI()
  615. {
  616. std::string tmpString;
  617. int size = m_Entries->size();
  618. for(int i=0; i < size; i++)
  619. {
  620. cmCacheManager::CacheIterator it =
  621. this->m_CMakeInstance->GetCacheManager()->GetCacheIterator(
  622. (*m_Entries)[i]->m_Key.c_str());
  623. if (!it.IsAtEnd())
  624. {
  625. tmpString = (*m_Entries)[i]->m_Entry->GetValue();
  626. // Remove trailing spaces, convert path to unix slashes
  627. std::string tmpSubString =
  628. tmpString.substr(0,tmpString.find_last_not_of(" ")+1);
  629. if ( it.GetType() == cmCacheManager::PATH ||
  630. it.GetType() == cmCacheManager::FILEPATH )
  631. {
  632. cmSystemTools::ConvertToUnixSlashes(tmpSubString);
  633. }
  634. it.SetValue(tmpSubString.c_str());
  635. }
  636. }
  637. }
  638. void cmCursesMainForm::HandleInput()
  639. {
  640. int x,y;
  641. if (!m_Form)
  642. {
  643. return;
  644. }
  645. FIELD* currentField;
  646. cmCursesWidget* currentWidget;
  647. char debugMessage[128];
  648. while(1)
  649. {
  650. this->UpdateStatusBar();
  651. this->PrintKeys();
  652. int key = getch();
  653. getmaxyx(stdscr, y, x);
  654. // If window too small, handle 'q' only
  655. if ( x < cmCursesMainForm::MIN_WIDTH ||
  656. y < cmCursesMainForm::MIN_HEIGHT )
  657. {
  658. // quit
  659. if ( key == 'q' )
  660. {
  661. break;
  662. }
  663. else
  664. {
  665. continue;
  666. }
  667. }
  668. currentField = current_field(m_Form);
  669. currentWidget = reinterpret_cast<cmCursesWidget*>(field_userptr(
  670. currentField));
  671. // Ask the current widget if it wants to handle input
  672. bool widgetHandled;
  673. if (currentWidget)
  674. {
  675. widgetHandled = currentWidget->HandleInput(key, this, stdscr);
  676. if (widgetHandled)
  677. {
  678. m_OkToGenerate = false;
  679. this->UpdateStatusBar();
  680. this->PrintKeys();
  681. }
  682. }
  683. if (!currentWidget || !widgetHandled)
  684. {
  685. // If the current widget does not want to handle input,
  686. // we handle it.
  687. sprintf(debugMessage, "Main form handling input, key: %d", key);
  688. cmCursesForm::LogMessage(debugMessage);
  689. // quit
  690. if ( key == 'q' )
  691. {
  692. break;
  693. }
  694. // if not end of page, next field otherwise next page
  695. // each entry consists of fields: label, isnew, value
  696. // therefore, the label field for the prev. entry is index-5
  697. // and the label field for the next entry is index+1
  698. // (index always corresponds to the value field)
  699. else if ( key == KEY_DOWN || key == ctrl('n') )
  700. {
  701. FIELD* cur = current_field(m_Form);
  702. int index = field_index(cur);
  703. if ( index == 3*m_NumberOfVisibleEntries-1 )
  704. {
  705. continue;
  706. }
  707. if (new_page(m_Fields[index+1]))
  708. {
  709. form_driver(m_Form, REQ_NEXT_PAGE);
  710. }
  711. else
  712. {
  713. form_driver(m_Form, REQ_NEXT_FIELD);
  714. }
  715. }
  716. // if not beginning of page, previous field, otherwise previous page
  717. // each entry consists of fields: label, isnew, value
  718. // therefore, the label field for the prev. entry is index-5
  719. // and the label field for the next entry is index+1
  720. // (index always corresponds to the value field)
  721. else if ( key == KEY_UP || key == ctrl('p') )
  722. {
  723. FIELD* cur = current_field(m_Form);
  724. int index = field_index(cur);
  725. if ( index == 2 )
  726. {
  727. continue;
  728. }
  729. if ( new_page(m_Fields[index-2]) )
  730. {
  731. form_driver(m_Form, REQ_PREV_PAGE);
  732. set_current_field(m_Form, m_Fields[index-3]);
  733. }
  734. else
  735. {
  736. form_driver(m_Form, REQ_PREV_FIELD);
  737. }
  738. }
  739. // pg down
  740. else if ( key == KEY_NPAGE || key == ctrl('d') )
  741. {
  742. form_driver(m_Form, REQ_NEXT_PAGE);
  743. }
  744. // pg up
  745. else if ( key == KEY_PPAGE || key == ctrl('u') )
  746. {
  747. form_driver(m_Form, REQ_PREV_PAGE);
  748. }
  749. // configure
  750. else if ( key == 'c' )
  751. {
  752. this->Configure();
  753. }
  754. // display help
  755. else if ( key == 'h' )
  756. {
  757. getmaxyx(stdscr, y, x);
  758. FIELD* cur = current_field(m_Form);
  759. int index = field_index(cur);
  760. cmCursesWidget* lbl = reinterpret_cast<cmCursesWidget*>(field_userptr(
  761. m_Fields[index-2]));
  762. const char* curField = lbl->GetValue();
  763. const char* helpString=0;
  764. cmCacheManager::CacheIterator it =
  765. this->m_CMakeInstance->GetCacheManager()->GetCacheIterator(curField);
  766. if (!it.IsAtEnd())
  767. {
  768. helpString = it.GetProperty("HELPSTRING");
  769. }
  770. if (helpString)
  771. {
  772. char* message = new char[strlen(curField)+strlen(helpString)
  773. +strlen("Current option is: \n Help string for this option is: \n")+10];
  774. sprintf(message,"Current option is: %s\nHelp string for this option is: %s\n", curField, helpString);
  775. m_HelpMessage[1] = message;
  776. delete[] message;
  777. }
  778. else
  779. {
  780. m_HelpMessage[1] = "";
  781. }
  782. cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(m_HelpMessage,
  783. "Help.");
  784. CurrentForm = msgs;
  785. msgs->Render(1,1,x,y);
  786. msgs->HandleInput();
  787. CurrentForm = this;
  788. this->Render(1,1,x,y);
  789. }
  790. // display last errors
  791. else if ( key == 'l' )
  792. {
  793. getmaxyx(stdscr, y, x);
  794. cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(m_Errors,
  795. "Errors which during last pass.");
  796. CurrentForm = msgs;
  797. msgs->Render(1,1,x,y);
  798. msgs->HandleInput();
  799. CurrentForm = this;
  800. this->Render(1,1,x,y);
  801. }
  802. // switch advanced on/off
  803. else if ( key == 't' )
  804. {
  805. if (m_AdvancedMode)
  806. {
  807. m_AdvancedMode = false;
  808. }
  809. else
  810. {
  811. m_AdvancedMode = true;
  812. }
  813. getmaxyx(stdscr, y, x);
  814. this->RePost();
  815. this->Render(1, 1, x, y);
  816. }
  817. // generate and exit
  818. else if ( key == 'g' )
  819. {
  820. if ( m_OkToGenerate )
  821. {
  822. this->Generate();
  823. break;
  824. }
  825. }
  826. // delete cache entry
  827. else if ( key == 'd' )
  828. {
  829. m_OkToGenerate = false;
  830. FIELD* cur = current_field(m_Form);
  831. int index = field_index(cur);
  832. // make the next or prev. current field after deletion
  833. // each entry consists of fields: label, isnew, value
  834. // therefore, the label field for the prev. entry is index-5
  835. // and the label field for the next entry is index+1
  836. // (index always corresponds to the value field)
  837. FIELD* nextCur;
  838. if ( index == 2 )
  839. {
  840. nextCur=0;
  841. }
  842. else if ( index == 3*m_NumberOfVisibleEntries-1 )
  843. {
  844. nextCur = m_Fields[index-5];
  845. }
  846. else
  847. {
  848. nextCur = m_Fields[index+1];
  849. }
  850. // Get the label widget
  851. // each entry consists of fields: label, isnew, value
  852. // therefore, the label field for the is index-2
  853. // (index always corresponds to the value field)
  854. cmCursesWidget* lbl = reinterpret_cast<cmCursesWidget*>(field_userptr(
  855. m_Fields[index-2]));
  856. this->m_CMakeInstance->GetCacheManager()->RemoveCacheEntry(lbl->GetValue());
  857. std::string nextVal;
  858. if (nextCur)
  859. {
  860. nextVal = (reinterpret_cast<cmCursesWidget*>(field_userptr(nextCur))->GetValue());
  861. }
  862. getmaxyx(stdscr, y, x);
  863. this->RemoveEntry(lbl->GetValue());
  864. this->RePost();
  865. this->Render(1, 1, x, y);
  866. if (nextCur)
  867. {
  868. // make the next or prev. current field after deletion
  869. nextCur = 0;
  870. std::vector<cmCursesCacheEntryComposite*>::iterator it;
  871. for (it = m_Entries->begin(); it != m_Entries->end(); ++it)
  872. {
  873. if (nextVal == (*it)->m_Key)
  874. {
  875. nextCur = (*it)->m_Entry->m_Field;
  876. }
  877. }
  878. if (nextCur)
  879. {
  880. set_current_field(m_Form, nextCur);
  881. }
  882. }
  883. }
  884. }
  885. touchwin(stdscr);
  886. wrefresh(stdscr);
  887. }
  888. }
  889. void cmCursesMainForm::LoadCache(const char *dir)
  890. {
  891. m_CMakeInstance->GetCacheManager()->LoadCache(dir);
  892. }
  893. const char* cmCursesMainForm::s_ConstHelpMessage =
  894. "CMake is used to configure and generate build files for software projects. "
  895. "The basic steps for configuring a project with ccmake are as follows:\n\n"
  896. "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"
  897. "2. When ccmake is run, it will read the configuration files and display the current build options. "
  898. "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 *. "
  899. "On the other hand, the first time you run ccmake, all build options will be new and will be marked as such. "
  900. "At this point, you can modify any options (see keys below) you want to change. "
  901. "When you are satisfied with your changes, press 'c' to have CMake process the configuration files. "
  902. "Please note that changing some options may cause new ones to appear. These will be shown on top and will be marked with *. "
  903. "Repeat this procedure until you are satisfied with all the options and there are no new entries. "
  904. "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. "
  905. "At any point during the process, you can exit ccmake with 'q'. However, this will not generate/change any build files.\n\n"
  906. "ccmake KEYS:\n\n"
  907. "Navigation: "
  908. "You can use the arrow keys and page up, down to navigate the options. Alternatively, you can use the following keys: \n"
  909. " C-n : next option\n"
  910. " C-p : previous options\n"
  911. " C-d : down one page\n"
  912. " C-u : up one page\n\n"
  913. "Editing options: "
  914. "To change an option press enter or return. If the current options is a boolean, this will toggle it's value. "
  915. "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"
  916. " C-b : back one character\n"
  917. " C-f : forward one character\n"
  918. " C-a : go to the beginning of the field\n"
  919. " C-e : go to the end of the field\n"
  920. " C-d : delete previous character\n"
  921. " C-k : kill the rest of the field\n"
  922. " Esc : Restore field (discard last changes)\n"
  923. " Enter : Leave edit mode\n"
  924. "You can also delete an option by pressing 'd'\n\n"
  925. "Commands:\n"
  926. " q : quit ccmake without generating build files\n"
  927. " h : help, shows this screen\n"
  928. " c : process the configuration files with the current options\n"
  929. " g : generate build files and exit, only available when there are no "
  930. "new options and no errors have been detected during last configuration.\n"
  931. " l : shows last errors\n"
  932. " 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";