Resync
[CMakeLuaTailorHgBridge.git] / CMakeLua / Source / CursesDialog / cmCursesMainForm.cxx
blobf35b4615d2b17fbf7cf38936c37fe5d866000224
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCursesMainForm.cxx,v $
5 Language: C++
6 Date: $Date: 2009-03-05 20:17:06 $
7 Version: $Revision: 1.75 $
9 Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
10 See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
12 This software is distributed WITHOUT ANY WARRANTY; without even
13 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 PURPOSE. See the above copyright notices for more information.
16 =========================================================================*/
17 #include "../cmCacheManager.h"
18 #include "../cmSystemTools.h"
19 #include "../cmVersion.h"
20 #include "../cmake.h"
21 #include "cmCursesMainForm.h"
22 #include "cmCursesStringWidget.h"
23 #include "cmCursesLabelWidget.h"
24 #include "cmCursesBoolWidget.h"
25 #include "cmCursesPathWidget.h"
26 #include "cmCursesFilePathWidget.h"
27 #include "cmCursesDummyWidget.h"
28 #include "cmCursesCacheEntryComposite.h"
29 #include "cmCursesLongMessageForm.h"
32 inline int ctrl(int z)
34 return (z&037);
37 cmCursesMainForm::cmCursesMainForm(std::vector<std::string> const& args,
38 int initWidth) :
39 Args(args), InitialWidth(initWidth)
41 this->NumberOfPages = 0;
42 this->Fields = 0;
43 this->Entries = 0;
44 this->AdvancedMode = false;
45 this->NumberOfVisibleEntries = 0;
46 this->OkToGenerate = false;
47 this->HelpMessage.push_back("Welcome to ccmake, curses based user interface for CMake.");
48 this->HelpMessage.push_back("");
49 this->HelpMessage.push_back(s_ConstHelpMessage);
50 this->CMakeInstance = new cmake;
51 this->CMakeInstance->SetCMakeEditCommand("ccmake");
53 // create the arguments for the cmake object
54 std::string whereCMake = cmSystemTools::GetProgramPath(this->Args[0].c_str());
55 whereCMake += "/cmake";
56 this->Args[0] = whereCMake;
57 this->CMakeInstance->SetArgs(this->Args);
58 this->CMakeInstance->SetCMakeCommand(whereCMake.c_str());
59 this->SearchString = "";
60 this->OldSearchString = "";
61 this->SearchMode = false;
64 cmCursesMainForm::~cmCursesMainForm()
66 if (this->Form)
68 unpost_form(this->Form);
69 free_form(this->Form);
70 this->Form = 0;
72 delete[] this->Fields;
74 // Clean-up composites
75 if (this->Entries)
77 std::vector<cmCursesCacheEntryComposite*>::iterator it;
78 for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
80 delete *it;
83 delete this->Entries;
84 if (this->CMakeInstance)
86 delete this->CMakeInstance;
87 this->CMakeInstance = 0;
91 // See if a cache entry is in the list of entries in the ui.
92 bool cmCursesMainForm::LookForCacheEntry(const char* key)
94 if (!key || !this->Entries)
96 return false;
99 std::vector<cmCursesCacheEntryComposite*>::iterator it;
100 for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
102 if (!strcmp(key, (*it)->Key.c_str()))
104 return true;
108 return false;
111 // Create new cmCursesCacheEntryComposite entries from the cache
112 void cmCursesMainForm::InitializeUI()
114 // Create a vector of cmCursesCacheEntryComposite's
115 // which contain labels, entries and new entry markers
116 std::vector<cmCursesCacheEntryComposite*>* newEntries =
117 new std::vector<cmCursesCacheEntryComposite*>;
118 newEntries->reserve(this->CMakeInstance->GetCacheManager()->GetSize());
120 // Count non-internal and non-static entries
121 int count=0;
122 for(cmCacheManager::CacheIterator i =
123 this->CMakeInstance->GetCacheManager()->NewIterator();
124 !i.IsAtEnd(); i.Next())
126 if ( i.GetType() != cmCacheManager::INTERNAL &&
127 i.GetType() != cmCacheManager::STATIC &&
128 i.GetType() != cmCacheManager::UNINITIALIZED)
130 ++count;
134 int entrywidth = this->InitialWidth - 35;
136 cmCursesCacheEntryComposite* comp;
137 if ( count == 0 )
139 // If cache is empty, display a label saying so and a
140 // dummy entry widget (does not respond to input)
141 comp = new cmCursesCacheEntryComposite("EMPTY CACHE", 30, 30);
142 comp->Entry = new cmCursesDummyWidget(1, 1, 1, 1);
143 newEntries->push_back(comp);
145 else
147 // Create the composites.
149 // First add entries which are new
150 for(cmCacheManager::CacheIterator i =
151 this->CMakeInstance->GetCacheManager()->NewIterator();
152 !i.IsAtEnd(); i.Next())
154 const char* key = i.GetName();
155 if ( i.GetType() == cmCacheManager::INTERNAL ||
156 i.GetType() == cmCacheManager::STATIC ||
157 i.GetType() == cmCacheManager::UNINITIALIZED )
159 continue;
162 if (!this->LookForCacheEntry(key))
164 newEntries->push_back(new cmCursesCacheEntryComposite(key, i,
165 true, 30,
166 entrywidth));
167 this->OkToGenerate = false;
171 // then add entries which are old
172 for(cmCacheManager::CacheIterator i =
173 this->CMakeInstance->GetCacheManager()->NewIterator();
174 !i.IsAtEnd(); i.Next())
176 const char* key = i.GetName();
177 if ( i.GetType() == cmCacheManager::INTERNAL ||
178 i.GetType() == cmCacheManager::STATIC ||
179 i.GetType() == cmCacheManager::UNINITIALIZED )
181 continue;
184 if (this->LookForCacheEntry(key))
186 newEntries->push_back(new cmCursesCacheEntryComposite(key, i,
187 false, 30,
188 entrywidth));
193 // Clean old entries
194 if (this->Entries)
196 // Have to call delete on each pointer
197 std::vector<cmCursesCacheEntryComposite*>::iterator it;
198 for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
200 delete *it;
203 delete this->Entries;
204 this->Entries = newEntries;
206 // Compute fields from composites
207 this->RePost();
211 void cmCursesMainForm::RePost()
213 // Create the fields to be passed to the form.
214 if (this->Form)
216 unpost_form(this->Form);
217 free_form(this->Form);
218 this->Form = 0;
220 delete[] this->Fields;
221 if (this->AdvancedMode)
223 this->NumberOfVisibleEntries = this->Entries->size();
225 else
227 // If normal mode, count only non-advanced entries
228 this->NumberOfVisibleEntries = 0;
229 std::vector<cmCursesCacheEntryComposite*>::iterator it;
230 for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
232 cmCacheManager::CacheIterator mit =
233 this->CMakeInstance->GetCacheManager()->GetCacheIterator((*it)->GetValue());
234 if (mit.IsAtEnd() || !this->AdvancedMode && mit.GetPropertyAsBool("ADVANCED"))
236 continue;
238 this->NumberOfVisibleEntries++;
241 // there is always one even if it is the dummy one
242 if(this->NumberOfVisibleEntries == 0)
244 this->NumberOfVisibleEntries = 1;
246 // Assign the fields: 3 for each entry: label, new entry marker
247 // ('*' or ' ') and entry widget
248 this->Fields = new FIELD*[3*this->NumberOfVisibleEntries+1];
249 int cc;
250 for ( cc = 0; cc < 3 * this->NumberOfVisibleEntries+1; cc ++ )
252 this->Fields[cc] = 0;
255 // Assign fields
256 int j=0;
257 std::vector<cmCursesCacheEntryComposite*>::iterator it;
258 for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
260 cmCacheManager::CacheIterator mit =
261 this->CMakeInstance->GetCacheManager()->GetCacheIterator((*it)->GetValue());
262 if (mit.IsAtEnd() || !this->AdvancedMode && mit.GetPropertyAsBool("ADVANCED"))
264 continue;
266 this->Fields[3*j] = (*it)->Label->Field;
267 this->Fields[3*j+1] = (*it)->IsNewLabel->Field;
268 this->Fields[3*j+2] = (*it)->Entry->Field;
269 j++;
271 // if no cache entries there should still be one dummy field
272 if(j == 0)
274 it = this->Entries->begin();
275 this->Fields[0] = (*it)->Label->Field;
276 this->Fields[1] = (*it)->IsNewLabel->Field;
277 this->Fields[2] = (*it)->Entry->Field;
278 this->NumberOfVisibleEntries = 1;
280 // Has to be null terminated.
281 this->Fields[3*this->NumberOfVisibleEntries] = 0;
284 void cmCursesMainForm::Render(int left, int top, int width, int height)
287 if (this->Form)
289 FIELD* currentField = current_field(this->Form);
290 cmCursesWidget* cw = reinterpret_cast<cmCursesWidget*>
291 (field_userptr(currentField));
292 // If in edit mode, get out of it
293 if ( cw->GetType() == cmCacheManager::STRING ||
294 cw->GetType() == cmCacheManager::PATH ||
295 cw->GetType() == cmCacheManager::FILEPATH )
297 cmCursesStringWidget* sw = static_cast<cmCursesStringWidget*>(cw);
298 sw->SetInEdit(false);
300 // Delete the previous form
301 unpost_form(this->Form);
302 free_form(this->Form);
303 this->Form = 0;
306 // Wrong window size
307 if ( width < cmCursesMainForm::MIN_WIDTH ||
308 width < this->InitialWidth ||
309 height < cmCursesMainForm::MIN_HEIGHT )
311 return;
314 // Leave room for toolbar
315 height -= 7;
317 if (this->AdvancedMode)
319 this->NumberOfVisibleEntries = this->Entries->size();
321 else
323 // If normal, display only non-advanced entries
324 this->NumberOfVisibleEntries = 0;
325 std::vector<cmCursesCacheEntryComposite*>::iterator it;
326 for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
328 cmCacheManager::CacheIterator mit =
329 this->CMakeInstance->GetCacheManager()->GetCacheIterator((*it)->GetValue());
330 if (mit.IsAtEnd() || !this->AdvancedMode && mit.GetPropertyAsBool("ADVANCED"))
332 continue;
334 this->NumberOfVisibleEntries++;
338 // Re-adjust the fields according to their place
339 bool isNewPage;
340 int i=0;
341 this->NumberOfPages = 1;
342 std::vector<cmCursesCacheEntryComposite*>::iterator it;
343 for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
345 cmCacheManager::CacheIterator mit =
346 this->CMakeInstance->GetCacheManager()->GetCacheIterator((*it)->GetValue());
347 if (mit.IsAtEnd() || !this->AdvancedMode && mit.GetPropertyAsBool("ADVANCED"))
349 continue;
351 int row = (i % height) + 1;
352 int page = (i / height) + 1;
353 isNewPage = ( page > 1 ) && ( row == 1 );
355 if (isNewPage)
357 this->NumberOfPages++;
359 (*it)->Label->Move(left, top+row-1, isNewPage);
360 (*it)->IsNewLabel->Move(left+32, top+row-1, false);
361 (*it)->Entry->Move(left+33, top+row-1, false);
362 (*it)->Entry->SetPage(this->NumberOfPages);
363 i++;
366 // Post the form
367 this->Form = new_form(this->Fields);
368 post_form(this->Form);
369 // Update toolbar
370 this->UpdateStatusBar();
371 this->PrintKeys();
373 touchwin(stdscr);
374 refresh();
377 void cmCursesMainForm::PrintKeys(int process /* = 0 */)
379 int x,y;
380 getmaxyx(stdscr, y, x);
381 if ( x < cmCursesMainForm::MIN_WIDTH ||
382 x < this->InitialWidth ||
383 y < cmCursesMainForm::MIN_HEIGHT )
385 return;
388 // Give the current widget (if it exists), a chance to print keys
389 cmCursesWidget* cw = 0;
390 if (this->Form)
392 FIELD* currentField = current_field(this->Form);
393 cw = reinterpret_cast<cmCursesWidget*>(field_userptr(currentField));
396 if (cw)
398 cw->PrintKeys();
401 // {
402 // }
403 // else
404 // {
405 char firstLine[512]="";
406 char secondLine[512]="";
407 char thirdLine[512]="";
408 if (process)
410 sprintf(firstLine,
411 " ");
412 sprintf(secondLine,
413 " ");
414 sprintf(thirdLine,
415 " ");
417 else
419 if (this->OkToGenerate)
421 sprintf(firstLine,
422 "Press [c] to configure Press [g] to generate and exit");
424 else
426 sprintf(firstLine, "Press [c] to configure ");
428 if (this->AdvancedMode)
430 sprintf(thirdLine, "Press [t] to toggle advanced mode (Currently On)");
432 else
434 sprintf(thirdLine, "Press [t] to toggle advanced mode (Currently Off)");
437 sprintf(secondLine,
438 "Press [h] for help Press [q] to quit without generating");
441 curses_move(y-4,0);
442 char fmt[512] = "Press [enter] to edit option";
443 if ( process )
445 strcpy(fmt, " ");
447 printw(fmt);
448 curses_move(y-3,0);
449 printw(firstLine);
450 curses_move(y-2,0);
451 printw(secondLine);
452 curses_move(y-1,0);
453 printw(thirdLine);
455 if (cw)
457 sprintf(firstLine, "Page %d of %d", cw->GetPage(), this->NumberOfPages);
458 curses_move(0,65-strlen(firstLine)-1);
459 printw(firstLine);
461 // }
463 pos_form_cursor(this->Form);
467 // Print the key of the current entry and the CMake version
468 // on the status bar. Designed for a width of 80 chars.
469 void cmCursesMainForm::UpdateStatusBar(const char* message)
471 int x,y;
472 getmaxyx(stdscr, y, x);
473 // If window size is too small, display error and return
474 if ( x < cmCursesMainForm::MIN_WIDTH ||
475 x < this->InitialWidth ||
476 y < cmCursesMainForm::MIN_HEIGHT )
478 curses_clear();
479 curses_move(0,0);
480 char fmt[] = "Window is too small. A size of at least %dx%d is required.";
481 printw(fmt,
482 (cmCursesMainForm::MIN_WIDTH < this->InitialWidth ?
483 this->InitialWidth : cmCursesMainForm::MIN_WIDTH),
484 cmCursesMainForm::MIN_HEIGHT);
485 touchwin(stdscr);
486 wrefresh(stdscr);
487 return;
490 // Get the key of the current entry
491 FIELD* cur = current_field(this->Form);
492 int findex = field_index(cur);
493 cmCursesWidget* lbl = 0;
494 if ( findex >= 0 )
496 lbl = reinterpret_cast<cmCursesWidget*>(field_userptr(this->Fields[findex-2]));
498 char help[128] = "";
499 const char* curField = "";
500 if ( lbl )
502 curField = lbl->GetValue();
504 // Get the help string of the current entry
505 // and add it to the help string
506 cmCacheManager::CacheIterator it =
507 this->CMakeInstance->GetCacheManager()->GetCacheIterator(curField);
508 if (!it.IsAtEnd())
510 const char* hs = it.GetProperty("HELPSTRING");
511 if ( hs )
513 strncpy(help, hs, 127);
514 help[127] = '\0';
516 else
518 help[0] = 0;
521 else
523 sprintf(help," ");
527 // Join the key, help string and pad with spaces
528 // (or truncate) as necessary
529 char bar[cmCursesMainForm::MAX_WIDTH];
530 int i, curFieldLen = strlen(curField);
531 int helpLen = strlen(help);
533 int width;
534 if (x < cmCursesMainForm::MAX_WIDTH )
536 width = x;
538 else
540 width = cmCursesMainForm::MAX_WIDTH;
543 if ( message )
545 curField = message;
546 curFieldLen = strlen(message);
547 if ( curFieldLen < width )
549 strcpy(bar, curField);
550 for(i=curFieldLen; i < width; ++i)
552 bar[i] = ' ';
555 else
557 strncpy(bar, curField, width);
560 else
562 if (curFieldLen >= width)
564 strncpy(bar, curField, width);
566 else
568 strcpy(bar, curField);
569 bar[curFieldLen] = ':';
570 bar[curFieldLen+1] = ' ';
571 if (curFieldLen + helpLen + 2 >= width)
573 strncpy(bar+curFieldLen+2, help, width
574 - curFieldLen - 2);
576 else
578 strcpy(bar+curFieldLen+2, help);
579 for(i=curFieldLen+helpLen+2; i < width; ++i)
581 bar[i] = ' ';
588 bar[width] = '\0';
591 // Display CMake version info on the next line
592 // We want to display this on the right
593 char version[cmCursesMainForm::MAX_WIDTH];
594 char vertmp[128];
595 sprintf(vertmp,"CMake Version %s", cmVersion::GetCMakeVersion());
596 int sideSpace = (width-strlen(vertmp));
597 for(i=0; i<sideSpace; i++) { version[i] = ' '; }
598 sprintf(version+sideSpace, "%s", vertmp);
599 version[width] = '\0';
601 // Now print both lines
602 curses_move(y-5,0);
603 attron(A_STANDOUT);
604 char format[] = "%s";
605 printw(format, bar);
606 attroff(A_STANDOUT);
607 curses_move(y-4,0);
608 printw(version);
609 pos_form_cursor(this->Form);
612 void cmCursesMainForm::UpdateProgress(const char *msg, float prog, void* vp)
614 cmCursesMainForm* cm = static_cast<cmCursesMainForm*>(vp);
615 if ( !cm )
617 return;
619 char tmp[1024];
620 const char *cmsg = tmp;
621 if ( prog >= 0 )
623 sprintf(tmp, "%s %i%%",msg,(int)(100*prog));
625 else
627 cmsg = msg;
629 cm->UpdateStatusBar(cmsg);
630 cm->PrintKeys(1);
631 curses_move(1,1);
632 touchwin(stdscr);
633 refresh();
636 int cmCursesMainForm::Configure(int noconfigure)
638 int xi,yi;
639 getmaxyx(stdscr, yi, xi);
641 curses_move(1,1);
642 this->UpdateStatusBar("Configuring, please wait...");
643 this->PrintKeys(1);
644 touchwin(stdscr);
645 refresh();
646 this->CMakeInstance->SetProgressCallback(cmCursesMainForm::UpdateProgress, this);
648 // always save the current gui values to disk
649 this->FillCacheManagerFromUI();
650 this->CMakeInstance->GetCacheManager()->SaveCache(
651 this->CMakeInstance->GetHomeOutputDirectory());
652 this->LoadCache(0);
654 // Get rid of previous errors
655 this->Errors = std::vector<std::string>();
657 // run the generate process
658 this->OkToGenerate = true;
659 int retVal;
660 if ( noconfigure )
662 retVal = this->CMakeInstance->DoPreConfigureChecks();
663 this->OkToGenerate = false;
664 if ( retVal > 0 )
666 retVal = 0;
669 else
671 retVal = this->CMakeInstance->Configure();
673 this->CMakeInstance->SetProgressCallback(0, 0);
675 keypad(stdscr,TRUE); /* Use key symbols as
676 KEY_DOWN*/
678 if( retVal != 0 || !this->Errors.empty())
680 // see if there was an error
681 if(cmSystemTools::GetErrorOccuredFlag())
683 this->OkToGenerate = false;
685 int xx,yy;
686 getmaxyx(stdscr, yy, xx);
687 cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(
688 this->Errors,
689 cmSystemTools::GetErrorOccuredFlag()
690 ? "Errors occurred during the last pass." :
691 "CMake produced the following output.");
692 // reset error condition
693 cmSystemTools::ResetErrorOccuredFlag();
694 CurrentForm = msgs;
695 msgs->Render(1,1,xx,yy);
696 msgs->HandleInput();
697 // If they typed the wrong source directory, we report
698 // an error and exit
699 if ( retVal == -2 )
701 return retVal;
703 CurrentForm = this;
704 this->Render(1,1,xx,yy);
707 this->InitializeUI();
708 this->Render(1, 1, xi, yi);
710 return 0;
713 int cmCursesMainForm::Generate()
715 int xi,yi;
716 getmaxyx(stdscr, yi, xi);
718 curses_move(1,1);
719 this->UpdateStatusBar("Generating, please wait...");
720 this->PrintKeys(1);
721 touchwin(stdscr);
722 refresh();
723 this->CMakeInstance->SetProgressCallback(cmCursesMainForm::UpdateProgress, this);
725 // Get rid of previous errors
726 this->Errors = std::vector<std::string>();
728 // run the generate process
729 int retVal = this->CMakeInstance->Generate();
731 this->CMakeInstance->SetProgressCallback(0, 0);
732 keypad(stdscr,TRUE); /* Use key symbols as
733 KEY_DOWN*/
735 if( retVal != 0 || !this->Errors.empty())
737 // see if there was an error
738 if(cmSystemTools::GetErrorOccuredFlag())
740 this->OkToGenerate = false;
742 // reset error condition
743 cmSystemTools::ResetErrorOccuredFlag();
744 int xx,yy;
745 getmaxyx(stdscr, yy, xx);
746 const char* title = "Messages during last pass.";
747 if(cmSystemTools::GetErrorOccuredFlag())
749 title = "Errors occurred during the last pass.";
751 cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(this->Errors,
752 title);
753 CurrentForm = msgs;
754 msgs->Render(1,1,xx,yy);
755 msgs->HandleInput();
756 // If they typed the wrong source directory, we report
757 // an error and exit
758 if ( retVal == -2 )
760 return retVal;
762 CurrentForm = this;
763 this->Render(1,1,xx,yy);
766 this->InitializeUI();
767 this->Render(1, 1, xi, yi);
769 return 0;
772 void cmCursesMainForm::AddError(const char* message, const char*)
774 this->Errors.push_back(message);
777 void cmCursesMainForm::RemoveEntry(const char* value)
779 if (!value)
781 return;
784 std::vector<cmCursesCacheEntryComposite*>::iterator it;
785 for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
787 const char* val = (*it)->GetValue();
788 if ( val && !strcmp(value, val) )
790 this->Entries->erase(it);
791 break;
796 // copy from the list box to the cache manager
797 void cmCursesMainForm::FillCacheManagerFromUI()
799 int size = this->Entries->size();
800 for(int i=0; i < size; i++)
802 cmCacheManager::CacheIterator it =
803 this->CMakeInstance->GetCacheManager()->GetCacheIterator(
804 (*this->Entries)[i]->Key.c_str());
805 if (!it.IsAtEnd())
807 std::string oldValue = it.GetValue();
808 std::string newValue = (*this->Entries)[i]->Entry->GetValue();
809 std::string fixedOldValue;
810 std::string fixedNewValue;
811 this->FixValue(it.GetType(), oldValue, fixedOldValue);
812 this->FixValue(it.GetType(), newValue, fixedNewValue);
814 if(!(fixedOldValue == fixedNewValue))
816 // The user has changed the value. Mark it as modified.
817 it.SetProperty("MODIFIED", true);
818 it.SetValue(fixedNewValue.c_str());
824 void cmCursesMainForm::FixValue(cmCacheManager::CacheEntryType type,
825 const std::string& in, std::string& out) const
827 out = in.substr(0,in.find_last_not_of(" ")+1);
828 if(type == cmCacheManager::PATH || type == cmCacheManager::FILEPATH)
830 cmSystemTools::ConvertToUnixSlashes(out);
832 if(type == cmCacheManager::BOOL)
834 if(cmSystemTools::IsOff(out.c_str()))
836 out = "OFF";
838 else
840 out = "ON";
845 #include <unistd.h>
847 void cmCursesMainForm::HandleInput()
849 int x=0,y=0;
851 if (!this->Form)
853 return;
856 FIELD* currentField;
857 cmCursesWidget* currentWidget;
859 char debugMessage[128];
861 for(;;)
863 this->UpdateStatusBar();
864 this->PrintKeys();
865 if ( this->SearchMode )
867 std::string searchstr = "Search: " + this->SearchString;
868 this->UpdateStatusBar( searchstr.c_str() );
869 this->PrintKeys(1);
870 curses_move(y-5,searchstr.size());
871 //curses_move(1,1);
872 touchwin(stdscr);
873 refresh();
875 int key = getch();
877 getmaxyx(stdscr, y, x);
878 // If window too small, handle 'q' only
879 if ( x < cmCursesMainForm::MIN_WIDTH ||
880 y < cmCursesMainForm::MIN_HEIGHT )
882 // quit
883 if ( key == 'q' )
885 break;
887 else
889 continue;
893 currentField = current_field(this->Form);
894 currentWidget = reinterpret_cast<cmCursesWidget*>(field_userptr(
895 currentField));
897 bool widgetHandled=false;
899 if ( this->SearchMode )
901 if ( key == 10 || key == KEY_ENTER )
903 this->SearchMode = false;
904 if ( this->SearchString.size() > 0 )
906 this->JumpToCacheEntry(-1, this->SearchString.c_str());
907 this->OldSearchString = this->SearchString;
909 this->SearchString = "";
912 else if ( key == KEY_ESCAPE )
914 this->SearchMode = false;
917 else if ( key >= 'a' && key <= 'z' ||
918 key >= 'A' && key <= 'Z' ||
919 key >= '0' && key <= '9' ||
920 key == '_' )
922 if ( this->SearchString.size() < static_cast<std::string::size_type>(x-10) )
924 this->SearchString += key;
927 else if ( key == ctrl('h') || key == KEY_BACKSPACE || key == KEY_DC )
929 if ( this->SearchString.size() > 0 )
931 this->SearchString.resize(this->SearchString.size()-1);
935 else if (currentWidget && !this->SearchMode)
937 // Ask the current widget if it wants to handle input
938 widgetHandled = currentWidget->HandleInput(key, this, stdscr);
939 if (widgetHandled)
941 this->OkToGenerate = false;
942 this->UpdateStatusBar();
943 this->PrintKeys();
946 if ((!currentWidget || !widgetHandled) && !this->SearchMode)
948 // If the current widget does not want to handle input,
949 // we handle it.
950 sprintf(debugMessage, "Main form handling input, key: %d", key);
951 cmCursesForm::LogMessage(debugMessage);
952 // quit
953 if ( key == 'q' )
955 break;
957 // if not end of page, next field otherwise next page
958 // each entry consists of fields: label, isnew, value
959 // therefore, the label field for the prev. entry is index-5
960 // and the label field for the next entry is index+1
961 // (index always corresponds to the value field)
962 else if ( key == KEY_DOWN || key == ctrl('n') )
964 FIELD* cur = current_field(this->Form);
965 int findex = field_index(cur);
966 if ( findex == 3*this->NumberOfVisibleEntries-1 )
968 continue;
970 if (new_page(this->Fields[findex+1]))
972 form_driver(this->Form, REQ_NEXT_PAGE);
974 else
976 form_driver(this->Form, REQ_NEXT_FIELD);
979 // if not beginning of page, previous field, otherwise previous page
980 // each entry consists of fields: label, isnew, value
981 // therefore, the label field for the prev. entry is index-5
982 // and the label field for the next entry is index+1
983 // (index always corresponds to the value field)
984 else if ( key == KEY_UP || key == ctrl('p') )
986 FIELD* cur = current_field(this->Form);
987 int findex = field_index(cur);
988 if ( findex == 2 )
990 continue;
992 if ( new_page(this->Fields[findex-2]) )
994 form_driver(this->Form, REQ_PREV_PAGE);
995 set_current_field(this->Form, this->Fields[findex-3]);
997 else
999 form_driver(this->Form, REQ_PREV_FIELD);
1002 // pg down
1003 else if ( key == KEY_NPAGE || key == ctrl('d') )
1005 form_driver(this->Form, REQ_NEXT_PAGE);
1007 // pg up
1008 else if ( key == KEY_PPAGE || key == ctrl('u') )
1010 form_driver(this->Form, REQ_PREV_PAGE);
1012 // configure
1013 else if ( key == 'c' )
1015 this->Configure();
1017 // display help
1018 else if ( key == 'h' )
1020 getmaxyx(stdscr, y, x);
1022 FIELD* cur = current_field(this->Form);
1023 int findex = field_index(cur);
1024 cmCursesWidget* lbl = reinterpret_cast<cmCursesWidget*>(field_userptr(
1025 this->Fields[findex-2]));
1026 const char* curField = lbl->GetValue();
1027 const char* helpString=0;
1028 cmCacheManager::CacheIterator it =
1029 this->CMakeInstance->GetCacheManager()->GetCacheIterator(curField);
1030 if (!it.IsAtEnd())
1032 helpString = it.GetProperty("HELPSTRING");
1034 if (helpString)
1036 char* message = new char[strlen(curField)+strlen(helpString)
1037 +strlen("Current option is: \n Help string for this option is: \n")+10];
1038 sprintf(message,"Current option is: %s\nHelp string for this option is: %s\n", curField, helpString);
1039 this->HelpMessage[1] = message;
1040 delete[] message;
1042 else
1044 this->HelpMessage[1] = "";
1047 cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(this->HelpMessage,
1048 "Help.");
1049 CurrentForm = msgs;
1050 msgs->Render(1,1,x,y);
1051 msgs->HandleInput();
1052 CurrentForm = this;
1053 this->Render(1,1,x,y);
1054 set_current_field(this->Form, cur);
1056 // display last errors
1057 else if ( key == 'l' )
1059 getmaxyx(stdscr, y, x);
1060 cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(this->Errors,
1061 "Errors occurred during the last pass.");
1062 CurrentForm = msgs;
1063 msgs->Render(1,1,x,y);
1064 msgs->HandleInput();
1065 CurrentForm = this;
1066 this->Render(1,1,x,y);
1068 else if ( key == '/' )
1070 this->SearchMode = true;
1071 this->UpdateStatusBar("Search");
1072 this->PrintKeys(1);
1073 touchwin(stdscr);
1074 refresh();
1076 else if ( key == 'n' )
1078 if ( this->OldSearchString.size() > 0 )
1080 this->JumpToCacheEntry(-1, this->OldSearchString.c_str());
1083 // switch advanced on/off
1084 else if ( key == 't' )
1086 if (this->AdvancedMode)
1088 this->AdvancedMode = false;
1090 else
1092 this->AdvancedMode = true;
1094 getmaxyx(stdscr, y, x);
1095 this->RePost();
1096 this->Render(1, 1, x, y);
1098 // generate and exit
1099 else if ( key == 'g' )
1101 if ( this->OkToGenerate )
1103 this->Generate();
1104 break;
1107 // delete cache entry
1108 else if ( key == 'd' && this->NumberOfVisibleEntries )
1110 this->OkToGenerate = false;
1111 FIELD* cur = current_field(this->Form);
1112 int findex = field_index(cur);
1114 // make the next or prev. current field after deletion
1115 // each entry consists of fields: label, isnew, value
1116 // therefore, the label field for the prev. entry is findex-5
1117 // and the label field for the next entry is findex+1
1118 // (findex always corresponds to the value field)
1119 FIELD* nextCur;
1120 if ( findex == 2 )
1122 nextCur=0;
1124 else if ( findex == 3*this->NumberOfVisibleEntries-1 )
1126 nextCur = this->Fields[findex-5];
1128 else
1130 nextCur = this->Fields[findex+1];
1133 // Get the label widget
1134 // each entry consists of fields: label, isnew, value
1135 // therefore, the label field for the is findex-2
1136 // (findex always corresponds to the value field)
1137 cmCursesWidget* lbl
1138 = reinterpret_cast<cmCursesWidget*>(
1139 field_userptr(this->Fields[findex-2]));
1140 if ( lbl )
1142 this->CMakeInstance->GetCacheManager()->RemoveCacheEntry(lbl->GetValue());
1144 std::string nextVal;
1145 if (nextCur)
1147 nextVal = (reinterpret_cast<cmCursesWidget*>(field_userptr(nextCur))->GetValue());
1150 getmaxyx(stdscr, y, x);
1151 this->RemoveEntry(lbl->GetValue());
1152 this->RePost();
1153 this->Render(1, 1, x, y);
1155 if (nextCur)
1157 // make the next or prev. current field after deletion
1158 nextCur = 0;
1159 std::vector<cmCursesCacheEntryComposite*>::iterator it;
1160 for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
1162 if (nextVal == (*it)->Key)
1164 nextCur = (*it)->Entry->Field;
1168 if (nextCur)
1170 set_current_field(this->Form, nextCur);
1177 touchwin(stdscr);
1178 wrefresh(stdscr);
1182 int cmCursesMainForm::LoadCache(const char *)
1185 int r = this->CMakeInstance->LoadCache();
1186 if(r < 0)
1188 return r;
1190 this->CMakeInstance->SetCacheArgs(this->Args);
1191 this->CMakeInstance->PreLoadCMakeFiles();
1192 return r;
1195 void cmCursesMainForm::JumpToCacheEntry(int idx, const char* astr)
1197 std::string str;
1198 if ( astr )
1200 str = cmSystemTools::LowerCase(astr);
1203 if ( idx > this->NumberOfVisibleEntries )
1205 return;
1207 if ( idx < 0 && str.size() == 0)
1209 return;
1211 FIELD* cur = current_field(this->Form);
1212 int start_index = field_index(cur);
1213 int findex = start_index;
1214 while ( (findex / 3) != idx )
1216 if ( str.size() > 0 )
1218 cmCursesWidget* lbl = 0;
1219 if ( findex >= 0 )
1221 lbl = reinterpret_cast<cmCursesWidget*>(field_userptr(this->Fields[findex-2]));
1223 if ( lbl )
1225 const char* curField = lbl->GetValue();
1226 if ( curField )
1228 std::string cfld = cmSystemTools::LowerCase(curField);
1229 if ( cfld.find(str) != cfld.npos && findex != start_index )
1231 break;
1236 if ( findex >= 3* this->NumberOfVisibleEntries-1 )
1238 set_current_field(this->Form, this->Fields[2]);
1240 else if (new_page(this->Fields[findex+1]))
1242 form_driver(this->Form, REQ_NEXT_PAGE);
1244 else
1246 form_driver(this->Form, REQ_NEXT_FIELD);
1249 char buffer[1024];
1250 sprintf(buffer, "Line: %d != %d / %d\n", findex, idx, this->NumberOfVisibleEntries);
1251 touchwin(stdscr);
1252 refresh();
1253 this->UpdateStatusBar( buffer );
1254 usleep(100000);
1256 cur = current_field(this->Form);
1257 findex = field_index(cur);
1258 if ( findex == start_index )
1260 break;
1266 const char* cmCursesMainForm::s_ConstHelpMessage =
1267 "CMake is used to configure and generate build files for software projects. "
1268 "The basic steps for configuring a project with ccmake are as follows:\n\n"
1269 "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"
1270 "2. When ccmake is run, it will read the configuration files and display the current build options. "
1271 "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 *. "
1272 "On the other hand, the first time you run ccmake, all build options will be new and will be marked as such. "
1273 "At this point, you can modify any options (see keys below) you want to change. "
1274 "When you are satisfied with your changes, press 'c' to have CMake process the configuration files. "
1275 "Please note that changing some options may cause new ones to appear. These will be shown on top and will be marked with *. "
1276 "Repeat this procedure until you are satisfied with all the options and there are no new entries. "
1277 "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. "
1278 "At any point during the process, you can exit ccmake with 'q'. However, this will not generate/change any build files.\n\n"
1279 "ccmake KEYS:\n\n"
1280 "Navigation: "
1281 "You can use the arrow keys and page up, down to navigate the options. Alternatively, you can use the following keys: \n"
1282 " C-n : next option\n"
1283 " C-p : previous options\n"
1284 " C-d : down one page\n"
1285 " C-u : up one page\n\n"
1286 "Editing options: "
1287 "To change an option press enter or return. If the current options is a boolean, this will toggle it's value. "
1288 "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"
1289 " C-b : back one character\n"
1290 " C-f : forward one character\n"
1291 " C-a : go to the beginning of the field\n"
1292 " C-e : go to the end of the field\n"
1293 " C-d : delete previous character\n"
1294 " C-k : kill the rest of the field\n"
1295 " Esc : Restore field (discard last changes)\n"
1296 " Enter : Leave edit mode\n"
1297 "You can also delete an option by pressing 'd'\n\n"
1298 "Commands:\n"
1299 " q : quit ccmake without generating build files\n"
1300 " h : help, shows this screen\n"
1301 " c : process the configuration files with the current options\n"
1302 " g : generate build files and exit, only available when there are no "
1303 "new options and no errors have been detected during last configuration.\n"
1304 " l : shows last errors\n"
1305 " 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";