diff options
Diffstat (limited to 'Source/CursesDialog')
-rw-r--r-- | Source/CursesDialog/CMakeLists.txt | 25 | ||||
-rw-r--r-- | Source/CursesDialog/ccmake.cxx | 24 | ||||
-rw-r--r-- | Source/CursesDialog/cmCursesBoolWidget.cxx | 15 | ||||
-rw-r--r-- | Source/CursesDialog/cmCursesColor.cxx | 29 | ||||
-rw-r--r-- | Source/CursesDialog/cmCursesColor.h | 24 | ||||
-rw-r--r-- | Source/CursesDialog/cmCursesLongMessageForm.cxx | 26 | ||||
-rw-r--r-- | Source/CursesDialog/cmCursesLongMessageForm.h | 9 | ||||
-rw-r--r-- | Source/CursesDialog/cmCursesMainForm.cxx | 271 | ||||
-rw-r--r-- | Source/CursesDialog/cmCursesMainForm.h | 25 | ||||
-rw-r--r-- | Source/CursesDialog/cmCursesOptionsWidget.cxx | 10 | ||||
-rw-r--r-- | Source/CursesDialog/cmCursesPathWidget.cxx | 8 | ||||
-rw-r--r-- | Source/CursesDialog/cmCursesStringWidget.cxx | 10 |
12 files changed, 307 insertions, 169 deletions
diff --git a/Source/CursesDialog/CMakeLists.txt b/Source/CursesDialog/CMakeLists.txt index 7009717..c24ee76 100644 --- a/Source/CursesDialog/CMakeLists.txt +++ b/Source/CursesDialog/CMakeLists.txt @@ -5,6 +5,7 @@ add_executable(ccmake ccmake.cxx cmCursesBoolWidget.cxx cmCursesCacheEntryComposite.cxx + cmCursesColor.cxx cmCursesDummyWidget.cxx cmCursesFilePathWidget.cxx cmCursesForm.cxx @@ -17,21 +18,45 @@ add_executable(ccmake cmCursesWidget.cxx ) target_include_directories(ccmake PRIVATE ${CURSES_INCLUDE_PATH}) +set(CMAKE_REQUIRED_INCLUDES ${CURSES_INCLUDE_PATH}) target_link_libraries(ccmake CMakeLib) if(CMAKE_USE_SYSTEM_FORM) find_path(CURSES_FORM_INCLUDE_DIR NAMES form.h HINTS ${CURSES_INCLUDE_PATH} ${CURSES_INCLUDE_PATH}/ncurses) if(CURSES_FORM_INCLUDE_DIR) target_include_directories(ccmake PRIVATE ${CURSES_FORM_INCLUDE_DIR}) + list(APPEND CMAKE_REQUIRED_INCLUDES ${CURSES_FORM_INCLUDE_DIR}) endif() target_link_libraries(ccmake ${CURSES_FORM_LIBRARY} ${CURSES_LIBRARY} ) + set(CMAKE_REQUIRED_LIBRARIES + ${CURSES_FORM_LIBRARY} + ${CURSES_LIBRARY} + ) if(CURSES_EXTRA_LIBRARY) target_link_libraries(ccmake ${CURSES_EXTRA_LIBRARY}) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${CURSES_EXTRA_LIBRARY}) endif() else() target_link_libraries(ccmake cmForm) + get_target_property(cmFormIncludeDirs cmForm INTERFACE_INCLUDE_DIRECTORIES) + list(APPEND CMAKE_REQUIRED_INCLUDES ${cmFormIncludeDirs}) + get_target_property(cmFormLibraries cmForm INTERFACE_LINK_LIBRARIES) + set(CMAKE_REQUIRED_LIBRARIES ${cmFormLibraries}) +endif() + +include(CheckSymbolExists) +check_symbol_exists(use_default_colors + "form.h" + HAVE_CURSES_USE_DEFAULT_COLORS) +if(HAVE_CURSES_USE_DEFAULT_COLORS) + set_source_files_properties(cmCursesColor.cxx + PROPERTIES COMPILE_DEFINITIONS HAVE_CURSES_USE_DEFAULT_COLORS) +endif() + +if(CMake_JOB_POOL_LINK_BIN) + set_property(TARGET ccmake PROPERTY JOB_POOL_LINK "link-bin") endif() CMake_OPTIONAL_COMPONENT(ccmake) diff --git a/Source/CursesDialog/ccmake.cxx b/Source/CursesDialog/ccmake.cxx index 9e9dfbd..01fce85 100644 --- a/Source/CursesDialog/ccmake.cxx +++ b/Source/CursesDialog/ccmake.cxx @@ -9,6 +9,7 @@ #include "cmsys/Encoding.hxx" +#include "cmCursesColor.h" #include "cmCursesForm.h" #include "cmCursesMainForm.h" #include "cmCursesStandardIncludes.h" @@ -126,6 +127,7 @@ int main(int argc, char const* const* argv) noecho(); /* Echo off */ cbreak(); /* nl- or cr not needed */ keypad(stdscr, true); /* Use key symbols as KEY_DOWN */ + cmCursesColor::InitColors(); signal(SIGWINCH, onsig); @@ -153,10 +155,28 @@ int main(int argc, char const* const* argv) return 1; } + /* + * The message is stored in a list by the form which will be + * joined by '\n' before display. + * Removing any trailing '\n' avoid extra empty lines in the final results + */ + auto cleanMessage = [](const std::string& message) -> std::string { + auto msg = message; + if (!msg.empty() && msg.back() == '\n') { + msg.pop_back(); + } + return msg; + }; cmSystemTools::SetMessageCallback( - [myform](const std::string& message, const char* title) { - myform->AddError(message, title); + [&](const std::string& message, const char* title) { + myform->AddError(cleanMessage(message), title); }); + cmSystemTools::SetStderrCallback([&](const std::string& message) { + myform->AddError(cleanMessage(message), ""); + }); + cmSystemTools::SetStdoutCallback([&](const std::string& message) { + myform->UpdateProgress(cleanMessage(message), -1); + }); cmCursesForm::CurrentForm = myform; diff --git a/Source/CursesDialog/cmCursesBoolWidget.cxx b/Source/CursesDialog/cmCursesBoolWidget.cxx index 97b0811..c4dbed8 100644 --- a/Source/CursesDialog/cmCursesBoolWidget.cxx +++ b/Source/CursesDialog/cmCursesBoolWidget.cxx @@ -4,6 +4,7 @@ #include <string> +#include "cmCursesColor.h" #include "cmCursesWidget.h" #include "cmStateTypes.h" @@ -12,8 +13,10 @@ cmCursesBoolWidget::cmCursesBoolWidget(int width, int height, int left, : cmCursesWidget(width, height, left, top) { this->Type = cmStateEnums::BOOL; - set_field_fore(this->Field, A_NORMAL); - set_field_back(this->Field, A_STANDOUT); + if (!cmCursesColor::HasColors()) { + set_field_fore(this->Field, A_NORMAL); + set_field_back(this->Field, A_STANDOUT); + } field_opts_off(this->Field, O_STATIC); this->SetValueAsBool(false); } @@ -42,8 +45,16 @@ void cmCursesBoolWidget::SetValueAsBool(bool value) { if (value) { this->SetValue("ON"); + if (cmCursesColor::HasColors()) { + set_field_fore(this->Field, COLOR_PAIR(cmCursesColor::BoolOn)); + set_field_back(this->Field, COLOR_PAIR(cmCursesColor::BoolOn)); + } } else { this->SetValue("OFF"); + if (cmCursesColor::HasColors()) { + set_field_fore(this->Field, COLOR_PAIR(cmCursesColor::BoolOff)); + set_field_back(this->Field, COLOR_PAIR(cmCursesColor::BoolOff)); + } } } diff --git a/Source/CursesDialog/cmCursesColor.cxx b/Source/CursesDialog/cmCursesColor.cxx new file mode 100644 index 0000000..641d48c --- /dev/null +++ b/Source/CursesDialog/cmCursesColor.cxx @@ -0,0 +1,29 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmCursesColor.h" + +#include "cmCursesStandardIncludes.h" + +bool cmCursesColor::HasColors() +{ +#ifdef HAVE_CURSES_USE_DEFAULT_COLORS + return has_colors(); +#else + return false; +#endif +} + +void cmCursesColor::InitColors() +{ +#ifdef HAVE_CURSES_USE_DEFAULT_COLORS + if (HasColors()) { + start_color(); + use_default_colors(); + init_pair(cmCursesColor::BoolOff, COLOR_RED, -1); + init_pair(cmCursesColor::BoolOn, COLOR_GREEN, -1); + init_pair(cmCursesColor::String, COLOR_BLUE, -1); + init_pair(cmCursesColor::Path, COLOR_YELLOW, -1); + init_pair(cmCursesColor::Options, COLOR_MAGENTA, -1); + } +#endif +} diff --git a/Source/CursesDialog/cmCursesColor.h b/Source/CursesDialog/cmCursesColor.h new file mode 100644 index 0000000..78ca52c --- /dev/null +++ b/Source/CursesDialog/cmCursesColor.h @@ -0,0 +1,24 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmCursesColor_h +#define cmCursesColor_h + +class cmCursesColor +{ +public: + enum Color + { + // Default color is pair 0 + BoolOff = 1, + BoolOn, + String, + Path, + Options + }; + + static bool HasColors(); + + static void InitColors(); +}; + +#endif // cmCursesColor_h diff --git a/Source/CursesDialog/cmCursesLongMessageForm.cxx b/Source/CursesDialog/cmCursesLongMessageForm.cxx index e2d8d06..806e663 100644 --- a/Source/CursesDialog/cmCursesLongMessageForm.cxx +++ b/Source/CursesDialog/cmCursesLongMessageForm.cxx @@ -8,6 +8,7 @@ #include "cmCursesForm.h" #include "cmCursesMainForm.h" #include "cmCursesStandardIncludes.h" +#include "cmStringAlgorithms.h" #include "cmVersion.h" inline int ctrl(int z) @@ -16,14 +17,12 @@ inline int ctrl(int z) } cmCursesLongMessageForm::cmCursesLongMessageForm( - std::vector<std::string> const& messages, const char* title) + std::vector<std::string> const& messages, const char* title, + ScrollBehavior scrollBehavior) + : Scrolling(scrollBehavior) { // Append all messages into on big string - for (std::string const& message : messages) { - this->Messages += message; - // Add one blank line after each message - this->Messages += "\n\n"; - } + this->Messages = cmJoin(messages, "\n"); this->Title = title; this->Fields[0] = nullptr; this->Fields[1] = nullptr; @@ -48,7 +47,7 @@ void cmCursesLongMessageForm::UpdateStatusBar() size = cmCursesMainForm::MAX_WIDTH - 1; } strncpy(bar, this->Title.c_str(), size); - for (size_t i = size - 1; i < cmCursesMainForm::MAX_WIDTH; i++) { + for (size_t i = size; i < cmCursesMainForm::MAX_WIDTH; i++) { bar[i] = ' '; } int width; @@ -89,7 +88,7 @@ void cmCursesLongMessageForm::PrintKeys() return; } char firstLine[512]; - sprintf(firstLine, "Press [e] to exit help"); + sprintf(firstLine, "Press [e] to exit screen"); char fmt_s[] = "%s"; curses_move(y - 2, 0); @@ -112,8 +111,6 @@ void cmCursesLongMessageForm::Render(int /*left*/, int /*top*/, int /*width*/, const char* msg = this->Messages.c_str(); - curses_clear(); - if (this->Fields[0]) { free_field(this->Fields[0]); this->Fields[0] = nullptr; @@ -136,10 +133,13 @@ void cmCursesLongMessageForm::Render(int /*left*/, int /*top*/, int /*width*/, } i++; } - form_driver(this->Form, REQ_BEG_FIELD); + if (this->Scrolling == ScrollBehavior::ScrollDown) { + form_driver(this->Form, REQ_END_FIELD); + } else { + form_driver(this->Form, REQ_BEG_FIELD); + } this->UpdateStatusBar(); - this->PrintKeys(); touchwin(stdscr); refresh(); } @@ -153,6 +153,7 @@ void cmCursesLongMessageForm::HandleInput() char debugMessage[128]; for (;;) { + this->PrintKeys(); int key = getch(); sprintf(debugMessage, "Message widget handling input, key: %d", key); @@ -173,7 +174,6 @@ void cmCursesLongMessageForm::HandleInput() } this->UpdateStatusBar(); - this->PrintKeys(); touchwin(stdscr); wrefresh(stdscr); } diff --git a/Source/CursesDialog/cmCursesLongMessageForm.h b/Source/CursesDialog/cmCursesLongMessageForm.h index 42f9c71..88efe62 100644 --- a/Source/CursesDialog/cmCursesLongMessageForm.h +++ b/Source/CursesDialog/cmCursesLongMessageForm.h @@ -14,8 +14,14 @@ class cmCursesLongMessageForm : public cmCursesForm { public: + enum class ScrollBehavior + { + NoScroll, + ScrollDown + }; + cmCursesLongMessageForm(std::vector<std::string> const& messages, - const char* title); + const char* title, ScrollBehavior scrollBehavior); ~cmCursesLongMessageForm() override; cmCursesLongMessageForm(cmCursesLongMessageForm const&) = delete; @@ -43,6 +49,7 @@ public: protected: std::string Messages; std::string Title; + ScrollBehavior Scrolling; FIELD* Fields[2]; }; diff --git a/Source/CursesDialog/cmCursesMainForm.cxx b/Source/CursesDialog/cmCursesMainForm.cxx index 6b71e8a..2c92835 100644 --- a/Source/CursesDialog/cmCursesMainForm.cxx +++ b/Source/CursesDialog/cmCursesMainForm.cxx @@ -34,6 +34,7 @@ cmCursesMainForm::cmCursesMainForm(std::vector<std::string> args, : Args(std::move(args)) , InitialWidth(initWidth) { + this->HasNonStatusOutputs = false; this->NumberOfPages = 0; this->AdvancedMode = false; this->NumberOfVisibleEntries = 0; @@ -321,25 +322,25 @@ void cmCursesMainForm::PrintKeys(int process /* = 0 */) } else { if (this->OkToGenerate) { sprintf(firstLine, - "Press [c] to configure Press [g] to generate and exit"); + " [l] Show log output [c] Configure" + " [g] Generate "); } else { sprintf(firstLine, - "Press [c] to configure "); + " [l] Show log output [c] Configure" + " "); } { const char* toggleKeyInstruction = - "Press [t] to toggle advanced mode (Currently %s)"; + " [t] Toggle advanced mode (currently %s)"; sprintf(thirdLine, toggleKeyInstruction, - this->AdvancedMode ? "On" : "Off"); + this->AdvancedMode ? "on" : "off"); } sprintf(secondLine, - "Press [h] for help " - "Press [q] to quit without generating"); + " [h] Help [q] Quit without generating"); } curses_move(y - 4, 0); - char fmt[512] = - "Press [enter] to edit option Press [d] to delete an entry"; + char fmt[512] = "Keys: [enter] Edit an entry [d] Delete an entry"; if (process) { memset(fmt, ' ', 57); } @@ -364,7 +365,7 @@ void cmCursesMainForm::PrintKeys(int process /* = 0 */) // 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) +void cmCursesMainForm::UpdateStatusBar(cm::optional<std::string> message) { int x; int y; @@ -385,119 +386,91 @@ void cmCursesMainForm::UpdateStatusBar(const char* message) return; } - // Get the key of the current entry - FIELD* cur = current_field(this->Form); - int findex = field_index(cur); - cmCursesWidget* lbl = nullptr; - if (findex >= 0) { - lbl = reinterpret_cast<cmCursesWidget*>( - field_userptr(this->Fields[findex - 2])); - } - char help[128] = ""; - const char* curField = ""; - if (lbl) { - curField = lbl->GetValue(); + // Find the current label index + // Field are grouped by 3, the label should be 2 less than the current index + using size_type = decltype(this->Fields)::size_type; + size_type currentLabelIndex = field_index(current_field(this->Form)) - 2; + + // Use the status message if any, otherwise join the key and help string + std::string bar; + if (message) { + bar = *message; + } else { + // Get the key of the current entry + cmCursesWidget* labelWidget = reinterpret_cast<cmCursesWidget*>( + field_userptr(this->Fields[currentLabelIndex])); + std::string labelValue = labelWidget->GetValue(); + bar = labelValue + ": "; // Get the help string of the current entry // and add it to the help string - const char* existingValue = - this->CMakeInstance->GetState()->GetCacheEntryValue(curField); + auto cmakeState = this->CMakeInstance->GetState(); + const char* existingValue = cmakeState->GetCacheEntryValue(labelValue); if (existingValue) { - const char* hs = this->CMakeInstance->GetState()->GetCacheEntryProperty( - curField, "HELPSTRING"); - if (hs) { - strncpy(help, hs, 127); - help[127] = '\0'; - } else { - help[0] = 0; + auto help = cmakeState->GetCacheEntryProperty(labelValue, "HELPSTRING"); + if (help) { + bar += help; } - } else { - sprintf(help, " "); } } + // Pad with spaces to erase any previous text, + // or truncate as necessary to fit the screen + bar.resize(x, ' '); + curses_move(y - 5, 0); + attron(A_STANDOUT); + char fmt_s[] = "%s"; + printw(fmt_s, bar.c_str()); + attroff(A_STANDOUT); - // Join the key, help string and pad with spaces - // (or truncate) as necessary - char bar[cmCursesMainForm::MAX_WIDTH]; - size_t curFieldLen = strlen(curField); - size_t helpLen = strlen(help); - - size_t width = std::min<size_t>(x, cmCursesMainForm::MAX_WIDTH); - - if (message) { - curField = message; - curFieldLen = strlen(message); - strncpy(bar, curField, width); - if (curFieldLen < width) { - memset(bar + curFieldLen, ' ', width - curFieldLen); - } - } else { - strncpy(bar, curField, width); - if (curFieldLen < width) { - bar[curFieldLen] = ':'; - bar[curFieldLen + 1] = ' '; - strncpy(bar + curFieldLen + 2, help, width - curFieldLen - 2); - if (curFieldLen + helpLen + 2 < width) { - memset(bar + curFieldLen + helpLen + 2, ' ', - width - (curFieldLen + helpLen + 2)); - } - } + // Highlight the current label, reset others + // Fields are grouped by 3, the first one being the label + // so start at 0 and move up by 3 avoiding the last null entry + for (size_type index = 0; index < this->Fields.size() - 1; index += 3) { + bool currentLabel = index == currentLabelIndex; + set_field_fore(this->Fields[index], currentLabel ? A_STANDOUT : A_NORMAL); } - bar[width] = '\0'; - - // Display CMake version info on the next line + // Display CMake version under the status bar // 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)); - memset(version, ' ', sideSpace); - sprintf(version + sideSpace, "%s", vertmp); - version[width] = '\0'; - - // Now print both lines - char fmt_s[] = "%s"; - curses_move(y - 5, 0); - attron(A_STANDOUT); - printw(fmt_s, bar); - attroff(A_STANDOUT); - curses_move(y - 4, 0); - printw(fmt_s, version); + std::string version = "CMake Version "; + version += cmVersion::GetCMakeVersion(); + version.resize(std::min<std::string::size_type>(x, version.size())); + curses_move(y - 4, x - static_cast<int>(version.size())); + printw(fmt_s, version.c_str()); + pos_form_cursor(this->Form); } void cmCursesMainForm::UpdateProgress(const std::string& msg, float prog) { - char tmp[1024]; - const char* cmsg = tmp; if (prog >= 0) { - sprintf(tmp, "%s %i%%", msg.c_str(), static_cast<int>(100 * prog)); + constexpr int progressBarWidth = 40; + int progressBarCompleted = static_cast<int>(progressBarWidth * prog); + int percentCompleted = static_cast<int>(100 * prog); + this->LastProgress = (percentCompleted < 100 ? " " : ""); + this->LastProgress += (percentCompleted < 10 ? " " : ""); + this->LastProgress += std::to_string(percentCompleted) + "% ["; + this->LastProgress.append(progressBarCompleted, '#'); + this->LastProgress.append(progressBarWidth - progressBarCompleted, ' '); + this->LastProgress += "] " + msg + "..."; } else { - cmsg = msg.c_str(); + this->Outputs.emplace_back(msg); } - this->UpdateStatusBar(cmsg); - this->PrintKeys(1); - curses_move(1, 1); - touchwin(stdscr); - refresh(); + + this->DisplayOutputs(); } int cmCursesMainForm::Configure(int noconfigure) { - int xi; - int yi; - getmaxyx(stdscr, yi, xi); - - curses_move(1, 1); - this->UpdateStatusBar("Configuring, please wait..."); - this->PrintKeys(1); - touchwin(stdscr); - refresh(); - this->CMakeInstance->SetProgressCallback( - [this](const std::string& msg, float prog) { - this->UpdateProgress(msg, prog); - }); + this->ResetOutputs(); + + if (noconfigure == 0) { + this->UpdateProgress("Configuring", 0); + this->CMakeInstance->SetProgressCallback( + [this](const std::string& msg, float prog) { + this->UpdateProgress(msg, prog); + }); + } // always save the current gui values to disk this->FillCacheManagerFromUI(); @@ -505,9 +478,6 @@ int cmCursesMainForm::Configure(int noconfigure) this->CMakeInstance->GetHomeOutputDirectory()); this->LoadCache(nullptr); - // Get rid of previous errors - this->Errors = std::vector<std::string>(); - // run the generate process this->OkToGenerate = true; int retVal; @@ -524,7 +494,7 @@ int cmCursesMainForm::Configure(int noconfigure) keypad(stdscr, true); /* Use key symbols as KEY_DOWN */ - if (retVal != 0 || !this->Errors.empty()) { + if (retVal != 0 || this->HasNonStatusOutputs) { // see if there was an error if (cmSystemTools::GetErrorOccuredFlag()) { this->OkToGenerate = false; @@ -532,11 +502,13 @@ int cmCursesMainForm::Configure(int noconfigure) int xx; int yy; getmaxyx(stdscr, yy, xx); - cmCursesLongMessageForm* msgs = - new cmCursesLongMessageForm(this->Errors, - cmSystemTools::GetErrorOccuredFlag() - ? "Errors occurred during the last pass." - : "CMake produced the following output."); + const char* title = "Configure produced the following output"; + if (cmSystemTools::GetErrorOccuredFlag()) { + title = "Configure failed with the following output"; + } + cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm( + this->Outputs, title, + cmCursesLongMessageForm::ScrollBehavior::ScrollDown); // reset error condition cmSystemTools::ResetErrorOccuredFlag(); CurrentForm = msgs; @@ -547,11 +519,13 @@ int cmCursesMainForm::Configure(int noconfigure) if (retVal == -2) { return retVal; } - CurrentForm = this; - this->Render(1, 1, xx, yy); } this->InitializeUI(); + CurrentForm = this; + int xi; + int yi; + getmaxyx(stdscr, yi, xi); this->Render(1, 1, xi, yi); return 0; @@ -559,30 +533,21 @@ int cmCursesMainForm::Configure(int noconfigure) int cmCursesMainForm::Generate() { - int xi; - int yi; - getmaxyx(stdscr, yi, xi); + this->ResetOutputs(); - curses_move(1, 1); - this->UpdateStatusBar("Generating, please wait..."); - this->PrintKeys(1); - touchwin(stdscr); - refresh(); + this->UpdateProgress("Generating", 0); this->CMakeInstance->SetProgressCallback( [this](const std::string& msg, float prog) { this->UpdateProgress(msg, prog); }); - // Get rid of previous errors - this->Errors = std::vector<std::string>(); - // run the generate process int retVal = this->CMakeInstance->Generate(); this->CMakeInstance->SetProgressCallback(nullptr); keypad(stdscr, true); /* Use key symbols as KEY_DOWN */ - if (retVal != 0 || !this->Errors.empty()) { + if (retVal != 0 || this->HasNonStatusOutputs) { // see if there was an error if (cmSystemTools::GetErrorOccuredFlag()) { this->OkToGenerate = false; @@ -592,12 +557,13 @@ int cmCursesMainForm::Generate() int xx; int yy; getmaxyx(stdscr, yy, xx); - const char* title = "Messages during last pass."; + const char* title = "Generate produced the following output"; if (cmSystemTools::GetErrorOccuredFlag()) { - title = "Errors occurred during the last pass."; + title = "Generate failed with the following output"; } - cmCursesLongMessageForm* msgs = - new cmCursesLongMessageForm(this->Errors, title); + cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm( + this->Outputs, title, + cmCursesLongMessageForm::ScrollBehavior::ScrollDown); CurrentForm = msgs; msgs->Render(1, 1, xx, yy); msgs->HandleInput(); @@ -606,11 +572,13 @@ int cmCursesMainForm::Generate() if (retVal == -2) { return retVal; } - CurrentForm = this; - this->Render(1, 1, xx, yy); } this->InitializeUI(); + CurrentForm = this; + int xi; + int yi; + getmaxyx(stdscr, yi, xi); this->Render(1, 1, xi, yi); return 0; @@ -619,7 +587,9 @@ int cmCursesMainForm::Generate() void cmCursesMainForm::AddError(const std::string& message, const char* /*unused*/) { - this->Errors.emplace_back(message); + this->Outputs.emplace_back(message); + this->HasNonStatusOutputs = true; + this->DisplayOutputs(); } void cmCursesMainForm::RemoveEntry(const char* value) @@ -704,7 +674,7 @@ void cmCursesMainForm::HandleInput() this->PrintKeys(); if (this->SearchMode) { std::string searchstr = "Search: " + this->SearchString; - this->UpdateStatusBar(searchstr.c_str()); + this->UpdateStatusBar(searchstr); this->PrintKeys(1); curses_move(y - 5, static_cast<unsigned int>(searchstr.size())); // curses_move(1,1); @@ -848,8 +818,9 @@ void cmCursesMainForm::HandleInput() this->HelpMessage[1] = ""; } - cmCursesLongMessageForm* msgs = - new cmCursesLongMessageForm(this->HelpMessage, "Help."); + cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm( + this->HelpMessage, "Help", + cmCursesLongMessageForm::ScrollBehavior::NoScroll); CurrentForm = msgs; msgs->Render(1, 1, x, y); msgs->HandleInput(); @@ -861,7 +832,8 @@ void cmCursesMainForm::HandleInput() else if (key == 'l') { getmaxyx(stdscr, y, x); cmCursesLongMessageForm* msgs = new cmCursesLongMessageForm( - this->Errors, "Errors occurred during the last pass."); + this->Outputs, "CMake produced the following output", + cmCursesLongMessageForm::ScrollBehavior::NoScroll); CurrentForm = msgs; msgs->Render(1, 1, x, y); msgs->HandleInput(); @@ -1007,15 +979,6 @@ void cmCursesMainForm::JumpToCacheEntry(const char* astr) } 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) { @@ -1024,6 +987,28 @@ void cmCursesMainForm::JumpToCacheEntry(const char* astr) } } +void cmCursesMainForm::ResetOutputs() +{ + this->LogForm.reset(); + this->Outputs.clear(); + this->HasNonStatusOutputs = false; + this->LastProgress.clear(); +} + +void cmCursesMainForm::DisplayOutputs() +{ + int xi; + int yi; + getmaxyx(stdscr, yi, xi); + + auto newLogForm = new cmCursesLongMessageForm( + this->Outputs, this->LastProgress.c_str(), + cmCursesLongMessageForm::ScrollBehavior::ScrollDown); + CurrentForm = newLogForm; + this->LogForm.reset(newLogForm); + this->LogForm->Render(1, 1, xi, yi); +} + 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" @@ -1080,7 +1065,7 @@ const char* cmCursesMainForm::s_ConstHelpMessage = " 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" + " l : shows cmake output\n" " d : delete an option\n" " t : toggles advanced mode. In normal mode, only the most important " "options are shown. In advanced mode, all options are shown. We recommend " diff --git a/Source/CursesDialog/cmCursesMainForm.h b/Source/CursesDialog/cmCursesMainForm.h index b8769b7..b7c204d 100644 --- a/Source/CursesDialog/cmCursesMainForm.h +++ b/Source/CursesDialog/cmCursesMainForm.h @@ -10,12 +10,15 @@ #include <string> #include <vector> +#include <cm/optional> + #include "cmCursesCacheEntryComposite.h" #include "cmCursesForm.h" #include "cmCursesStandardIncludes.h" #include "cmStateTypes.h" class cmake; +class cmCursesLongMessageForm; /** \class cmCursesMainForm * \brief The main page of ccmake @@ -66,8 +69,8 @@ public: * exception is during a resize. The optional argument specifies the * string to be displayed in the status bar. */ - void UpdateStatusBar() override { this->UpdateStatusBar(nullptr); } - virtual void UpdateStatusBar(const char* message); + void UpdateStatusBar() override { this->UpdateStatusBar(cm::nullopt); } + void UpdateStatusBar(cm::optional<std::string> message); /** * Display current commands and their keys on the toolbar. This @@ -122,10 +125,24 @@ protected: // Jump to the cache entry whose name matches the string. void JumpToCacheEntry(const char* str); + // Clear and reset the output log and state + void ResetOutputs(); + + // Display the current progress and output + void DisplayOutputs(); + // Copies of cache entries stored in the user interface std::vector<cmCursesCacheEntryComposite> Entries; - // Errors produced during last run of cmake - std::vector<std::string> Errors; + + // The form used to display logs during processing + std::unique_ptr<cmCursesLongMessageForm> LogForm; + // Output produced by the last pass + std::vector<std::string> Outputs; + // Did the last pass produced outputs of interest (errors, warnings, ...) + bool HasNonStatusOutputs; + // Last progress bar + std::string LastProgress; + // Command line arguments to be passed to cmake each time // it is run std::vector<std::string> Args; diff --git a/Source/CursesDialog/cmCursesOptionsWidget.cxx b/Source/CursesDialog/cmCursesOptionsWidget.cxx index eb773ad..a15241f 100644 --- a/Source/CursesDialog/cmCursesOptionsWidget.cxx +++ b/Source/CursesDialog/cmCursesOptionsWidget.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmCursesOptionsWidget.h" +#include "cmCursesColor.h" #include "cmCursesWidget.h" #include "cmStateTypes.h" @@ -15,8 +16,13 @@ cmCursesOptionsWidget::cmCursesOptionsWidget(int width, int height, int left, // there is no option type, and string type causes ccmake to cast // the widget into a string widget at some point. BOOL is safe for // now. - set_field_fore(this->Field, A_NORMAL); - set_field_back(this->Field, A_STANDOUT); + if (cmCursesColor::HasColors()) { + set_field_fore(this->Field, COLOR_PAIR(cmCursesColor::Options)); + set_field_back(this->Field, COLOR_PAIR(cmCursesColor::Options)); + } else { + set_field_fore(this->Field, A_NORMAL); + set_field_back(this->Field, A_STANDOUT); + } field_opts_off(this->Field, O_STATIC); } diff --git a/Source/CursesDialog/cmCursesPathWidget.cxx b/Source/CursesDialog/cmCursesPathWidget.cxx index bb3808e..8ed42de 100644 --- a/Source/CursesDialog/cmCursesPathWidget.cxx +++ b/Source/CursesDialog/cmCursesPathWidget.cxx @@ -4,6 +4,7 @@ #include <vector> +#include "cmCursesColor.h" #include "cmCursesMainForm.h" #include "cmCursesStringWidget.h" #include "cmStateTypes.h" @@ -16,6 +17,13 @@ cmCursesPathWidget::cmCursesPathWidget(int width, int height, int left, this->Type = cmStateEnums::PATH; this->Cycle = false; this->CurrentIndex = 0; + if (cmCursesColor::HasColors()) { + set_field_fore(this->Field, COLOR_PAIR(cmCursesColor::Path)); + set_field_back(this->Field, COLOR_PAIR(cmCursesColor::Path)); + } else { + set_field_fore(this->Field, A_NORMAL); + set_field_back(this->Field, A_STANDOUT); + } } void cmCursesPathWidget::OnType(int& key, cmCursesMainForm* fm, WINDOW* w) diff --git a/Source/CursesDialog/cmCursesStringWidget.cxx b/Source/CursesDialog/cmCursesStringWidget.cxx index 6296af2..c629478 100644 --- a/Source/CursesDialog/cmCursesStringWidget.cxx +++ b/Source/CursesDialog/cmCursesStringWidget.cxx @@ -4,6 +4,7 @@ #include <cstdio> +#include "cmCursesColor.h" #include "cmCursesForm.h" #include "cmCursesMainForm.h" #include "cmCursesStandardIncludes.h" @@ -21,8 +22,13 @@ cmCursesStringWidget::cmCursesStringWidget(int width, int height, int left, { this->InEdit = false; this->Type = cmStateEnums::STRING; - set_field_fore(this->Field, A_NORMAL); - set_field_back(this->Field, A_STANDOUT); + if (cmCursesColor::HasColors()) { + set_field_fore(this->Field, COLOR_PAIR(cmCursesColor::String)); + set_field_back(this->Field, COLOR_PAIR(cmCursesColor::String)); + } else { + set_field_fore(this->Field, A_NORMAL); + set_field_back(this->Field, A_STANDOUT); + } field_opts_off(this->Field, O_STATIC); } |