1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCursesMainForm.cxx,v $
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"
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
)
37 cmCursesMainForm::cmCursesMainForm(std::vector
<std::string
> const& args
,
39 Args(args
), InitialWidth(initWidth
)
41 this->NumberOfPages
= 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()
68 unpost_form(this->Form
);
69 free_form(this->Form
);
72 delete[] this->Fields
;
74 // Clean-up composites
77 std::vector
<cmCursesCacheEntryComposite
*>::iterator it
;
78 for (it
= this->Entries
->begin(); it
!= this->Entries
->end(); ++it
)
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
)
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()))
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
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
)
134 int entrywidth
= this->InitialWidth
- 35;
136 cmCursesCacheEntryComposite
* comp
;
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
);
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
)
162 if (!this->LookForCacheEntry(key
))
164 newEntries
->push_back(new cmCursesCacheEntryComposite(key
, i
,
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
)
184 if (this->LookForCacheEntry(key
))
186 newEntries
->push_back(new cmCursesCacheEntryComposite(key
, i
,
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
)
203 delete this->Entries
;
204 this->Entries
= newEntries
;
206 // Compute fields from composites
211 void cmCursesMainForm::RePost()
213 // Create the fields to be passed to the form.
216 unpost_form(this->Form
);
217 free_form(this->Form
);
220 delete[] this->Fields
;
221 if (this->AdvancedMode
)
223 this->NumberOfVisibleEntries
= this->Entries
->size();
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"))
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];
250 for ( cc
= 0; cc
< 3 * this->NumberOfVisibleEntries
+1; cc
++ )
252 this->Fields
[cc
] = 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"))
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
;
271 // if no cache entries there should still be one dummy field
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
)
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
);
307 if ( width
< cmCursesMainForm::MIN_WIDTH
||
308 width
< this->InitialWidth
||
309 height
< cmCursesMainForm::MIN_HEIGHT
)
314 // Leave room for toolbar
317 if (this->AdvancedMode
)
319 this->NumberOfVisibleEntries
= this->Entries
->size();
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"))
334 this->NumberOfVisibleEntries
++;
338 // Re-adjust the fields according to their place
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"))
351 int row
= (i
% height
) + 1;
352 int page
= (i
/ height
) + 1;
353 isNewPage
= ( page
> 1 ) && ( row
== 1 );
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
);
367 this->Form
= new_form(this->Fields
);
368 post_form(this->Form
);
370 this->UpdateStatusBar();
377 void cmCursesMainForm::PrintKeys(int process
/* = 0 */)
380 getmaxyx(stdscr
, y
, x
);
381 if ( x
< cmCursesMainForm::MIN_WIDTH
||
382 x
< this->InitialWidth
||
383 y
< cmCursesMainForm::MIN_HEIGHT
)
388 // Give the current widget (if it exists), a chance to print keys
389 cmCursesWidget
* cw
= 0;
392 FIELD
* currentField
= current_field(this->Form
);
393 cw
= reinterpret_cast<cmCursesWidget
*>(field_userptr(currentField
));
405 char firstLine
[512]="";
406 char secondLine
[512]="";
407 char thirdLine
[512]="";
419 if (this->OkToGenerate
)
422 "Press [c] to configure Press [g] to generate and exit");
426 sprintf(firstLine
, "Press [c] to configure ");
428 if (this->AdvancedMode
)
430 sprintf(thirdLine
, "Press [t] to toggle advanced mode (Currently On)");
434 sprintf(thirdLine
, "Press [t] to toggle advanced mode (Currently Off)");
438 "Press [h] for help Press [q] to quit without generating");
442 char fmt
[512] = "Press [enter] to edit option";
457 sprintf(firstLine
, "Page %d of %d", cw
->GetPage(), this->NumberOfPages
);
458 curses_move(0,65-strlen(firstLine
)-1);
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
)
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
)
480 char fmt
[] = "Window is too small. A size of at least %dx%d is required.";
482 (cmCursesMainForm::MIN_WIDTH
< this->InitialWidth
?
483 this->InitialWidth
: cmCursesMainForm::MIN_WIDTH
),
484 cmCursesMainForm::MIN_HEIGHT
);
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;
496 lbl
= reinterpret_cast<cmCursesWidget
*>(field_userptr(this->Fields
[findex
-2]));
499 const char* curField
= "";
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
);
510 const char* hs
= it
.GetProperty("HELPSTRING");
513 strncpy(help
, hs
, 127);
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
);
534 if (x
< cmCursesMainForm::MAX_WIDTH
)
540 width
= cmCursesMainForm::MAX_WIDTH
;
546 curFieldLen
= strlen(message
);
547 if ( curFieldLen
< width
)
549 strcpy(bar
, curField
);
550 for(i
=curFieldLen
; i
< width
; ++i
)
557 strncpy(bar
, curField
, width
);
562 if (curFieldLen
>= width
)
564 strncpy(bar
, curField
, width
);
568 strcpy(bar
, curField
);
569 bar
[curFieldLen
] = ':';
570 bar
[curFieldLen
+1] = ' ';
571 if (curFieldLen
+ helpLen
+ 2 >= width
)
573 strncpy(bar
+curFieldLen
+2, help
, width
578 strcpy(bar
+curFieldLen
+2, help
);
579 for(i
=curFieldLen
+helpLen
+2; i
< width
; ++i
)
591 // Display CMake version info on the next line
592 // We want to display this on the right
593 char version
[cmCursesMainForm::MAX_WIDTH
];
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
605 char format
[] = "%s";
610 pos_form_cursor(this->Form
);
613 void cmCursesMainForm::UpdateProgress(const char *msg
, float prog
, void* vp
)
615 cmCursesMainForm
* cm
= static_cast<cmCursesMainForm
*>(vp
);
621 const char *cmsg
= tmp
;
624 sprintf(tmp
, "%s %i%%",msg
,(int)(100*prog
));
630 cm
->UpdateStatusBar(cmsg
);
637 int cmCursesMainForm::Configure(int noconfigure
)
640 getmaxyx(stdscr
, yi
, xi
);
643 this->UpdateStatusBar("Configuring, please wait...");
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());
655 // Get rid of previous errors
656 this->Errors
= std::vector
<std::string
>();
658 // run the generate process
659 this->OkToGenerate
= true;
663 retVal
= this->CMakeInstance
->DoPreConfigureChecks();
664 this->OkToGenerate
= false;
672 retVal
= this->CMakeInstance
->Configure();
674 this->CMakeInstance
->SetProgressCallback(0, 0);
676 keypad(stdscr
,TRUE
); /* Use key symbols as
679 if( retVal
!= 0 || !this->Errors
.empty())
681 // see if there was an error
682 if(cmSystemTools::GetErrorOccuredFlag())
684 this->OkToGenerate
= false;
687 getmaxyx(stdscr
, yy
, xx
);
688 cmCursesLongMessageForm
* msgs
= new cmCursesLongMessageForm(
690 cmSystemTools::GetErrorOccuredFlag()
691 ? "Errors occurred during the last pass." :
692 "CMake produced the following output.");
693 // reset error condition
694 cmSystemTools::ResetErrorOccuredFlag();
696 msgs
->Render(1,1,xx
,yy
);
698 // If they typed the wrong source directory, we report
705 this->Render(1,1,xx
,yy
);
708 this->InitializeUI();
709 this->Render(1, 1, xi
, yi
);
714 int cmCursesMainForm::Generate()
717 getmaxyx(stdscr
, yi
, xi
);
720 this->UpdateStatusBar("Generating, please wait...");
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
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();
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
,
755 msgs
->Render(1,1,xx
,yy
);
757 // If they typed the wrong source directory, we report
764 this->Render(1,1,xx
,yy
);
767 this->InitializeUI();
768 this->Render(1, 1, xi
, yi
);
773 void cmCursesMainForm::AddError(const char* message
, const char*)
775 this->Errors
.push_back(message
);
778 void cmCursesMainForm::RemoveEntry(const char* value
)
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
);
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());
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()))
848 void cmCursesMainForm::HandleInput()
858 cmCursesWidget
* currentWidget
;
860 char debugMessage
[128];
864 this->UpdateStatusBar();
866 if ( this->SearchMode
)
868 std::string searchstr
= "Search: " + this->SearchString
;
869 this->UpdateStatusBar( searchstr
.c_str() );
871 curses_move(y
-5,searchstr
.size());
878 getmaxyx(stdscr
, y
, x
);
879 // If window too small, handle 'q' only
880 if ( x
< cmCursesMainForm::MIN_WIDTH
||
881 y
< cmCursesMainForm::MIN_HEIGHT
)
894 currentField
= current_field(this->Form
);
895 currentWidget
= reinterpret_cast<cmCursesWidget
*>(field_userptr(
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' ||
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
);
942 this->OkToGenerate
= false;
943 this->UpdateStatusBar();
947 if ((!currentWidget
|| !widgetHandled
) && !this->SearchMode
)
949 // If the current widget does not want to handle input,
951 sprintf(debugMessage
, "Main form handling input, key: %d", key
);
952 cmCursesForm::LogMessage(debugMessage
);
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 )
971 if (new_page(this->Fields
[findex
+1]))
973 form_driver(this->Form
, REQ_NEXT_PAGE
);
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
);
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]);
1000 form_driver(this->Form
, REQ_PREV_FIELD
);
1004 else if ( key
== KEY_NPAGE
|| key
== ctrl('d') )
1006 form_driver(this->Form
, REQ_NEXT_PAGE
);
1009 else if ( key
== KEY_PPAGE
|| key
== ctrl('u') )
1011 form_driver(this->Form
, REQ_PREV_PAGE
);
1014 else if ( key
== 'c' )
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
);
1033 helpString
= it
.GetProperty("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
;
1045 this->HelpMessage
[1] = "";
1048 cmCursesLongMessageForm
* msgs
= new cmCursesLongMessageForm(this->HelpMessage
,
1051 msgs
->Render(1,1,x
,y
);
1052 msgs
->HandleInput();
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.");
1064 msgs
->Render(1,1,x
,y
);
1065 msgs
->HandleInput();
1067 this->Render(1,1,x
,y
);
1069 else if ( key
== '/' )
1071 this->SearchMode
= true;
1072 this->UpdateStatusBar("Search");
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;
1093 this->AdvancedMode
= true;
1095 getmaxyx(stdscr
, y
, x
);
1097 this->Render(1, 1, x
, y
);
1099 // generate and exit
1100 else if ( key
== 'g' )
1102 if ( this->OkToGenerate
)
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)
1125 else if ( findex
== 3*this->NumberOfVisibleEntries
-1 )
1127 nextCur
= this->Fields
[findex
-5];
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)
1139 = reinterpret_cast<cmCursesWidget
*>(
1140 field_userptr(this->Fields
[findex
-2]));
1143 this->CMakeInstance
->GetCacheManager()->RemoveCacheEntry(lbl
->GetValue());
1145 std::string nextVal
;
1148 nextVal
= (reinterpret_cast<cmCursesWidget
*>(field_userptr(nextCur
))->GetValue());
1151 getmaxyx(stdscr
, y
, x
);
1152 this->RemoveEntry(lbl
->GetValue());
1154 this->Render(1, 1, x
, y
);
1158 // make the next or prev. current field after deletion
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
;
1171 set_current_field(this->Form
, nextCur
);
1183 int cmCursesMainForm::LoadCache(const char *)
1186 int r
= this->CMakeInstance
->LoadCache();
1191 this->CMakeInstance
->SetCacheArgs(this->Args
);
1192 this->CMakeInstance
->PreLoadCMakeFiles();
1196 void cmCursesMainForm::JumpToCacheEntry(int idx
, const char* astr
)
1201 str
= cmSystemTools::LowerCase(astr
);
1204 if ( idx
> this->NumberOfVisibleEntries
)
1208 if ( idx
< 0 && str
.size() == 0)
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;
1222 lbl
= reinterpret_cast<cmCursesWidget
*>(field_userptr(this->Fields
[findex
-2]));
1226 const char* curField
= lbl
->GetValue();
1229 std::string cfld
= cmSystemTools::LowerCase(curField
);
1230 if ( cfld
.find(str
) != cfld
.npos
&& findex
!= start_index
)
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
);
1247 form_driver(this->Form
, REQ_NEXT_FIELD
);
1251 sprintf(buffer, "Line: %d != %d / %d\n", findex, idx, this->NumberOfVisibleEntries);
1254 this->UpdateStatusBar( buffer );
1257 cur
= current_field(this->Form
);
1258 findex
= field_index(cur
);
1259 if ( findex
== start_index
)
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"
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"
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"
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";