1 /*=========================================================================
3 Program: CMake - Cross-Platform Makefile Generator
4 Module: $RCSfile: cmCursesMainForm.cxx,v $
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"
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 %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
604 char format
[] = "%s";
609 pos_form_cursor(this->Form
);
612 void cmCursesMainForm::UpdateProgress(const char *msg
, float prog
, void* vp
)
614 cmCursesMainForm
* cm
= static_cast<cmCursesMainForm
*>(vp
);
620 const char *cmsg
= tmp
;
623 sprintf(tmp
, "%s %i%%",msg
,(int)(100*prog
));
629 cm
->UpdateStatusBar(cmsg
);
636 int cmCursesMainForm::Configure(int noconfigure
)
639 getmaxyx(stdscr
, yi
, xi
);
642 this->UpdateStatusBar("Configuring, please wait...");
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());
654 // Get rid of previous errors
655 this->Errors
= std::vector
<std::string
>();
657 // run the generate process
658 this->OkToGenerate
= true;
662 retVal
= this->CMakeInstance
->DoPreConfigureChecks();
663 this->OkToGenerate
= false;
671 retVal
= this->CMakeInstance
->Configure();
673 this->CMakeInstance
->SetProgressCallback(0, 0);
675 keypad(stdscr
,TRUE
); /* Use key symbols as
678 if( retVal
!= 0 || !this->Errors
.empty())
680 // see if there was an error
681 if(cmSystemTools::GetErrorOccuredFlag())
683 this->OkToGenerate
= false;
686 getmaxyx(stdscr
, yy
, xx
);
687 cmCursesLongMessageForm
* msgs
= new cmCursesLongMessageForm(
689 cmSystemTools::GetErrorOccuredFlag()
690 ? "Errors occurred during the last pass." :
691 "CMake produced the following output.");
692 // reset error condition
693 cmSystemTools::ResetErrorOccuredFlag();
695 msgs
->Render(1,1,xx
,yy
);
697 // If they typed the wrong source directory, we report
704 this->Render(1,1,xx
,yy
);
707 this->InitializeUI();
708 this->Render(1, 1, xi
, yi
);
713 int cmCursesMainForm::Generate()
716 getmaxyx(stdscr
, yi
, xi
);
719 this->UpdateStatusBar("Generating, please wait...");
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
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();
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
,
754 msgs
->Render(1,1,xx
,yy
);
756 // If they typed the wrong source directory, we report
763 this->Render(1,1,xx
,yy
);
766 this->InitializeUI();
767 this->Render(1, 1, xi
, yi
);
772 void cmCursesMainForm::AddError(const char* message
, const char*)
774 this->Errors
.push_back(message
);
777 void cmCursesMainForm::RemoveEntry(const char* value
)
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
);
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());
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()))
847 void cmCursesMainForm::HandleInput()
857 cmCursesWidget
* currentWidget
;
859 char debugMessage
[128];
863 this->UpdateStatusBar();
865 if ( this->SearchMode
)
867 std::string searchstr
= "Search: " + this->SearchString
;
868 this->UpdateStatusBar( searchstr
.c_str() );
870 curses_move(y
-5,searchstr
.size());
877 getmaxyx(stdscr
, y
, x
);
878 // If window too small, handle 'q' only
879 if ( x
< cmCursesMainForm::MIN_WIDTH
||
880 y
< cmCursesMainForm::MIN_HEIGHT
)
893 currentField
= current_field(this->Form
);
894 currentWidget
= reinterpret_cast<cmCursesWidget
*>(field_userptr(
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' ||
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
);
941 this->OkToGenerate
= false;
942 this->UpdateStatusBar();
946 if ((!currentWidget
|| !widgetHandled
) && !this->SearchMode
)
948 // If the current widget does not want to handle input,
950 sprintf(debugMessage
, "Main form handling input, key: %d", key
);
951 cmCursesForm::LogMessage(debugMessage
);
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 )
970 if (new_page(this->Fields
[findex
+1]))
972 form_driver(this->Form
, REQ_NEXT_PAGE
);
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
);
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]);
999 form_driver(this->Form
, REQ_PREV_FIELD
);
1003 else if ( key
== KEY_NPAGE
|| key
== ctrl('d') )
1005 form_driver(this->Form
, REQ_NEXT_PAGE
);
1008 else if ( key
== KEY_PPAGE
|| key
== ctrl('u') )
1010 form_driver(this->Form
, REQ_PREV_PAGE
);
1013 else if ( key
== 'c' )
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
);
1032 helpString
= it
.GetProperty("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
;
1044 this->HelpMessage
[1] = "";
1047 cmCursesLongMessageForm
* msgs
= new cmCursesLongMessageForm(this->HelpMessage
,
1050 msgs
->Render(1,1,x
,y
);
1051 msgs
->HandleInput();
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.");
1063 msgs
->Render(1,1,x
,y
);
1064 msgs
->HandleInput();
1066 this->Render(1,1,x
,y
);
1068 else if ( key
== '/' )
1070 this->SearchMode
= true;
1071 this->UpdateStatusBar("Search");
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;
1092 this->AdvancedMode
= true;
1094 getmaxyx(stdscr
, y
, x
);
1096 this->Render(1, 1, x
, y
);
1098 // generate and exit
1099 else if ( key
== 'g' )
1101 if ( this->OkToGenerate
)
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)
1124 else if ( findex
== 3*this->NumberOfVisibleEntries
-1 )
1126 nextCur
= this->Fields
[findex
-5];
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)
1138 = reinterpret_cast<cmCursesWidget
*>(
1139 field_userptr(this->Fields
[findex
-2]));
1142 this->CMakeInstance
->GetCacheManager()->RemoveCacheEntry(lbl
->GetValue());
1144 std::string nextVal
;
1147 nextVal
= (reinterpret_cast<cmCursesWidget
*>(field_userptr(nextCur
))->GetValue());
1150 getmaxyx(stdscr
, y
, x
);
1151 this->RemoveEntry(lbl
->GetValue());
1153 this->Render(1, 1, x
, y
);
1157 // make the next or prev. current field after deletion
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
;
1170 set_current_field(this->Form
, nextCur
);
1182 int cmCursesMainForm::LoadCache(const char *)
1185 int r
= this->CMakeInstance
->LoadCache();
1190 this->CMakeInstance
->SetCacheArgs(this->Args
);
1191 this->CMakeInstance
->PreLoadCMakeFiles();
1195 void cmCursesMainForm::JumpToCacheEntry(int idx
, const char* astr
)
1200 str
= cmSystemTools::LowerCase(astr
);
1203 if ( idx
> this->NumberOfVisibleEntries
)
1207 if ( idx
< 0 && str
.size() == 0)
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;
1221 lbl
= reinterpret_cast<cmCursesWidget
*>(field_userptr(this->Fields
[findex
-2]));
1225 const char* curField
= lbl
->GetValue();
1228 std::string cfld
= cmSystemTools::LowerCase(curField
);
1229 if ( cfld
.find(str
) != cfld
.npos
&& findex
!= start_index
)
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
);
1246 form_driver(this->Form
, REQ_NEXT_FIELD
);
1250 sprintf(buffer, "Line: %d != %d / %d\n", findex, idx, this->NumberOfVisibleEntries);
1253 this->UpdateStatusBar( buffer );
1256 cur
= current_field(this->Form
);
1257 findex
= field_index(cur
);
1258 if ( findex
== start_index
)
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"
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"
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"
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";