ENH: fix uppercase version so defines are not upper as well
[cmake.git] / Source / CursesDialog / cmCursesMainForm.cxx
blobc444ebdf12db76c3c0d3b531d14366f0df10fc8b
1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCursesMainForm.cxx,v $
5 Language: C++
6 Date: $Date: 2008-10-09 16:49:49 $
7 Version: $Revision: 1.74 $
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 %d.%d - %s", cmVersion::GetMajorVersion(),
596 cmVersion::GetMinorVersion(),cmVersion::GetReleaseVersion().c_str());
597 int sideSpace = (width-strlen(vertmp));
598 for(i=0; i<sideSpace; i++) { version[i] = ' '; }
599 sprintf(version+sideSpace, "%s", vertmp);
600 version[width] = '\0';
602 // Now print both lines
603 curses_move(y-5,0);
604 attron(A_STANDOUT);
605 char format[] = "%s";
606 printw(format, bar);
607 attroff(A_STANDOUT);
608 curses_move(y-4,0);
609 printw(version);
610 pos_form_cursor(this->Form);
613 void cmCursesMainForm::UpdateProgress(const char *msg, float prog, void* vp)
615 cmCursesMainForm* cm = static_cast<cmCursesMainForm*>(vp);
616 if ( !cm )
618 return;
620 char tmp[1024];
621 const char *cmsg = tmp;
622 if ( prog >= 0 )
624 sprintf(tmp, "%s %i%%",msg,(int)(100*prog));
626 else
628 cmsg = msg;
630 cm->UpdateStatusBar(cmsg);
631 cm->PrintKeys(1);
632 curses_move(1,1);
633 touchwin(stdscr);
634 refresh();
637 int cmCursesMainForm::Configure(int noconfigure)
639 int xi,yi;
640 getmaxyx(stdscr, yi, xi);
642 curses_move(1,1);
643 this->UpdateStatusBar("Configuring, please wait...");
644 this->PrintKeys(1);
645 touchwin(stdscr);
646 refresh();
647 this->CMakeInstance->SetProgressCallback(cmCursesMainForm::UpdateProgress, this);
649 // always save the current gui values to disk
650 this->FillCacheManagerFromUI();
651 this->CMakeInstance->GetCacheManager()->SaveCache(
652 this->CMakeInstance->GetHomeOutputDirectory());
653 this->LoadCache(0);
655 // Get rid of previous errors
656 this->Errors = std::vector<std::string>();
658 // run the generate process
659 this->OkToGenerate = true;
660 int retVal;
661 if ( noconfigure )
663 retVal = this->CMakeInstance->DoPreConfigureChecks();
664 this->OkToGenerate = false;
665 if ( retVal > 0 )
667 retVal = 0;
670 else
672 retVal = this->CMakeInstance->Configure();
674 this->CMakeInstance->SetProgressCallback(0, 0);
676 keypad(stdscr,TRUE); /* Use key symbols as
677 KEY_DOWN*/
679 if( retVal != 0 || !this->Errors.empty())
681 // see if there was an error
682 if(cmSystemTools::GetErrorOccuredFlag())
684 this->OkToGenerate = false;
686 int xx,yy;
687 getmaxyx(stdscr, yy, xx);
688 cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(
689 this->Errors,
690 cmSystemTools::GetErrorOccuredFlag()
691 ? "Errors occurred during the last pass." :
692 "CMake produced the following output.");
693 // reset error condition
694 cmSystemTools::ResetErrorOccuredFlag();
695 CurrentForm = msgs;
696 msgs->Render(1,1,xx,yy);
697 msgs->HandleInput();
698 // If they typed the wrong source directory, we report
699 // an error and exit
700 if ( retVal == -2 )
702 return retVal;
704 CurrentForm = this;
705 this->Render(1,1,xx,yy);
708 this->InitializeUI();
709 this->Render(1, 1, xi, yi);
711 return 0;
714 int cmCursesMainForm::Generate()
716 int xi,yi;
717 getmaxyx(stdscr, yi, xi);
719 curses_move(1,1);
720 this->UpdateStatusBar("Generating, please wait...");
721 this->PrintKeys(1);
722 touchwin(stdscr);
723 refresh();
724 this->CMakeInstance->SetProgressCallback(cmCursesMainForm::UpdateProgress, this);
726 // Get rid of previous errors
727 this->Errors = std::vector<std::string>();
729 // run the generate process
730 int retVal = this->CMakeInstance->Generate();
732 this->CMakeInstance->SetProgressCallback(0, 0);
733 keypad(stdscr,TRUE); /* Use key symbols as
734 KEY_DOWN*/
736 if( retVal != 0 || !this->Errors.empty())
738 // see if there was an error
739 if(cmSystemTools::GetErrorOccuredFlag())
741 this->OkToGenerate = false;
743 // reset error condition
744 cmSystemTools::ResetErrorOccuredFlag();
745 int xx,yy;
746 getmaxyx(stdscr, yy, xx);
747 const char* title = "Messages during last pass.";
748 if(cmSystemTools::GetErrorOccuredFlag())
750 title = "Errors occurred during the last pass.";
752 cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(this->Errors,
753 title);
754 CurrentForm = msgs;
755 msgs->Render(1,1,xx,yy);
756 msgs->HandleInput();
757 // If they typed the wrong source directory, we report
758 // an error and exit
759 if ( retVal == -2 )
761 return retVal;
763 CurrentForm = this;
764 this->Render(1,1,xx,yy);
767 this->InitializeUI();
768 this->Render(1, 1, xi, yi);
770 return 0;
773 void cmCursesMainForm::AddError(const char* message, const char*)
775 this->Errors.push_back(message);
778 void cmCursesMainForm::RemoveEntry(const char* value)
780 if (!value)
782 return;
785 std::vector<cmCursesCacheEntryComposite*>::iterator it;
786 for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
788 const char* val = (*it)->GetValue();
789 if ( val && !strcmp(value, val) )
791 this->Entries->erase(it);
792 break;
797 // copy from the list box to the cache manager
798 void cmCursesMainForm::FillCacheManagerFromUI()
800 int size = this->Entries->size();
801 for(int i=0; i < size; i++)
803 cmCacheManager::CacheIterator it =
804 this->CMakeInstance->GetCacheManager()->GetCacheIterator(
805 (*this->Entries)[i]->Key.c_str());
806 if (!it.IsAtEnd())
808 std::string oldValue = it.GetValue();
809 std::string newValue = (*this->Entries)[i]->Entry->GetValue();
810 std::string fixedOldValue;
811 std::string fixedNewValue;
812 this->FixValue(it.GetType(), oldValue, fixedOldValue);
813 this->FixValue(it.GetType(), newValue, fixedNewValue);
815 if(!(fixedOldValue == fixedNewValue))
817 // The user has changed the value. Mark it as modified.
818 it.SetProperty("MODIFIED", true);
819 it.SetValue(fixedNewValue.c_str());
825 void cmCursesMainForm::FixValue(cmCacheManager::CacheEntryType type,
826 const std::string& in, std::string& out) const
828 out = in.substr(0,in.find_last_not_of(" ")+1);
829 if(type == cmCacheManager::PATH || type == cmCacheManager::FILEPATH)
831 cmSystemTools::ConvertToUnixSlashes(out);
833 if(type == cmCacheManager::BOOL)
835 if(cmSystemTools::IsOff(out.c_str()))
837 out = "OFF";
839 else
841 out = "ON";
846 #include <unistd.h>
848 void cmCursesMainForm::HandleInput()
850 int x=0,y=0;
852 if (!this->Form)
854 return;
857 FIELD* currentField;
858 cmCursesWidget* currentWidget;
860 char debugMessage[128];
862 for(;;)
864 this->UpdateStatusBar();
865 this->PrintKeys();
866 if ( this->SearchMode )
868 std::string searchstr = "Search: " + this->SearchString;
869 this->UpdateStatusBar( searchstr.c_str() );
870 this->PrintKeys(1);
871 curses_move(y-5,searchstr.size());
872 //curses_move(1,1);
873 touchwin(stdscr);
874 refresh();
876 int key = getch();
878 getmaxyx(stdscr, y, x);
879 // If window too small, handle 'q' only
880 if ( x < cmCursesMainForm::MIN_WIDTH ||
881 y < cmCursesMainForm::MIN_HEIGHT )
883 // quit
884 if ( key == 'q' )
886 break;
888 else
890 continue;
894 currentField = current_field(this->Form);
895 currentWidget = reinterpret_cast<cmCursesWidget*>(field_userptr(
896 currentField));
898 bool widgetHandled=false;
900 if ( this->SearchMode )
902 if ( key == 10 || key == KEY_ENTER )
904 this->SearchMode = false;
905 if ( this->SearchString.size() > 0 )
907 this->JumpToCacheEntry(-1, this->SearchString.c_str());
908 this->OldSearchString = this->SearchString;
910 this->SearchString = "";
913 else if ( key == KEY_ESCAPE )
915 this->SearchMode = false;
918 else if ( key >= 'a' && key <= 'z' ||
919 key >= 'A' && key <= 'Z' ||
920 key >= '0' && key <= '9' ||
921 key == '_' )
923 if ( this->SearchString.size() < static_cast<std::string::size_type>(x-10) )
925 this->SearchString += key;
928 else if ( key == ctrl('h') || key == KEY_BACKSPACE || key == KEY_DC )
930 if ( this->SearchString.size() > 0 )
932 this->SearchString.resize(this->SearchString.size()-1);
936 else if (currentWidget && !this->SearchMode)
938 // Ask the current widget if it wants to handle input
939 widgetHandled = currentWidget->HandleInput(key, this, stdscr);
940 if (widgetHandled)
942 this->OkToGenerate = false;
943 this->UpdateStatusBar();
944 this->PrintKeys();
947 if ((!currentWidget || !widgetHandled) && !this->SearchMode)
949 // If the current widget does not want to handle input,
950 // we handle it.
951 sprintf(debugMessage, "Main form handling input, key: %d", key);
952 cmCursesForm::LogMessage(debugMessage);
953 // quit
954 if ( key == 'q' )
956 break;
958 // if not end of page, next field otherwise next page
959 // each entry consists of fields: label, isnew, value
960 // therefore, the label field for the prev. entry is index-5
961 // and the label field for the next entry is index+1
962 // (index always corresponds to the value field)
963 else if ( key == KEY_DOWN || key == ctrl('n') )
965 FIELD* cur = current_field(this->Form);
966 int findex = field_index(cur);
967 if ( findex == 3*this->NumberOfVisibleEntries-1 )
969 continue;
971 if (new_page(this->Fields[findex+1]))
973 form_driver(this->Form, REQ_NEXT_PAGE);
975 else
977 form_driver(this->Form, REQ_NEXT_FIELD);
980 // if not beginning of page, previous field, otherwise previous page
981 // each entry consists of fields: label, isnew, value
982 // therefore, the label field for the prev. entry is index-5
983 // and the label field for the next entry is index+1
984 // (index always corresponds to the value field)
985 else if ( key == KEY_UP || key == ctrl('p') )
987 FIELD* cur = current_field(this->Form);
988 int findex = field_index(cur);
989 if ( findex == 2 )
991 continue;
993 if ( new_page(this->Fields[findex-2]) )
995 form_driver(this->Form, REQ_PREV_PAGE);
996 set_current_field(this->Form, this->Fields[findex-3]);
998 else
1000 form_driver(this->Form, REQ_PREV_FIELD);
1003 // pg down
1004 else if ( key == KEY_NPAGE || key == ctrl('d') )
1006 form_driver(this->Form, REQ_NEXT_PAGE);
1008 // pg up
1009 else if ( key == KEY_PPAGE || key == ctrl('u') )
1011 form_driver(this->Form, REQ_PREV_PAGE);
1013 // configure
1014 else if ( key == 'c' )
1016 this->Configure();
1018 // display help
1019 else if ( key == 'h' )
1021 getmaxyx(stdscr, y, x);
1023 FIELD* cur = current_field(this->Form);
1024 int findex = field_index(cur);
1025 cmCursesWidget* lbl = reinterpret_cast<cmCursesWidget*>(field_userptr(
1026 this->Fields[findex-2]));
1027 const char* curField = lbl->GetValue();
1028 const char* helpString=0;
1029 cmCacheManager::CacheIterator it =
1030 this->CMakeInstance->GetCacheManager()->GetCacheIterator(curField);
1031 if (!it.IsAtEnd())
1033 helpString = it.GetProperty("HELPSTRING");
1035 if (helpString)
1037 char* message = new char[strlen(curField)+strlen(helpString)
1038 +strlen("Current option is: \n Help string for this option is: \n")+10];
1039 sprintf(message,"Current option is: %s\nHelp string for this option is: %s\n", curField, helpString);
1040 this->HelpMessage[1] = message;
1041 delete[] message;
1043 else
1045 this->HelpMessage[1] = "";
1048 cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(this->HelpMessage,
1049 "Help.");
1050 CurrentForm = msgs;
1051 msgs->Render(1,1,x,y);
1052 msgs->HandleInput();
1053 CurrentForm = this;
1054 this->Render(1,1,x,y);
1055 set_current_field(this->Form, cur);
1057 // display last errors
1058 else if ( key == 'l' )
1060 getmaxyx(stdscr, y, x);
1061 cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(this->Errors,
1062 "Errors occurred during the last pass.");
1063 CurrentForm = msgs;
1064 msgs->Render(1,1,x,y);
1065 msgs->HandleInput();
1066 CurrentForm = this;
1067 this->Render(1,1,x,y);
1069 else if ( key == '/' )
1071 this->SearchMode = true;
1072 this->UpdateStatusBar("Search");
1073 this->PrintKeys(1);
1074 touchwin(stdscr);
1075 refresh();
1077 else if ( key == 'n' )
1079 if ( this->OldSearchString.size() > 0 )
1081 this->JumpToCacheEntry(-1, this->OldSearchString.c_str());
1084 // switch advanced on/off
1085 else if ( key == 't' )
1087 if (this->AdvancedMode)
1089 this->AdvancedMode = false;
1091 else
1093 this->AdvancedMode = true;
1095 getmaxyx(stdscr, y, x);
1096 this->RePost();
1097 this->Render(1, 1, x, y);
1099 // generate and exit
1100 else if ( key == 'g' )
1102 if ( this->OkToGenerate )
1104 this->Generate();
1105 break;
1108 // delete cache entry
1109 else if ( key == 'd' && this->NumberOfVisibleEntries )
1111 this->OkToGenerate = false;
1112 FIELD* cur = current_field(this->Form);
1113 int findex = field_index(cur);
1115 // make the next or prev. current field after deletion
1116 // each entry consists of fields: label, isnew, value
1117 // therefore, the label field for the prev. entry is findex-5
1118 // and the label field for the next entry is findex+1
1119 // (findex always corresponds to the value field)
1120 FIELD* nextCur;
1121 if ( findex == 2 )
1123 nextCur=0;
1125 else if ( findex == 3*this->NumberOfVisibleEntries-1 )
1127 nextCur = this->Fields[findex-5];
1129 else
1131 nextCur = this->Fields[findex+1];
1134 // Get the label widget
1135 // each entry consists of fields: label, isnew, value
1136 // therefore, the label field for the is findex-2
1137 // (findex always corresponds to the value field)
1138 cmCursesWidget* lbl
1139 = reinterpret_cast<cmCursesWidget*>(
1140 field_userptr(this->Fields[findex-2]));
1141 if ( lbl )
1143 this->CMakeInstance->GetCacheManager()->RemoveCacheEntry(lbl->GetValue());
1145 std::string nextVal;
1146 if (nextCur)
1148 nextVal = (reinterpret_cast<cmCursesWidget*>(field_userptr(nextCur))->GetValue());
1151 getmaxyx(stdscr, y, x);
1152 this->RemoveEntry(lbl->GetValue());
1153 this->RePost();
1154 this->Render(1, 1, x, y);
1156 if (nextCur)
1158 // make the next or prev. current field after deletion
1159 nextCur = 0;
1160 std::vector<cmCursesCacheEntryComposite*>::iterator it;
1161 for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
1163 if (nextVal == (*it)->Key)
1165 nextCur = (*it)->Entry->Field;
1169 if (nextCur)
1171 set_current_field(this->Form, nextCur);
1178 touchwin(stdscr);
1179 wrefresh(stdscr);
1183 int cmCursesMainForm::LoadCache(const char *)
1186 int r = this->CMakeInstance->LoadCache();
1187 if(r < 0)
1189 return r;
1191 this->CMakeInstance->SetCacheArgs(this->Args);
1192 this->CMakeInstance->PreLoadCMakeFiles();
1193 return r;
1196 void cmCursesMainForm::JumpToCacheEntry(int idx, const char* astr)
1198 std::string str;
1199 if ( astr )
1201 str = cmSystemTools::LowerCase(astr);
1204 if ( idx > this->NumberOfVisibleEntries )
1206 return;
1208 if ( idx < 0 && str.size() == 0)
1210 return;
1212 FIELD* cur = current_field(this->Form);
1213 int start_index = field_index(cur);
1214 int findex = start_index;
1215 while ( (findex / 3) != idx )
1217 if ( str.size() > 0 )
1219 cmCursesWidget* lbl = 0;
1220 if ( findex >= 0 )
1222 lbl = reinterpret_cast<cmCursesWidget*>(field_userptr(this->Fields[findex-2]));
1224 if ( lbl )
1226 const char* curField = lbl->GetValue();
1227 if ( curField )
1229 std::string cfld = cmSystemTools::LowerCase(curField);
1230 if ( cfld.find(str) != cfld.npos && findex != start_index )
1232 break;
1237 if ( findex >= 3* this->NumberOfVisibleEntries-1 )
1239 set_current_field(this->Form, this->Fields[2]);
1241 else if (new_page(this->Fields[findex+1]))
1243 form_driver(this->Form, REQ_NEXT_PAGE);
1245 else
1247 form_driver(this->Form, REQ_NEXT_FIELD);
1250 char buffer[1024];
1251 sprintf(buffer, "Line: %d != %d / %d\n", findex, idx, this->NumberOfVisibleEntries);
1252 touchwin(stdscr);
1253 refresh();
1254 this->UpdateStatusBar( buffer );
1255 usleep(100000);
1257 cur = current_field(this->Form);
1258 findex = field_index(cur);
1259 if ( findex == start_index )
1261 break;
1267 const char* cmCursesMainForm::s_ConstHelpMessage =
1268 "CMake is used to configure and generate build files for software projects. "
1269 "The basic steps for configuring a project with ccmake are as follows:\n\n"
1270 "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"
1271 "2. When ccmake is run, it will read the configuration files and display the current build options. "
1272 "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 *. "
1273 "On the other hand, the first time you run ccmake, all build options will be new and will be marked as such. "
1274 "At this point, you can modify any options (see keys below) you want to change. "
1275 "When you are satisfied with your changes, press 'c' to have CMake process the configuration files. "
1276 "Please note that changing some options may cause new ones to appear. These will be shown on top and will be marked with *. "
1277 "Repeat this procedure until you are satisfied with all the options and there are no new entries. "
1278 "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. "
1279 "At any point during the process, you can exit ccmake with 'q'. However, this will not generate/change any build files.\n\n"
1280 "ccmake KEYS:\n\n"
1281 "Navigation: "
1282 "You can use the arrow keys and page up, down to navigate the options. Alternatively, you can use the following keys: \n"
1283 " C-n : next option\n"
1284 " C-p : previous options\n"
1285 " C-d : down one page\n"
1286 " C-u : up one page\n\n"
1287 "Editing options: "
1288 "To change an option press enter or return. If the current options is a boolean, this will toggle it's value. "
1289 "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"
1290 " C-b : back one character\n"
1291 " C-f : forward one character\n"
1292 " C-a : go to the beginning of the field\n"
1293 " C-e : go to the end of the field\n"
1294 " C-d : delete previous character\n"
1295 " C-k : kill the rest of the field\n"
1296 " Esc : Restore field (discard last changes)\n"
1297 " Enter : Leave edit mode\n"
1298 "You can also delete an option by pressing 'd'\n\n"
1299 "Commands:\n"
1300 " q : quit ccmake without generating build files\n"
1301 " h : help, shows this screen\n"
1302 " c : process the configuration files with the current options\n"
1303 " g : generate build files and exit, only available when there are no "
1304 "new options and no errors have been detected during last configuration.\n"
1305 " l : shows last errors\n"
1306 " 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";