CMake/Source/CursesDialog/cmCursesMainForm.cxx
Brad King 182eb7c7ef Merge topic 'dev/strict-mode'
dd2f814 Merge branch 'dev/add_test-working-directory' into dev/strict-mode
949d32c Unwatch manual variables upon removal in cmake-gui
3939032 Unwatch manual variables upon removal in ccmake
8354413 Add method to unwatch a manual variable
8ed3c85 Give a better message for unused variables
729db48 Fix ArgumentExpansion test expected results
89c2544 Checking for a definition is a usage
5625dee Don't output to stderr in the GUI
ad25a96 Merge branch 'ImprovedDotSupport2' into dev/strict-mode
c128abe Merge branch 'AddCMAKE_CURRENT_LIST_DIR' into dev/strict-mode
9bcaff0 Merge branch 'cmake-guiRememberAdvancedCheckbox' into dev/strict-mode
544d0c3 Fix expected output for WarnUninitialized test
4e3bea4 Update expected messages to new format
8e8c9e4 Don't check at destruction for usage
668e005 Use cmake::IssueMessage for warnings
88cd4c1 Use 'CMake Warning' versus 'warning' for CDash
3c3b98d Initialize the class before setting warn flags
cf8b15a Ignore files under the CMakeFiles directory
fd50f06 Don't check for unused vars at configure time
447a04c Don't warn during configure when doing everything
b97ee21 Check for unused variables at the end of generate
c18c977 When checking for variables, specify a reason
3f1121f Use a long int since Line is a long as well
2507f93 Change the failure case string to 'Unexpected'
fe390a2 Add 'ArgumentExpansion' test
8dbb209 Wrong boolean value for CLI warnings
d4ee998 Hard-code the --no-warn-unused-cli flag
a267b99 Fix line lengths
82ed104 Flag that the directories have been set
5aa535b Add argument to arg parsing to not set directories
367e5c3 Revert "Revert "When calling CMake, set the args and the cache""
ab5d4e4 Revert "When calling CMake, set the args and the cache"
9b90040 When calling CMake, set the args and the cache
fe56002 Fix long lines for KWStyle
5d30cfc Set a watch on variables added through the gui
33c63b1 Add a method to put a watch for variables
535253f Initialize the warning variables earlier
cbb286c Fix the path detection to work for top-level
62be1f7 Initialize the usage stack earlier
c6e7fab Factor out the checks for unused variables
5e41ba8 When using the API, check for Add vs. Remove
dee1976 Fix typo in VariableUnusedViaUnset test
f231ce5 Remove old false positive avoidance code
a117e02 Revert "Add test for unused warnings at the end of scope"
2c82f2b Exempt CMAKE(CURRENT|PARENT)_LIST_FILE from usage
6d7d449 Ignore CLI warnings for ABI determination
7740a73 Only return local keys that are defined
bef3aee Use the API so that warnings can be tracked
05cb0f4 Check for unused variables in the dtor
91c4c99 Add test for unused warnings at the end of scope
ca90f67 Fix detection of unused variables when setting
f7438ca Add test for unused detection via setting it
995cfb0 Don't warn if the variable wasn't defined
aefc91d Add test for usage checks via unset
a8e97f8 Remove VarRemoved code since it's been superceded
59463ef Rework CheckVariableForUnused usage
f117423 Fix line lengths to be no more than 78
e49a935 Improve unused warning logic
e01e40c Mark ARGC, ARGV*, and ARGN as used
a17aff7 Ignore CMAKE_MATCH_* variables for usage
02a114d Add method to allow variables to be marked as used
a0b0d23 CMAKE_DO_TRY_COMPILE is no longer used
ae3eff3 Fix the path used for ignoring system warnings
056b441 Fix missing case for usage of a variable
980e048 Factor out checks for unused variables
83acb0a Remove now unused variables
3801463 Use built-ins for readability and maintainability
8b52015 Push the initialize and unused states when copying
439877f Be consistent with single and double quotes
4cf1706 Add documentation for check-system-vars
b74777f Fix the spelling of the flag for warn-unused-vars
b948120 Change logic of flag to turn off cli unused checks
f047a17 Add test for uninitialized variables
75bda38 Add tests for unused command line variables
300fc15 Fix detection of system files
d784e6a Run the unused variables check on the final pass
9efc057 VariableWatch is not available when bootstrapping
2e78224 Add a missing comma to the warning message
7499700 Add a flag to warn about system files
fff9f6d Rename flags again and use variablewatch for cli
786e269 Add warn-unused to the Qt interface
636e6c4 Default to marking things as used
4ff0340 Rename find-unused to warn-unused
d7999e9 Rename strict-mode to warn-uninitialized
e141bc9 Detect unused variables
d3e8eb5 Add flags to detect unused variables
f332e14 Complete strict-mode checks for uninitialized vars
52f9637 Add method to get the local scope variables
f794d58 Make --strict-mode option, and integrate with cmake-gui
48b5b85 Add a warning when variables are used uninitialized.
cd626ea For macros make sure the FilePath points to a valid pointer in the args.
2011-01-27 15:34:07 -05:00

