Files
CMake/Source/CursesDialog/cmCursesLongMessageForm.cxx
Sylvain Joubert 60bfaa8fe6 ccmake: Use incremental rendering for the logs
This should avoid an exponential slowdown in the display time for
projects with lots of output.
This is still slower than cmake due to the ncurses drawing, but it should
now be O(L) in total and not O(L^2) wrt to output length.

Fixes: #20535
2020-04-08 14:40:09 -04:00

200 lines
4.6 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmCursesLongMessageForm.h"
#include <cstdio>
#include <cstring>
#include "cmCursesForm.h"
#include "cmCursesMainForm.h"
#include "cmCursesStandardIncludes.h"
#include "cmStringAlgorithms.h"
#include "cmVersion.h"
inline int ctrl(int z)
{
return (z & 037);
}
cmCursesLongMessageForm::cmCursesLongMessageForm(
std::vector<std::string> const& messages, const char* title,
ScrollBehavior scrollBehavior)
: Scrolling(scrollBehavior)
{
// Append all messages into on big string
this->Messages = cmJoin(messages, "\n");
this->Title = title;
this->Fields[0] = nullptr;
this->Fields[1] = nullptr;
}
cmCursesLongMessageForm::~cmCursesLongMessageForm()
{
if (this->Fields[0]) {
free_field(this->Fields[0]);
}
}
void cmCursesLongMessageForm::UpdateContent(std::string const& output,
std::string const& title)
{
this->Title = title;
if (!output.empty() && this->Messages.size() < MAX_CONTENT_SIZE) {
this->Messages.append("\n" + output);
form_driver(this->Form, REQ_NEW_LINE);
this->DrawMessage(output.c_str());
}
this->UpdateStatusBar();
touchwin(stdscr);
refresh();
}
void cmCursesLongMessageForm::UpdateStatusBar()
{
int x;
int y;
getmaxyx(stdscr, y, x);
char bar[cmCursesMainForm::MAX_WIDTH];
size_t size = this->Title.size();
if (size >= cmCursesMainForm::MAX_WIDTH) {
size = cmCursesMainForm::MAX_WIDTH - 1;
}
strncpy(bar, this->Title.c_str(), size);
for (size_t i = size; i < cmCursesMainForm::MAX_WIDTH; i++) {
bar[i] = ' ';
}
int width;
if (x < cmCursesMainForm::MAX_WIDTH) {
width = x;
} else {
width = cmCursesMainForm::MAX_WIDTH - 1;
}
bar[width] = '\0';
char version[cmCursesMainForm::MAX_WIDTH];
char vertmp[128];
sprintf(vertmp, "CMake Version %s", cmVersion::GetCMakeVersion());
size_t sideSpace = (width - strlen(vertmp));
for (size_t i = 0; i < sideSpace; i++) {
version[i] = ' ';
}
sprintf(version + sideSpace, "%s", vertmp);
version[width] = '\0';
char fmt_s[] = "%s";
curses_move(y - 4, 0);
attron(A_STANDOUT);
printw(fmt_s, bar);
attroff(A_STANDOUT);
curses_move(y - 3, 0);
printw(fmt_s, version);
pos_form_cursor(this->Form);
}
void cmCursesLongMessageForm::PrintKeys()
{
int x;
int y;
getmaxyx(stdscr, y, x);
if (x < cmCursesMainForm::MIN_WIDTH || y < cmCursesMainForm::MIN_HEIGHT) {
return;
}
char firstLine[512];
sprintf(firstLine, "Press [e] to exit screen");
char fmt_s[] = "%s";
curses_move(y - 2, 0);
printw(fmt_s, firstLine);
pos_form_cursor(this->Form);
}
void cmCursesLongMessageForm::Render(int /*left*/, int /*top*/, int /*width*/,
int /*height*/)
{
int x;
int y;
getmaxyx(stdscr, y, x);
if (this->Form) {
unpost_form(this->Form);
free_form(this->Form);
this->Form = nullptr;
}
if (this->Fields[0]) {
free_field(this->Fields[0]);
this->Fields[0] = nullptr;
}
this->Fields[0] = new_field(y - 6, x - 2, 1, 1, 0, 0);
field_opts_off(this->Fields[0], O_STATIC);
this->Form = new_form(this->Fields);
post_form(this->Form);
form_driver(this->Form, REQ_BEG_FIELD);
this->DrawMessage(this->Messages.c_str());
this->UpdateStatusBar();
touchwin(stdscr);
refresh();
}
void cmCursesLongMessageForm::DrawMessage(const char* msg) const
{
int i = 0;
while (msg[i] != '\0' && i < MAX_CONTENT_SIZE) {
if (msg[i] == '\n' && msg[i + 1] != '\0') {
form_driver(this->Form, REQ_NEW_LINE);
} else {
form_driver(this->Form, msg[i]);
}
i++;
}
if (this->Scrolling == ScrollBehavior::ScrollDown) {
form_driver(this->Form, REQ_END_FIELD);
} else {
form_driver(this->Form, REQ_BEG_FIELD);
}
}
void cmCursesLongMessageForm::HandleInput()
{
if (!this->Form) {
return;
}
char debugMessage[128];
for (;;) {
this->PrintKeys();
int key = getch();
sprintf(debugMessage, "Message widget handling input, key: %d", key);
cmCursesForm::LogMessage(debugMessage);
// quit
if (key == 'o' || key == 'e') {
break;
}
if (key == KEY_DOWN || key == ctrl('n')) {
form_driver(this->Form, REQ_SCR_FLINE);
} else if (key == KEY_UP || key == ctrl('p')) {
form_driver(this->Form, REQ_SCR_BLINE);
} else if (key == KEY_NPAGE || key == ctrl('d')) {
form_driver(this->Form, REQ_SCR_FPAGE);
} else if (key == KEY_PPAGE || key == ctrl('u')) {
form_driver(this->Form, REQ_SCR_BPAGE);
}
this->UpdateStatusBar();
touchwin(stdscr);
wrefresh(stdscr);
}
}