1307 lines
36 KiB
C++

/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "../cmCacheManager.h"
#include "../cmSystemTools.h"
#include "../cmVersion.h"
#include "../cmake.h"
#include "cmCursesMainForm.h"
#include "cmCursesStringWidget.h"
#include "cmCursesLabelWidget.h"
#include "cmCursesBoolWidget.h"
#include "cmCursesPathWidget.h"
#include "cmCursesFilePathWidget.h"
#include "cmCursesDummyWidget.h"
#include "cmCursesCacheEntryComposite.h"
#include "cmCursesLongMessageForm.h"
inline int ctrl(int z)
{
return (z&037);
}
cmCursesMainForm::cmCursesMainForm(std::vector<std::string> const& args,
int initWidth) :
Args(args), InitialWidth(initWidth)
{
this->NumberOfPages = 0;
this->Fields = 0;
this->Entries = 0;
this->AdvancedMode = false;
this->NumberOfVisibleEntries = 0;
this->OkToGenerate = false;
this->HelpMessage.push_back("Welcome to ccmake, curses based user interface for CMake.");
this->HelpMessage.push_back("");
this->HelpMessage.push_back(s_ConstHelpMessage);
this->CMakeInstance = new cmake;
this->CMakeInstance->SetCMakeEditCommand("ccmake");
// create the arguments for the cmake object
std::string whereCMake = cmSystemTools::GetProgramPath(this->Args[0].c_str());
whereCMake += "/cmake";
this->Args[0] = whereCMake;
this->CMakeInstance->SetArgs(this->Args);
this->CMakeInstance->SetCMakeCommand(whereCMake.c_str());
this->SearchString = "";
this->OldSearchString = "";
this->SearchMode = false;
}
cmCursesMainForm::~cmCursesMainForm()
{
if (this->Form)
{
unpost_form(this->Form);
free_form(this->Form);
this->Form = 0;
}
delete[] this->Fields;
// Clean-up composites
if (this->Entries)
{
std::vector<cmCursesCacheEntryComposite*>::iterator it;
for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
{
delete *it;
}
}
delete this->Entries;
if (this->CMakeInstance)
{
delete this->CMakeInstance;
this->CMakeInstance = 0;
}
}
// See if a cache entry is in the list of entries in the ui.
bool cmCursesMainForm::LookForCacheEntry(const char* key)
{
if (!key || !this->Entries)
{
return false;
}
std::vector<cmCursesCacheEntryComposite*>::iterator it;
for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
{
if (!strcmp(key, (*it)->Key.c_str()))
{
return true;
}
}
return false;
}
// Create new cmCursesCacheEntryComposite entries from the cache
void cmCursesMainForm::InitializeUI()
{
// Create a vector of cmCursesCacheEntryComposite's
// which contain labels, entries and new entry markers
std::vector<cmCursesCacheEntryComposite*>* newEntries =
new std::vector<cmCursesCacheEntryComposite*>;
newEntries->reserve(this->CMakeInstance->GetCacheManager()->GetSize());
// Count non-internal and non-static entries
int count=0;
for(cmCacheManager::CacheIterator i =
this->CMakeInstance->GetCacheManager()->NewIterator();
!i.IsAtEnd(); i.Next())
{
if ( i.GetType() != cmCacheManager::INTERNAL &&
i.GetType() != cmCacheManager::STATIC &&
i.GetType() != cmCacheManager::UNINITIALIZED)
{
++count;
}
}
int entrywidth = this->InitialWidth - 35;
cmCursesCacheEntryComposite* comp;
if ( count == 0 )
{
// If cache is empty, display a label saying so and a
// dummy entry widget (does not respond to input)
comp = new cmCursesCacheEntryComposite("EMPTY CACHE", 30, 30);
comp->Entry = new cmCursesDummyWidget(1, 1, 1, 1);
newEntries->push_back(comp);
}
else
{
// Create the composites.
// First add entries which are new
for(cmCacheManager::CacheIterator i =
this->CMakeInstance->GetCacheManager()->NewIterator();
!i.IsAtEnd(); i.Next())
{
const char* key = i.GetName();
if ( i.GetType() == cmCacheManager::INTERNAL ||
i.GetType() == cmCacheManager::STATIC ||
i.GetType() == cmCacheManager::UNINITIALIZED )
{
continue;
}
if (!this->LookForCacheEntry(key))
{
newEntries->push_back(new cmCursesCacheEntryComposite(key, i,
true, 30,
entrywidth));
this->OkToGenerate = false;
}
}
// then add entries which are old
for(cmCacheManager::CacheIterator i =
this->CMakeInstance->GetCacheManager()->NewIterator();
!i.IsAtEnd(); i.Next())
{
const char* key = i.GetName();
if ( i.GetType() == cmCacheManager::INTERNAL ||
i.GetType() == cmCacheManager::STATIC ||
i.GetType() == cmCacheManager::UNINITIALIZED )
{
continue;
}
if (this->LookForCacheEntry(key))
{
newEntries->push_back(new cmCursesCacheEntryComposite(key, i,
false, 30,
entrywidth));
}
}
}
// Clean old entries
if (this->Entries)
{
// Have to call delete on each pointer
std::vector<cmCursesCacheEntryComposite*>::iterator it;
for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
{
delete *it;
}
}
delete this->Entries;
this->Entries = newEntries;
// Compute fields from composites
this->RePost();
}
void cmCursesMainForm::RePost()
{
// Create the fields to be passed to the form.
if (this->Form)
{
unpost_form(this->Form);
free_form(this->Form);
this->Form = 0;
}
delete[] this->Fields;
if (this->AdvancedMode)
{
this->NumberOfVisibleEntries = this->Entries->size();
}
else
{
// If normal mode, count only non-advanced entries
this->NumberOfVisibleEntries = 0;
std::vector<cmCursesCacheEntryComposite*>::iterator it;
for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
{
cmCacheManager::CacheIterator mit =
this->CMakeInstance->GetCacheManager()->GetCacheIterator((*it)->GetValue());
if (mit.IsAtEnd() ||
(!this->AdvancedMode && mit.GetPropertyAsBool("ADVANCED")))
{
continue;
}
this->NumberOfVisibleEntries++;
}
}
// there is always one even if it is the dummy one
if(this->NumberOfVisibleEntries == 0)
{
this->NumberOfVisibleEntries = 1;
}
// Assign the fields: 3 for each entry: label, new entry marker
// ('*' or ' ') and entry widget
this->Fields = new FIELD*[3*this->NumberOfVisibleEntries+1];
size_t cc;
for ( cc = 0; cc < 3 * this->NumberOfVisibleEntries+1; cc ++ )
{
this->Fields[cc] = 0;
}
// Assign fields
int j=0;
std::vector<cmCursesCacheEntryComposite*>::iterator it;
for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
{
cmCacheManager::CacheIterator mit =
this->CMakeInstance->GetCacheManager()->GetCacheIterator((*it)->GetValue());
if (mit.IsAtEnd() ||
(!this->AdvancedMode && mit.GetPropertyAsBool("ADVANCED")))
{
continue;
}
this->Fields[3*j] = (*it)->Label->Field;
this->Fields[3*j+1] = (*it)->IsNewLabel->Field;
this->Fields[3*j+2] = (*it)->Entry->Field;
j++;
}
// if no cache entries there should still be one dummy field
if(j == 0)
{
it = this->Entries->begin();
this->Fields[0] = (*it)->Label->Field;
this->Fields[1] = (*it)->IsNewLabel->Field;
this->Fields[2] = (*it)->Entry->Field;
this->NumberOfVisibleEntries = 1;
}
// Has to be null terminated.
this->Fields[3*this->NumberOfVisibleEntries] = 0;
}
void cmCursesMainForm::Render(int left, int top, int width, int height)
{
if (this->Form)
{
FIELD* currentField = current_field(this->Form);
cmCursesWidget* cw = reinterpret_cast<cmCursesWidget*>
(field_userptr(currentField));
// If in edit mode, get out of it
if ( cw->GetType() == cmCacheManager::STRING ||
cw->GetType() == cmCacheManager::PATH ||
cw->GetType() == cmCacheManager::FILEPATH )
{
cmCursesStringWidget* sw = static_cast<cmCursesStringWidget*>(cw);
sw->SetInEdit(false);
}
// Delete the previous form
unpost_form(this->Form);
free_form(this->Form);
this->Form = 0;
}
// Wrong window size
if ( width < cmCursesMainForm::MIN_WIDTH ||
width < this->InitialWidth ||
height < cmCursesMainForm::MIN_HEIGHT )
{
return;
}
// Leave room for toolbar
height -= 7;
if (this->AdvancedMode)
{
this->NumberOfVisibleEntries = this->Entries->size();
}
else
{
// If normal, display only non-advanced entries
this->NumberOfVisibleEntries = 0;
std::vector<cmCursesCacheEntryComposite*>::iterator it;
for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
{
cmCacheManager::CacheIterator mit =
this->CMakeInstance->GetCacheManager()->GetCacheIterator((*it)->GetValue());
if (mit.IsAtEnd() ||
(!this->AdvancedMode && mit.GetPropertyAsBool("ADVANCED")))
{
continue;
}
this->NumberOfVisibleEntries++;
}
}
// Re-adjust the fields according to their place
this->NumberOfPages = 1;
if (height > 0)
{
bool isNewPage;
int i=0;
std::vector<cmCursesCacheEntryComposite*>::iterator it;
for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
{
cmCacheManager::CacheIterator mit =
this->CMakeInstance->GetCacheManager()->GetCacheIterator((*it)->GetValue());
if (mit.IsAtEnd() ||
(!this->AdvancedMode && mit.GetPropertyAsBool("ADVANCED")))
{
continue;
}
int row = (i % height) + 1;
int page = (i / height) + 1;
isNewPage = ( page > 1 ) && ( row == 1 );
if (isNewPage)
{
this->NumberOfPages++;
}
(*it)->Label->Move(left, top+row-1, isNewPage);
(*it)->IsNewLabel->Move(left+32, top+row-1, false);
(*it)->Entry->Move(left+33, top+row-1, false);
(*it)->Entry->SetPage(this->NumberOfPages);
i++;
}
}
// Post the form
this->Form = new_form(this->Fields);
post_form(this->Form);
// Update toolbar
this->UpdateStatusBar();
this->PrintKeys();
touchwin(stdscr);
refresh();
}
void cmCursesMainForm::PrintKeys(int process /* = 0 */)
{
int x,y;
getmaxyx(stdscr, y, x);
if ( x < cmCursesMainForm::MIN_WIDTH ||
x < this->InitialWidth ||
y < cmCursesMainForm::MIN_HEIGHT )
{
return;
}
// Give the current widget (if it exists), a chance to print keys
cmCursesWidget* cw = 0;
if (this->Form)
{
FIELD* currentField = current_field(this->Form);
cw = reinterpret_cast<cmCursesWidget*>(field_userptr(currentField));
}
if (cw)
{
cw->PrintKeys();
}
// {
// }
// else
// {
char firstLine[512]="";
char secondLine[512]="";
char thirdLine[512]="";
if (process)
{
sprintf(firstLine,
" ");
sprintf(secondLine,
" ");
sprintf(thirdLine,
" ");
}
else
{
if (this->OkToGenerate)
{
sprintf(firstLine,
"Press [c] to configure Press [g] to generate and exit");
}
else
{
sprintf(firstLine, "Press [c] to configure ");
}
if (this->AdvancedMode)
{
sprintf(thirdLine, "Press [t] to toggle advanced mode (Currently On)");
}
else
{
sprintf(thirdLine, "Press [t] to toggle advanced mode (Currently Off)");
}
sprintf(secondLine,
"Press [h] for help Press [q] to quit without generating");
}
curses_move(y-4,0);
char fmt[512] = "Press [enter] to edit option";
if ( process )
{
strcpy(fmt, " ");
}
printw(fmt);
curses_move(y-3,0);
printw(firstLine);
curses_move(y-2,0);
printw(secondLine);
curses_move(y-1,0);
printw(thirdLine);
if (cw)
{
sprintf(firstLine, "Page %d of %d", cw->GetPage(), this->NumberOfPages);
curses_move(0,65-static_cast<unsigned int>(strlen(firstLine))-1);
printw(firstLine);
}
// }
pos_form_cursor(this->Form);
}
// Print the key of the current entry and the CMake version
// on the status bar. Designed for a width of 80 chars.
void cmCursesMainForm::UpdateStatusBar(const char* message)
{
int x,y;
getmaxyx(stdscr, y, x);
// If window size is too small, display error and return
if ( x < cmCursesMainForm::MIN_WIDTH ||
x < this->InitialWidth ||
y < cmCursesMainForm::MIN_HEIGHT )
{
curses_clear();
curses_move(0,0);
char fmt[] = "Window is too small. A size of at least %dx%d is required.";
printw(fmt,
(cmCursesMainForm::MIN_WIDTH < this->InitialWidth ?
this->InitialWidth : cmCursesMainForm::MIN_WIDTH),
cmCursesMainForm::MIN_HEIGHT);
touchwin(stdscr);
wrefresh(stdscr);
return;
}
// Get the key of the current entry
FIELD* cur = current_field(this->Form);
int findex = field_index(cur);
cmCursesWidget* lbl = 0;
if ( findex >= 0 )
{
lbl = reinterpret_cast<cmCursesWidget*>(field_userptr(this->Fields[findex-2]));
}
char help[128] = "";
const char* curField = "";
if ( lbl )
{
curField = lbl->GetValue();
// Get the help string of the current entry
// and add it to the help string
cmCacheManager::CacheIterator it =
this->CMakeInstance->GetCacheManager()->GetCacheIterator(curField);
if (!it.IsAtEnd())
{
const char* hs = it.GetProperty("HELPSTRING");
if ( hs )
{
strncpy(help, hs, 127);
help[127] = '\0';
}
else
{
help[0] = 0;
}
}
else
{
sprintf(help," ");
}
}
// Join the key, help string and pad with spaces
// (or truncate) as necessary
char bar[cmCursesMainForm::MAX_WIDTH];
size_t i, curFieldLen = strlen(curField);
size_t helpLen = strlen(help);
size_t width;
if (x < cmCursesMainForm::MAX_WIDTH )
{
width = x;
}
else
{
width = cmCursesMainForm::MAX_WIDTH;
}
if ( message )
{
curField = message;
curFieldLen = strlen(message);
if ( curFieldLen < width )
{
strcpy(bar, curField);
for(i=curFieldLen; i < width; ++i)
{
bar[i] = ' ';
}
}
else
{
strncpy(bar, curField, width);
}
}
else
{
if (curFieldLen >= width)
{
strncpy(bar, curField, width);
}
else
{
strcpy(bar, curField);
bar[curFieldLen] = ':';
bar[curFieldLen+1] = ' ';
if (curFieldLen + helpLen + 2 >= width)
{
strncpy(bar+curFieldLen+2, help, width
- curFieldLen - 2);
}
else
{
strcpy(bar+curFieldLen+2, help);
for(i=curFieldLen+helpLen+2; i < width; ++i)
{
bar[i] = ' ';
}
}
}
}
bar[width] = '\0';
// Display CMake version info on the next line
// We want to display this on the right
char version[cmCursesMainForm::MAX_WIDTH];
char vertmp[128];
sprintf(vertmp,"CMake Version %s", cmVersion::GetCMakeVersion());
size_t sideSpace = (width-strlen(vertmp));
for(i=0; i<sideSpace; i++) { version[i] = ' '; }
sprintf(version+sideSpace, "%s", vertmp);
version[width] = '\0';
// Now print both lines
curses_move(y-5,0);
attron(A_STANDOUT);
char format[] = "%s";
printw(format, bar);
attroff(A_STANDOUT);
curses_move(y-4,0);
printw(version);
pos_form_cursor(this->Form);
}
void cmCursesMainForm::UpdateProgress(const char *msg, float prog, void* vp)
{
cmCursesMainForm* cm = static_cast<cmCursesMainForm*>(vp);
if ( !cm )
{
return;
}
char tmp[1024];
const char *cmsg = tmp;
if ( prog >= 0 )
{
sprintf(tmp, "%s %i%%",msg,(int)(100*prog));
}
else
{
cmsg = msg;
}
cm->UpdateStatusBar(cmsg);
cm->PrintKeys(1);
curses_move(1,1);
touchwin(stdscr);
refresh();
}
int cmCursesMainForm::Configure(int noconfigure)
{
int xi,yi;
getmaxyx(stdscr, yi, xi);
curses_move(1,1);
this->UpdateStatusBar("Configuring, please wait...");
this->PrintKeys(1);
touchwin(stdscr);
refresh();
this->CMakeInstance->SetProgressCallback(cmCursesMainForm::UpdateProgress, this);
// always save the current gui values to disk
this->FillCacheManagerFromUI();
this->CMakeInstance->GetCacheManager()->SaveCache(
this->CMakeInstance->GetHomeOutputDirectory());
this->LoadCache(0);
// Get rid of previous errors
this->Errors = std::vector<std::string>();
// run the generate process
this->OkToGenerate = true;
int retVal;
if ( noconfigure )
{
retVal = this->CMakeInstance->DoPreConfigureChecks();
this->OkToGenerate = false;
if ( retVal > 0 )
{
retVal = 0;
}
}
else
{
retVal = this->CMakeInstance->Configure();
}
this->CMakeInstance->SetProgressCallback(0, 0);
keypad(stdscr,TRUE); /* Use key symbols as
KEY_DOWN*/
if( retVal != 0 || !this->Errors.empty())
{
// see if there was an error
if(cmSystemTools::GetErrorOccuredFlag())
{
this->OkToGenerate = false;
}
int xx,yy;
getmaxyx(stdscr, yy, xx);
cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(
this->Errors,
cmSystemTools::GetErrorOccuredFlag()
? "Errors occurred during the last pass." :
"CMake produced the following output.");
// reset error condition
cmSystemTools::ResetErrorOccuredFlag();
CurrentForm = msgs;
msgs->Render(1,1,xx,yy);
msgs->HandleInput();
// If they typed the wrong source directory, we report
// an error and exit
if ( retVal == -2 )
{
return retVal;
}
CurrentForm = this;
this->Render(1,1,xx,yy);
}
this->InitializeUI();
this->Render(1, 1, xi, yi);
return 0;
}
int cmCursesMainForm::Generate()
{
int xi,yi;
getmaxyx(stdscr, yi, xi);
curses_move(1,1);
this->UpdateStatusBar("Generating, please wait...");
this->PrintKeys(1);
touchwin(stdscr);
refresh();
this->CMakeInstance->SetProgressCallback(cmCursesMainForm::UpdateProgress, this);
// Get rid of previous errors
this->Errors = std::vector<std::string>();
// run the generate process
int retVal = this->CMakeInstance->Generate();
this->CMakeInstance->SetProgressCallback(0, 0);
keypad(stdscr,TRUE); /* Use key symbols as
KEY_DOWN*/
if( retVal != 0 || !this->Errors.empty())
{
// see if there was an error
if(cmSystemTools::GetErrorOccuredFlag())
{
this->OkToGenerate = false;
}
// reset error condition
cmSystemTools::ResetErrorOccuredFlag();
int xx,yy;
getmaxyx(stdscr, yy, xx);
const char* title = "Messages during last pass.";
if(cmSystemTools::GetErrorOccuredFlag())
{
title = "Errors occurred during the last pass.";
}
cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(this->Errors,
title);
CurrentForm = msgs;
msgs->Render(1,1,xx,yy);
msgs->HandleInput();
// If they typed the wrong source directory, we report
// an error and exit
if ( retVal == -2 )
{
return retVal;
}
CurrentForm = this;
this->Render(1,1,xx,yy);
}
this->InitializeUI();
this->Render(1, 1, xi, yi);
return 0;
}
void cmCursesMainForm::AddError(const char* message, const char*)
{
this->Errors.push_back(message);
}
void cmCursesMainForm::RemoveEntry(const char* value)
{
if (!value)
{
return;
}
std::vector<cmCursesCacheEntryComposite*>::iterator it;
for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
{
const char* val = (*it)->GetValue();
if ( val && !strcmp(value, val) )
{
this->CMakeInstance->UnwatchUnusedCli(value);
this->Entries->erase(it);
break;
}
}
}
// copy from the list box to the cache manager
void cmCursesMainForm::FillCacheManagerFromUI()
{
size_t size = this->Entries->size();
for(size_t i=0; i < size; i++)
{
cmCacheManager::CacheIterator it =
this->CMakeInstance->GetCacheManager()->GetCacheIterator(
(*this->Entries)[i]->Key.c_str());
if (!it.IsAtEnd())
{
std::string oldValue = it.GetValue();
std::string newValue = (*this->Entries)[i]->Entry->GetValue();
std::string fixedOldValue;
std::string fixedNewValue;
this->FixValue(it.GetType(), oldValue, fixedOldValue);
this->FixValue(it.GetType(), newValue, fixedNewValue);
if(!(fixedOldValue == fixedNewValue))
{
// The user has changed the value. Mark it as modified.
it.SetProperty("MODIFIED", true);
it.SetValue(fixedNewValue.c_str());
}
}
}
}
void cmCursesMainForm::FixValue(cmCacheManager::CacheEntryType type,
const std::string& in, std::string& out) const
{
out = in.substr(0,in.find_last_not_of(" ")+1);
if(type == cmCacheManager::PATH || type == cmCacheManager::FILEPATH)
{
cmSystemTools::ConvertToUnixSlashes(out);
}
if(type == cmCacheManager::BOOL)
{
if(cmSystemTools::IsOff(out.c_str()))
{
out = "OFF";
}
else
{
out = "ON";
}
}
}
#include <unistd.h>
void cmCursesMainForm::HandleInput()
{
int x=0,y=0;
if (!this->Form)
{
return;
}
FIELD* currentField;
cmCursesWidget* currentWidget;
char debugMessage[128];
for(;;)
{
this->UpdateStatusBar();
this->PrintKeys();
if ( this->SearchMode )
{
std::string searchstr = "Search: " + this->SearchString;
this->UpdateStatusBar( searchstr.c_str() );
this->PrintKeys(1);
curses_move(y-5,static_cast<unsigned int>(searchstr.size()));
//curses_move(1,1);
touchwin(stdscr);
refresh();
}
int key = getch();
getmaxyx(stdscr, y, x);
// If window too small, handle 'q' only
if ( x < cmCursesMainForm::MIN_WIDTH ||
y < cmCursesMainForm::MIN_HEIGHT )
{
// quit
if ( key == 'q' )
{
break;
}
else
{
continue;
}
}
currentField = current_field(this->Form);
currentWidget = reinterpret_cast<cmCursesWidget*>(field_userptr(
currentField));
bool widgetHandled=false;
if ( this->SearchMode )
{
if ( key == 10 || key == KEY_ENTER )
{
this->SearchMode = false;
if ( this->SearchString.size() > 0 )
{
this->JumpToCacheEntry(this->SearchString.c_str());
this->OldSearchString = this->SearchString;
}
this->SearchString = "";
}
/*
else if ( key == KEY_ESCAPE )
{
this->SearchMode = false;
}
*/
else if ((key >= 'a' && key <= 'z') ||
(key >= 'A' && key <= 'Z') ||
(key >= '0' && key <= '9') ||
(key == '_' ))
{
if ( this->SearchString.size() < static_cast<std::string::size_type>(x-10) )
{
this->SearchString += static_cast<char>(key);
}
}
else if ( key == ctrl('h') || key == KEY_BACKSPACE || key == KEY_DC )
{
if ( this->SearchString.size() > 0 )
{
this->SearchString.resize(this->SearchString.size()-1);
}
}
}
else if (currentWidget && !this->SearchMode)
{
// Ask the current widget if it wants to handle input
widgetHandled = currentWidget->HandleInput(key, this, stdscr);
if (widgetHandled)
{
this->OkToGenerate = false;
this->UpdateStatusBar();
this->PrintKeys();
}
}
if ((!currentWidget || !widgetHandled) && !this->SearchMode)
{
// If the current widget does not want to handle input,
// we handle it.
sprintf(debugMessage, "Main form handling input, key: %d", key);
cmCursesForm::LogMessage(debugMessage);
// quit
if ( key == 'q' )
{
break;
}
// if not end of page, next field otherwise next page
// each entry consists of fields: label, isnew, value
// therefore, the label field for the prev. entry is index-5
// and the label field for the next entry is index+1
// (index always corresponds to the value field)
else if ( key == KEY_DOWN || key == ctrl('n') )
{
FIELD* cur = current_field(this->Form);
size_t findex = field_index(cur);
if ( findex == 3*this->NumberOfVisibleEntries-1 )
{
continue;
}
if (new_page(this->Fields[findex+1]))
{
form_driver(this->Form, REQ_NEXT_PAGE);
}
else
{
form_driver(this->Form, REQ_NEXT_FIELD);
}
}
// if not beginning of page, previous field, otherwise previous page
// each entry consists of fields: label, isnew, value
// therefore, the label field for the prev. entry is index-5
// and the label field for the next entry is index+1
// (index always corresponds to the value field)
else if ( key == KEY_UP || key == ctrl('p') )
{
FIELD* cur = current_field(this->Form);
int findex = field_index(cur);
if ( findex == 2 )
{
continue;
}
if ( new_page(this->Fields[findex-2]) )
{
form_driver(this->Form, REQ_PREV_PAGE);
set_current_field(this->Form, this->Fields[findex-3]);
}
else
{
form_driver(this->Form, REQ_PREV_FIELD);
}
}
// pg down
else if ( key == KEY_NPAGE || key == ctrl('d') )
{
form_driver(this->Form, REQ_NEXT_PAGE);
}
// pg up
else if ( key == KEY_PPAGE || key == ctrl('u') )
{
form_driver(this->Form, REQ_PREV_PAGE);
}
// configure
else if ( key == 'c' )
{
this->Configure();
}
// display help
else if ( key == 'h' )
{
getmaxyx(stdscr, y, x);
FIELD* cur = current_field(this->Form);
int findex = field_index(cur);
cmCursesWidget* lbl = reinterpret_cast<cmCursesWidget*>(field_userptr(
this->Fields[findex-2]));
const char* curField = lbl->GetValue();
const char* helpString=0;
cmCacheManager::CacheIterator it =
this->CMakeInstance->GetCacheManager()->GetCacheIterator(curField);
if (!it.IsAtEnd())
{
helpString = it.GetProperty("HELPSTRING");
}
if (helpString)
{
char* message = new char[strlen(curField)+strlen(helpString)
+strlen("Current option is: \n Help string for this option is: \n")+10];
sprintf(message,"Current option is: %s\nHelp string for this option is: %s\n", curField, helpString);
this->HelpMessage[1] = message;
delete[] message;
}
else
{
this->HelpMessage[1] = "";
}
cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(this->HelpMessage,
"Help.");
CurrentForm = msgs;
msgs->Render(1,1,x,y);
msgs->HandleInput();
CurrentForm = this;
this->Render(1,1,x,y);
set_current_field(this->Form, cur);
}
// display last errors
else if ( key == 'l' )
{
getmaxyx(stdscr, y, x);
cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm(this->Errors,
"Errors occurred during the last pass.");
CurrentForm = msgs;
msgs->Render(1,1,x,y);
msgs->HandleInput();
CurrentForm = this;
this->Render(1,1,x,y);
}
else if ( key == '/' )
{
this->SearchMode = true;
this->UpdateStatusBar("Search");
this->PrintKeys(1);
touchwin(stdscr);
refresh();
}
else if ( key == 'n' )
{
if ( this->OldSearchString.size() > 0 )
{
this->JumpToCacheEntry(this->OldSearchString.c_str());
}
}
// switch advanced on/off
else if ( key == 't' )
{
if (this->AdvancedMode)
{
this->AdvancedMode = false;
}
else
{
this->AdvancedMode = true;
}
getmaxyx(stdscr, y, x);
this->RePost();
this->Render(1, 1, x, y);
}
// generate and exit
else if ( key == 'g' )
{
if ( this->OkToGenerate )
{
this->Generate();
break;
}
}
// delete cache entry
else if ( key == 'd' && this->NumberOfVisibleEntries )
{
this->OkToGenerate = false;
FIELD* cur = current_field(this->Form);
size_t findex = field_index(cur);
// make the next or prev. current field after deletion
// each entry consists of fields: label, isnew, value
// therefore, the label field for the prev. entry is findex-5
// and the label field for the next entry is findex+1
// (findex always corresponds to the value field)
FIELD* nextCur;
if ( findex == 2 )
{
nextCur=0;
}
else if ( findex == 3*this->NumberOfVisibleEntries-1 )
{
nextCur = this->Fields[findex-5];
}
else
{
nextCur = this->Fields[findex+1];
}
// Get the label widget
// each entry consists of fields: label, isnew, value
// therefore, the label field for the is findex-2
// (findex always corresponds to the value field)
cmCursesWidget* lbl
= reinterpret_cast<cmCursesWidget*>(
field_userptr(this->Fields[findex-2]));
if ( lbl )
{
this->CMakeInstance->GetCacheManager()->RemoveCacheEntry(lbl->GetValue());
std::string nextVal;
if (nextCur)
{
nextVal = (reinterpret_cast<cmCursesWidget*>(field_userptr(nextCur))->GetValue());
}
getmaxyx(stdscr, y, x);
this->RemoveEntry(lbl->GetValue());
this->RePost();
this->Render(1, 1, x, y);
if (nextCur)
{
// make the next or prev. current field after deletion
nextCur = 0;
std::vector<cmCursesCacheEntryComposite*>::iterator it;
for (it = this->Entries->begin(); it != this->Entries->end(); ++it)
{
if (nextVal == (*it)->Key)
{
nextCur = (*it)->Entry->Field;
}
}
if (nextCur)
{
set_current_field(this->Form, nextCur);
}
}
}
}
}
touchwin(stdscr);
wrefresh(stdscr);
}
}
int cmCursesMainForm::LoadCache(const char *)
{
int r = this->CMakeInstance->LoadCache();
if(r < 0)
{
return r;
}
this->CMakeInstance->SetCacheArgs(this->Args);
this->CMakeInstance->PreLoadCMakeFiles();
return r;
}
void cmCursesMainForm::JumpToCacheEntry(const char* astr)
{
std::string str;
if ( astr )
{
str = cmSystemTools::LowerCase(astr);
}
if(str.empty())
{
return;
}
FIELD* cur = current_field(this->Form);
int start_index = field_index(cur);
int findex = start_index;
for(;;)
{
if ( str.size() > 0 )
{
cmCursesWidget* lbl = 0;
if ( findex >= 0 )
{
lbl = reinterpret_cast<cmCursesWidget*>(field_userptr(this->Fields[findex-2]));
}
if ( lbl )
{
const char* curField = lbl->GetValue();
if ( curField )
{
std::string cfld = cmSystemTools::LowerCase(curField);
if ( cfld.find(str) != cfld.npos && findex != start_index )
{
break;
}
}
}
}
if ( size_t(findex) >= 3* this->NumberOfVisibleEntries-1 )
{
set_current_field(this->Form, this->Fields[2]);
}
else if (new_page(this->Fields[findex+1]))
{
form_driver(this->Form, REQ_NEXT_PAGE);
}
else
{
form_driver(this->Form, REQ_NEXT_FIELD);
}
/*
char buffer[1024];
sprintf(buffer, "Line: %d != %d / %d\n", findex, idx, this->NumberOfVisibleEntries);
touchwin(stdscr);
refresh();
this->UpdateStatusBar( buffer );
usleep(100000);
*/
cur = current_field(this->Form);
findex = field_index(cur);
if ( findex == start_index )
{
break;
}
}
}
const char* cmCursesMainForm::s_ConstHelpMessage =
"CMake is used to configure and generate build files for software projects. "
"The basic steps for configuring a project with ccmake are as follows:\n\n"
"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"
"2. When ccmake is run, it will read the configuration files and display the current build options. "
"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 *. "
"On the other hand, the first time you run ccmake, all build options will be new and will be marked as such. "
"At this point, you can modify any options (see keys below) you want to change. "
"When you are satisfied with your changes, press 'c' to have CMake process the configuration files. "
"Please note that changing some options may cause new ones to appear. These will be shown on top and will be marked with *. "
"Repeat this procedure until you are satisfied with all the options and there are no new entries. "
"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. "
"At any point during the process, you can exit ccmake with 'q'. However, this will not generate/change any build files.\n\n"
"ccmake KEYS:\n\n"
"Navigation: "
"You can use the arrow keys and page up, down to navigate the options. Alternatively, you can use the following keys: \n"
" C-n : next option\n"
" C-p : previous options\n"
" C-d : down one page\n"
" C-u : up one page\n\n"
"Editing options: "
"To change an option press enter or return. If the current options is a boolean, this will toggle it's value. "
"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"
" C-b : back one character\n"
" C-f : forward one character\n"
" C-a : go to the beginning of the field\n"
" C-e : go to the end of the field\n"
" C-d : delete previous character\n"
" C-k : kill the rest of the field\n"
" Esc : Restore field (discard last changes)\n"
" Enter : Leave edit mode\n"
"You can also delete an option by pressing 'd'\n\n"
"Commands:\n"
" q : quit ccmake without generating build files\n"
" h : help, shows this screen\n"
" c : process the configuration files with the current options\n"
" g : generate build files and exit, only available when there are no "
"new options and no errors have been detected during last configuration.\n"
" l : shows last errors\n"
" 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";