|
@@ -0,0 +1,1304 @@
|
|
|
+#include "Console.h"
|
|
|
+
|
|
|
+#include <iostream>
|
|
|
+
|
|
|
+#ifndef WIN32
|
|
|
+# include <termios.h>
|
|
|
+# include <unistd.h>
|
|
|
+#endif
|
|
|
+
|
|
|
+using namespace Framework;
|
|
|
+
|
|
|
+ConsoleCommand::ConsoleCommand(Text name)
|
|
|
+ : ReferenceCounter(),
|
|
|
+ name(name)
|
|
|
+{}
|
|
|
+
|
|
|
+ConsoleCommand::~ConsoleCommand() {}
|
|
|
+
|
|
|
+Text& Framework::ConsoleCommand::getName()
|
|
|
+{
|
|
|
+ return name;
|
|
|
+}
|
|
|
+
|
|
|
+Framework::StickyConsoleContent::StickyConsoleContent()
|
|
|
+ : ReferenceCounter(),
|
|
|
+ length(0),
|
|
|
+ content(0),
|
|
|
+ color(0),
|
|
|
+ backgroundColor(0),
|
|
|
+ zConsoleHandler(0)
|
|
|
+{}
|
|
|
+
|
|
|
+Framework::StickyConsoleContent::~StickyConsoleContent()
|
|
|
+{
|
|
|
+ delete[] content;
|
|
|
+ delete[] color;
|
|
|
+ delete[] backgroundColor;
|
|
|
+}
|
|
|
+
|
|
|
+int Framework::StickyConsoleContent::getLength() const
|
|
|
+{
|
|
|
+ return length;
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::StickyConsoleContent::setContent(
|
|
|
+ int length, const char* content)
|
|
|
+{
|
|
|
+ delete[] this->content;
|
|
|
+ this->content = new char[length];
|
|
|
+ memcpy(this->content, content, length);
|
|
|
+ this->length = length;
|
|
|
+ delete[] color;
|
|
|
+ delete[] backgroundColor;
|
|
|
+ color = 0;
|
|
|
+ backgroundColor = 0;
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::StickyConsoleContent::setContent(
|
|
|
+ int length, const char* content, Color color)
|
|
|
+{
|
|
|
+ setContent(length, content);
|
|
|
+ this->color = new Color[length];
|
|
|
+ for (int i = 0; i < length; i++)
|
|
|
+ {
|
|
|
+ this->color[i] = color;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::StickyConsoleContent::setContent(
|
|
|
+ int length, const char* content, Color color, Color backgroundColor)
|
|
|
+{
|
|
|
+ setContent(length, content, color);
|
|
|
+ this->backgroundColor = new Color[length];
|
|
|
+ for (int i = 0; i < length; i++)
|
|
|
+ {
|
|
|
+ this->backgroundColor[i] = backgroundColor;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::StickyConsoleContent::setContent(
|
|
|
+ int length, const char* content, Color* color)
|
|
|
+{
|
|
|
+ setContent(length, content);
|
|
|
+ this->color = new Color[length];
|
|
|
+ for (int i = 0; i < length; i++)
|
|
|
+ {
|
|
|
+ this->color[i] = color[i];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::StickyConsoleContent::setContent(
|
|
|
+ int length, const char* content, Color* color, Color* backgroundColor)
|
|
|
+{
|
|
|
+ setContent(length, content, color);
|
|
|
+ this->backgroundColor = new Color[length];
|
|
|
+ for (int i = 0; i < length; i++)
|
|
|
+ {
|
|
|
+ this->backgroundColor[i] = backgroundColor[i];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::StickyConsoleContent::repaceContent(
|
|
|
+ int start, int length, int newLength, const char* newContent)
|
|
|
+{
|
|
|
+ int resultLength = this->length + newLength - length;
|
|
|
+ char* resultContent = new char[resultLength];
|
|
|
+ memcpy(resultContent, content, start);
|
|
|
+ memcpy(resultContent + start, newContent, newLength);
|
|
|
+ memcpy(resultContent + start + newLength,
|
|
|
+ content + start + length,
|
|
|
+ this->length - start - length);
|
|
|
+ delete[] content;
|
|
|
+ content = resultContent;
|
|
|
+ if (color)
|
|
|
+ {
|
|
|
+ Color* resultColor = new Color[resultLength];
|
|
|
+ memcpy(resultColor, color, start);
|
|
|
+ for (int i = 0; i < newLength; i++)
|
|
|
+ {
|
|
|
+ resultColor[start + i] = color[start];
|
|
|
+ }
|
|
|
+ memcpy(resultColor + start + newLength,
|
|
|
+ color + start + length,
|
|
|
+ sizeof(Color) * (this->length - start - length));
|
|
|
+ delete[] color;
|
|
|
+ color = resultColor;
|
|
|
+ }
|
|
|
+ if (backgroundColor)
|
|
|
+ {
|
|
|
+ Color* resultBgColor = new Color[resultLength];
|
|
|
+ memcpy(resultBgColor, backgroundColor, start);
|
|
|
+ for (int i = 0; i < newLength; i++)
|
|
|
+ {
|
|
|
+ resultBgColor[start + i] = backgroundColor[start];
|
|
|
+ }
|
|
|
+ memcpy(resultBgColor + start + newLength,
|
|
|
+ backgroundColor + start + length,
|
|
|
+ sizeof(Color) * (this->length - start - length));
|
|
|
+ delete[] backgroundColor;
|
|
|
+ backgroundColor = resultBgColor;
|
|
|
+ }
|
|
|
+ this->length = resultLength;
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::StickyConsoleContent::repaceContent(
|
|
|
+ int start, int length, int newLength, const char* newContent, Color color)
|
|
|
+{
|
|
|
+ repaceContent(start, length, newLength, newContent);
|
|
|
+ if (!this->color)
|
|
|
+ {
|
|
|
+ this->color = new Color[this->length];
|
|
|
+ for (int i = 0; i < this->length; i++)
|
|
|
+ {
|
|
|
+ this->color[i] = (Color)-1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (int i = start; i < start + newLength; i++)
|
|
|
+ {
|
|
|
+ this->color[i] = color;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::StickyConsoleContent::repaceContent(int start,
|
|
|
+ int length,
|
|
|
+ int newLength,
|
|
|
+ const char* newContent,
|
|
|
+ Color color,
|
|
|
+ Color backgroundColor)
|
|
|
+{
|
|
|
+ repaceContent(start, length, newLength, newContent, color);
|
|
|
+ if (!this->backgroundColor)
|
|
|
+ {
|
|
|
+ this->backgroundColor = new Color[this->length];
|
|
|
+ for (int i = 0; i < this->length; i++)
|
|
|
+ {
|
|
|
+ this->backgroundColor[i] = (Color)-1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (int i = start; i < start + newLength; i++)
|
|
|
+ {
|
|
|
+ this->backgroundColor[i] = backgroundColor;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::StickyConsoleContent::repaceContent(
|
|
|
+ int start, int length, int newLength, const char* newContent, Color* color)
|
|
|
+{
|
|
|
+ repaceContent(start, length, newLength, newContent);
|
|
|
+ if (!this->color)
|
|
|
+ {
|
|
|
+ this->color = new Color[this->length];
|
|
|
+ for (int i = 0; i < this->length; i++)
|
|
|
+ {
|
|
|
+ this->color[i] = (Color)-1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ memcpy(this->color + start, color, sizeof(Color) * newLength);
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::StickyConsoleContent::repaceContent(int start,
|
|
|
+ int length,
|
|
|
+ int newLength,
|
|
|
+ const char* newContent,
|
|
|
+ Color* color,
|
|
|
+ Color* backgroundColor)
|
|
|
+{
|
|
|
+ repaceContent(start, length, newLength, newContent, color);
|
|
|
+ if (!this->backgroundColor)
|
|
|
+ {
|
|
|
+ this->backgroundColor = new Color[this->length];
|
|
|
+ for (int i = 0; i < this->length; i++)
|
|
|
+ {
|
|
|
+ this->backgroundColor[i] = (Color)-1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ memcpy(this->backgroundColor + start,
|
|
|
+ backgroundColor,
|
|
|
+ sizeof(Color) * newLength);
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::StickyConsoleContent::triggerUpdate()
|
|
|
+{
|
|
|
+ if (zConsoleHandler != 0)
|
|
|
+ {
|
|
|
+ zConsoleHandler->print();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+bool Framework::StickyConsoleContent::isInput()
|
|
|
+{
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::StickyConsoleContent::setConsoleHandlerZ(
|
|
|
+ ConsoleHandler* zConsoleHandler)
|
|
|
+{
|
|
|
+ this->zConsoleHandler = zConsoleHandler;
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::StickyConsoleContent::setCursorToBeginning() {}
|
|
|
+
|
|
|
+int Framework::StickyConsoleContent::print() const
|
|
|
+{
|
|
|
+ int lineCount = 0;
|
|
|
+ int maxWidth = zConsoleHandler->getWidth();
|
|
|
+ std::cout << "\r\33[0K"; // erase current line
|
|
|
+ int lineLength = 0;
|
|
|
+ int lastColor = -1;
|
|
|
+ int lastBgColor = -1;
|
|
|
+ for (int i = 0; i < length; i++)
|
|
|
+ {
|
|
|
+ if (color && (int)color[i] != lastColor)
|
|
|
+ {
|
|
|
+ if ((int)color[i] == -1)
|
|
|
+ {
|
|
|
+ std::cout << "\033[0m";
|
|
|
+ if (backgroundColor && (int)backgroundColor[i] != -1)
|
|
|
+ {
|
|
|
+ if (backgroundColor[i] < Color::LIGHT_Black)
|
|
|
+ {
|
|
|
+ std::cout << Text("\033[1;")
|
|
|
+ + Text(40 + (int)backgroundColor[i])
|
|
|
+ + "m";
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ std::cout << Text("\033[1;")
|
|
|
+ + Text(100 + (int)backgroundColor[i])
|
|
|
+ + "m";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (color[i] < Color::LIGHT_Black)
|
|
|
+ {
|
|
|
+ std::cout << Text("\033[1;") + Text(30 + (int)color[i]) + "m";
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ std::cout << Text("\033[1;") + Text(90 + (int)color[i]) + "m";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (backgroundColor && (int)backgroundColor[i] != lastBgColor)
|
|
|
+ {
|
|
|
+ if ((int)backgroundColor[i] == -1)
|
|
|
+ {
|
|
|
+ std::cout << "\033[0m";
|
|
|
+ if (color && (int)color[i] != -1)
|
|
|
+ {
|
|
|
+ if (color[i] < Color::LIGHT_Black)
|
|
|
+ {
|
|
|
+ std::cout
|
|
|
+ << Text("\033[1;") + Text(30 + (int)color[i]) + "m";
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ std::cout
|
|
|
+ << Text("\033[1;") + Text(90 + (int)color[i]) + "m";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (backgroundColor[i] < Color::LIGHT_Black)
|
|
|
+ {
|
|
|
+ std::cout << Text("\033[1;")
|
|
|
+ + Text(40 + (int)backgroundColor[i]) + "m";
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ std::cout << Text("\033[1;")
|
|
|
+ + Text(100 + (int)backgroundColor[i]) + "m";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ std::cout << content[i];
|
|
|
+ lastColor = color ? (int)color[i] : -1;
|
|
|
+ lastBgColor = backgroundColor ? (int)backgroundColor[i] : -1;
|
|
|
+ if ((content[i] == '\n' || lineLength == maxWidth) && i < length - 1)
|
|
|
+ {
|
|
|
+ if (lineLength == maxWidth && content[i] != '\n')
|
|
|
+ {
|
|
|
+ std::cout << "\n";
|
|
|
+ }
|
|
|
+ lineLength = 0;
|
|
|
+ lineCount++;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ lineLength++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (lastColor != -1 || lastBgColor != -1) std::cout << "\033[0m";
|
|
|
+ if (lineCount == 0 && lineLength > 0)
|
|
|
+ {
|
|
|
+ lineCount++;
|
|
|
+ }
|
|
|
+ return lineCount;
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::StickyConsoleContent::restoreCursorPos() {}
|
|
|
+
|
|
|
+ConsoleHandler* Framework::StickyConsoleContent::zConsoleHandlerRef() const
|
|
|
+{
|
|
|
+ return zConsoleHandler;
|
|
|
+}
|
|
|
+
|
|
|
+Framework::ConsoleProgressBar::ConsoleProgressBar()
|
|
|
+ : StickyConsoleContent(),
|
|
|
+ progress(0),
|
|
|
+ maxProgress(1),
|
|
|
+ maxWidth(-1)
|
|
|
+{}
|
|
|
+
|
|
|
+void Framework::ConsoleProgressBar::setMaxWidth(int maxWidth)
|
|
|
+{
|
|
|
+ this->maxWidth = maxWidth;
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::ConsoleProgressBar::setProgress(int progress)
|
|
|
+{
|
|
|
+ if (progress < 0) progress = 0;
|
|
|
+ this->progress = progress;
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::ConsoleProgressBar::setMaxProgress(int maxProgress)
|
|
|
+{
|
|
|
+ if (maxProgress < 0) maxProgress = 0;
|
|
|
+ this->maxProgress = maxProgress;
|
|
|
+}
|
|
|
+
|
|
|
+int Framework::ConsoleProgressBar::print() const
|
|
|
+{
|
|
|
+ int maxWidth = zConsoleHandlerRef()->getWidth();
|
|
|
+ int width = this->maxWidth == -1 ? maxWidth : this->maxWidth;
|
|
|
+ if (width > maxWidth)
|
|
|
+ {
|
|
|
+ width = maxWidth;
|
|
|
+ }
|
|
|
+ if (width < 7)
|
|
|
+ {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ std::cout << "\r\33[0K"; // erase current line
|
|
|
+ int progress = this->progress > maxProgress ? maxProgress : this->progress;
|
|
|
+ int progressChars = width - 7;
|
|
|
+ std::cout << "[\033[1;47m";
|
|
|
+ int progressWidth = (int)(((double)progress / maxProgress) * progressChars);
|
|
|
+ if (progressWidth > progressChars)
|
|
|
+ {
|
|
|
+ progressWidth = progressChars;
|
|
|
+ }
|
|
|
+ for (int i = 0; i < progressWidth; i++)
|
|
|
+ {
|
|
|
+ std::cout << " ";
|
|
|
+ }
|
|
|
+ std::cout << "\033[0m";
|
|
|
+ for (int i = 0; i < progressChars - progressWidth; i++)
|
|
|
+ {
|
|
|
+ std::cout << " ";
|
|
|
+ }
|
|
|
+ std::cout << "] ";
|
|
|
+ Text str((int)(((double)progress / maxProgress) * 100));
|
|
|
+ for (int i = 0; i < 3 - str.getLength(); i++)
|
|
|
+ {
|
|
|
+ std::cout << " ";
|
|
|
+ }
|
|
|
+ std::cout << str.getText() << "%";
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+Framework::InputLine::InputLine()
|
|
|
+ : StickyConsoleContent(),
|
|
|
+ Thread(),
|
|
|
+ input(""),
|
|
|
+ cursorPos(0),
|
|
|
+ suggestions(0)
|
|
|
+{}
|
|
|
+
|
|
|
+Framework::InputLine::~InputLine()
|
|
|
+{
|
|
|
+ suggestions->release();
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::InputLine::addPossibleCommand(ConsoleCommand* command)
|
|
|
+{
|
|
|
+ commands.add(command);
|
|
|
+}
|
|
|
+
|
|
|
+bool Framework::InputLine::isInput()
|
|
|
+{
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::InputLine::setCursorToBeginning()
|
|
|
+{
|
|
|
+ cs.lock();
|
|
|
+ std::cout << "\r";
|
|
|
+}
|
|
|
+
|
|
|
+int Framework::InputLine::print() const
|
|
|
+{
|
|
|
+ std::cout << "\33[0K" // clear current line
|
|
|
+ << input.getText();
|
|
|
+ if (suggestions)
|
|
|
+ {
|
|
|
+ std::cout << "\n";
|
|
|
+ return 1 + dynamic_cast<StickyConsoleContent*>(suggestions)->print();
|
|
|
+ }
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::InputLine::restoreCursorPos()
|
|
|
+{
|
|
|
+ if (cursorPos > 0)
|
|
|
+ {
|
|
|
+ std::cout << Text("\33[") + Text(cursorPos) + "C";
|
|
|
+ }
|
|
|
+ cs.unlock();
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::InputLine::thread()
|
|
|
+{
|
|
|
+#ifdef WIN32
|
|
|
+ INPUT_RECORD inputRecord;
|
|
|
+ DWORD eventsRead;
|
|
|
+ HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ if (ReadConsoleInput(handle, &inputRecord, 1, &eventsRead))
|
|
|
+ {
|
|
|
+ cs.lock();
|
|
|
+ if (eventsRead > 0 && inputRecord.EventType == KEY_EVENT
|
|
|
+ && inputRecord.Event.KeyEvent.bKeyDown)
|
|
|
+ {
|
|
|
+ if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_LEFT)
|
|
|
+ {
|
|
|
+ cursorPos--;
|
|
|
+ if (cursorPos < 0)
|
|
|
+ {
|
|
|
+ cursorPos = 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ std::cout << "\33[1D" << std::flush;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_HOME)
|
|
|
+ { // Pos1 key moves cursor to beginning of line
|
|
|
+ std::cout << "\r" << std::flush;
|
|
|
+ cursorPos = 0;
|
|
|
+ }
|
|
|
+ else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_RIGHT)
|
|
|
+ {
|
|
|
+ cursorPos++;
|
|
|
+ if (cursorPos > input.getLength())
|
|
|
+ {
|
|
|
+ cursorPos = input.getLength();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ std::cout << "\33[1C" << std::flush;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_END)
|
|
|
+ { // Pos1 key moves cursor to beginning of line
|
|
|
+ cursorPos = input.getLength();
|
|
|
+ std::cout << Text("\r\33[") + Text(cursorPos) + "C"
|
|
|
+ << std::flush;
|
|
|
+ }
|
|
|
+ else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_TAB)
|
|
|
+ {
|
|
|
+ applyAutocompletion();
|
|
|
+ }
|
|
|
+ else if (inputRecord.Event.KeyEvent.wVirtualKeyCode
|
|
|
+ == VK_RETURN)
|
|
|
+ {
|
|
|
+ std::cout << "\r\33[0K" << std::flush; // eraze line
|
|
|
+ Text cmd = input;
|
|
|
+ cs.unlock();
|
|
|
+ if (executeCommand(cmd))
|
|
|
+ {
|
|
|
+ cs.lock();
|
|
|
+ input = "";
|
|
|
+ cursorPos = 0;
|
|
|
+ cs.unlock();
|
|
|
+ }
|
|
|
+ continue; // skip the unlock
|
|
|
+ }
|
|
|
+ else if (inputRecord.Event.KeyEvent.wVirtualKeyCode == VK_BACK)
|
|
|
+ {
|
|
|
+ if (cursorPos > 0)
|
|
|
+ {
|
|
|
+ input.remove(cursorPos - 1, cursorPos);
|
|
|
+ cursorPos--;
|
|
|
+ std::cout << "\r\33[0K" // eraze line
|
|
|
+ << input.getText() // output current input
|
|
|
+ // move cursor to current position
|
|
|
+ << Text("\r\33[") + Text(cursorPos) + "C"
|
|
|
+ << std::flush;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (inputRecord.Event.KeyEvent.uChar.AsciiChar)
|
|
|
+ {
|
|
|
+ input.insert(
|
|
|
+ cursorPos, inputRecord.Event.KeyEvent.uChar.AsciiChar);
|
|
|
+ cursorPos++;
|
|
|
+ std::cout << "\r\33[0K" // eraze line
|
|
|
+ << input.getText() // output current input
|
|
|
+ // move cursor to current position
|
|
|
+ << Text("\r\33[") + Text(cursorPos) + "C"
|
|
|
+ << std::flush;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ cs.unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+#else
|
|
|
+ char c;
|
|
|
+ while (read(STDIN_FILENO, &c, 1) == 1)
|
|
|
+ {
|
|
|
+ if (c == 27)
|
|
|
+ { // Check for the escape key (27 is the ASCII code for escape)
|
|
|
+ char seq[2];
|
|
|
+ if (read(STDIN_FILENO, &seq, 2) == 2)
|
|
|
+ {
|
|
|
+ cs.lock();
|
|
|
+ if (seq[0] == '[')
|
|
|
+ {
|
|
|
+ if (seq[1] == 'D')
|
|
|
+ { // left arrow key
|
|
|
+ cursorPos--;
|
|
|
+ if (cursorPos < 0)
|
|
|
+ {
|
|
|
+ cursorPos = 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ std::cout << "\33[1D" << std::flush;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (seq[1] == 'H')
|
|
|
+ { // Pos1 key moves cursor to beginning of line
|
|
|
+ std::cout << "\r" << std::flush;
|
|
|
+ cursorPos = 0;
|
|
|
+ }
|
|
|
+ else if (seq[1] == 'C')
|
|
|
+ { // right arrow key
|
|
|
+ cursorPos++;
|
|
|
+ if (cursorPos > input.getLength())
|
|
|
+ {
|
|
|
+ cursorPos = input.getLength();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ std::cout << "\33[1C" << std::flush;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (seq[1] == 'F')
|
|
|
+ { // End key moves cursor to end of line
|
|
|
+ cursorPos = input.getLength();
|
|
|
+ std::cout << Text("\r\33[") + Text(cursorPos) + "C"
|
|
|
+ << std::flush;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ cs.unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (c == 8)
|
|
|
+ { // backspace
|
|
|
+ if (cursorPos > 0)
|
|
|
+ {
|
|
|
+ cs.lock();
|
|
|
+ input.remove(cursorPos - 1, cursorPos);
|
|
|
+ cursorPos--;
|
|
|
+ std::cout << "\r\33[0K" // eraze line
|
|
|
+ << input.getText() // output current input
|
|
|
+ // move cursor to current position
|
|
|
+ << Text("\r\33[") + Text(cursorPos) + "C"
|
|
|
+ << std::flush;
|
|
|
+ cs.unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (c == 9)
|
|
|
+ { // tab
|
|
|
+ cs.lock();
|
|
|
+ applyAutocompletion();
|
|
|
+ cs.unlock();
|
|
|
+ }
|
|
|
+ else if (c == 13)
|
|
|
+ { // enter
|
|
|
+ cs.lock();
|
|
|
+ std::cout << "\r\33[0K" << std::flush; // eraze line
|
|
|
+ Text cmd = input;
|
|
|
+ cs.unlock();
|
|
|
+ if (executeCommand(cmd))
|
|
|
+ {
|
|
|
+ cs.lock();
|
|
|
+ input = "";
|
|
|
+ cursorPos = 0;
|
|
|
+ cs.unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ cs.lock();
|
|
|
+ input.insert(cursorPos, c);
|
|
|
+ cursorPos++;
|
|
|
+ std::cout << "\r\33[0K" // eraze line
|
|
|
+ << input.getText() // output current input
|
|
|
+ // move cursor to current position
|
|
|
+ << Text("\r\33[") + Text(cursorPos) + "C" << std::flush;
|
|
|
+ cs.unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::InputLine::applyAutocompletion()
|
|
|
+{
|
|
|
+ RCArray<Text> params;
|
|
|
+ bool lastArgFinished = 0;
|
|
|
+ Text* cmd = input.getTeilText(0, cursorPos);
|
|
|
+ parseCommand(*cmd, params, lastArgFinished);
|
|
|
+ cmd->release();
|
|
|
+ bool paramsStarted = params.getEintragAnzahl() > 1 || lastArgFinished;
|
|
|
+ Text* name;
|
|
|
+ if (params.getEintragAnzahl() > 0)
|
|
|
+ {
|
|
|
+ name = params.get(0);
|
|
|
+ params.remove(0);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ name = new Text("");
|
|
|
+ }
|
|
|
+ RCArray<Text> suggestions;
|
|
|
+ for (ConsoleCommand* command : commands)
|
|
|
+ {
|
|
|
+ if ((!paramsStarted && command->getName().hatAt(0, *name))
|
|
|
+ || (paramsStarted && command->getName().istGleich(*name)))
|
|
|
+ {
|
|
|
+ if (paramsStarted)
|
|
|
+ {
|
|
|
+ command->addAutocompletePossibilities(
|
|
|
+ params, !lastArgFinished, suggestions);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ suggestions.add(new Text(command->getName()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (this->suggestions)
|
|
|
+ {
|
|
|
+ this->suggestions->clear();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ this->suggestions = new ConsoleListView();
|
|
|
+ this->suggestions->setConsoleHandlerZ(zConsoleHandlerRef());
|
|
|
+ }
|
|
|
+ for (Text* suggestion : suggestions)
|
|
|
+ {
|
|
|
+ this->suggestions->addItem(*suggestion);
|
|
|
+ }
|
|
|
+ if (this->suggestions->getItems().getEintragAnzahl() > 0)
|
|
|
+ {
|
|
|
+ bool commonChar = true;
|
|
|
+ for (int i
|
|
|
+ = lastArgFinished
|
|
|
+ ? 0
|
|
|
+ : (params.getEintragAnzahl() > 0
|
|
|
+ ? params.z(params.getEintragAnzahl() - 1)->getLength()
|
|
|
+ : name->getLength());
|
|
|
+ true;
|
|
|
+ i++)
|
|
|
+ {
|
|
|
+ if (i >= this->suggestions->getItems().z(0)->getLength())
|
|
|
+ {
|
|
|
+ commonChar = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ for (int j = 1;
|
|
|
+ j < this->suggestions->getItems().getEintragAnzahl();
|
|
|
+ j++)
|
|
|
+ {
|
|
|
+ if (i >= this->suggestions->getItems().z(j)->getLength())
|
|
|
+ {
|
|
|
+ commonChar = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (this->suggestions->getItems().z(j)->getText()[i]
|
|
|
+ != this->suggestions->getItems().z(0)->getText()[i])
|
|
|
+ {
|
|
|
+ commonChar = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!commonChar)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ input.insert(
|
|
|
+ cursorPos, this->suggestions->getItems().z(0)->getText()[i]);
|
|
|
+ cursorPos++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ name->release();
|
|
|
+ if (this->suggestions->getItems().getEintragAnzahl() <= 1)
|
|
|
+ {
|
|
|
+ this->suggestions->release();
|
|
|
+ this->suggestions = 0;
|
|
|
+ }
|
|
|
+ triggerUpdate();
|
|
|
+}
|
|
|
+
|
|
|
+bool Framework::InputLine::executeCommand(Text command)
|
|
|
+{
|
|
|
+ RCArray<Text> params;
|
|
|
+ bool lastArgFinished = 0;
|
|
|
+ Text* cmd = input.getTeilText(0, cursorPos);
|
|
|
+ if (!parseCommand(*cmd, params, lastArgFinished))
|
|
|
+ {
|
|
|
+ cmd->release();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ cmd->release();
|
|
|
+ Text* name;
|
|
|
+ if (params.getEintragAnzahl() > 0)
|
|
|
+ {
|
|
|
+ name = params.get(0);
|
|
|
+ params.remove(0);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ name = new Text("");
|
|
|
+ }
|
|
|
+ RCArray<Text> suggestions;
|
|
|
+ for (ConsoleCommand* command : commands)
|
|
|
+ {
|
|
|
+ if (command->getName().istGleich(*name))
|
|
|
+ {
|
|
|
+ name->release();
|
|
|
+ return command->execute(params);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ name->release();
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+bool Framework::InputLine::parseCommand(
|
|
|
+ Text command, RCArray<Text>& split, bool& lastFinished)
|
|
|
+{
|
|
|
+ const char* cmd = command.getText();
|
|
|
+ Text str;
|
|
|
+ bool insideString = false;
|
|
|
+ bool insideString2 = false;
|
|
|
+ bool lastEscape = false;
|
|
|
+ lastFinished = false;
|
|
|
+ while (*cmd)
|
|
|
+ {
|
|
|
+ if (*cmd == ' ' && !insideString && !insideString2 && !lastEscape)
|
|
|
+ {
|
|
|
+ if (str.getLength() > 0)
|
|
|
+ {
|
|
|
+ split.add(new Text(str));
|
|
|
+ str = "";
|
|
|
+ lastFinished = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (*cmd == '"' && !insideString2 && !lastEscape)
|
|
|
+ {
|
|
|
+ insideString = !insideString;
|
|
|
+ lastFinished = !insideString;
|
|
|
+ }
|
|
|
+ else if (*cmd == '\'' && !insideString && !lastEscape)
|
|
|
+ {
|
|
|
+ insideString2 = !insideString2;
|
|
|
+ lastFinished = !insideString2;
|
|
|
+ }
|
|
|
+ else if (*cmd == '\\' && !lastEscape)
|
|
|
+ {
|
|
|
+ lastEscape = true;
|
|
|
+ lastFinished = false;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ lastEscape = false;
|
|
|
+ str.append(*cmd);
|
|
|
+ lastFinished = false;
|
|
|
+ }
|
|
|
+ cmd++;
|
|
|
+ }
|
|
|
+ if (str.getLength() > 0)
|
|
|
+ {
|
|
|
+ split.add(new Text(str));
|
|
|
+ }
|
|
|
+ return !insideString && !insideString2 && !lastEscape;
|
|
|
+}
|
|
|
+
|
|
|
+Framework::ConsoleListView::ConsoleListView()
|
|
|
+ : StickyConsoleContent(),
|
|
|
+ maxColumns(-1),
|
|
|
+ maxVisibleLines(5),
|
|
|
+ lineOffset(0)
|
|
|
+{}
|
|
|
+
|
|
|
+int Framework::ConsoleListView::getUsedColumns() const
|
|
|
+{
|
|
|
+ if (!zConsoleHandlerRef()) return 0;
|
|
|
+ int maxWidth = zConsoleHandlerRef()->getWidth();
|
|
|
+ int columnSize = 0;
|
|
|
+ for (Text* item : items)
|
|
|
+ {
|
|
|
+ if (item->getLength() > columnSize)
|
|
|
+ {
|
|
|
+ columnSize = item->getLength();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ columnSize += 4;
|
|
|
+ if (maxWidth < columnSize)
|
|
|
+ {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ int columns = maxWidth / columnSize;
|
|
|
+ if (maxColumns > 0 && columns > maxColumns)
|
|
|
+ {
|
|
|
+ columns = maxColumns;
|
|
|
+ }
|
|
|
+ if (columns > items.getEintragAnzahl())
|
|
|
+ {
|
|
|
+ columns = items.getEintragAnzahl();
|
|
|
+ }
|
|
|
+ return columns;
|
|
|
+}
|
|
|
+
|
|
|
+int Framework::ConsoleListView::getNeededLines() const
|
|
|
+{
|
|
|
+ int columns = getUsedColumns();
|
|
|
+ if (!columns)
|
|
|
+ {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if (maxColumns > 0 && columns > maxColumns)
|
|
|
+ {
|
|
|
+ columns = maxColumns;
|
|
|
+ }
|
|
|
+ int lines = items.getEintragAnzahl() / columns;
|
|
|
+ if (items.getEintragAnzahl() % columns != 0)
|
|
|
+ {
|
|
|
+ lines++;
|
|
|
+ }
|
|
|
+ return lines;
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::ConsoleListView::setMaxVisibleLines(int maxVisibleLines)
|
|
|
+{
|
|
|
+ this->maxVisibleLines = maxVisibleLines;
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::ConsoleListView::setLineOffset(int lineOffset)
|
|
|
+{
|
|
|
+ this->lineOffset = lineOffset;
|
|
|
+ int maxLines = getNeededLines();
|
|
|
+ if (this->lineOffset > maxLines - maxVisibleLines)
|
|
|
+ {
|
|
|
+ this->lineOffset = maxLines - maxVisibleLines;
|
|
|
+ }
|
|
|
+ if (this->lineOffset < 0)
|
|
|
+ {
|
|
|
+ this->lineOffset = 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::ConsoleListView::setMaxColumns(int maxColumns)
|
|
|
+{
|
|
|
+ this->maxColumns = maxColumns;
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::ConsoleListView::addItem(Text item)
|
|
|
+{
|
|
|
+ item.ersetzen("\n", " ");
|
|
|
+ int index = 0;
|
|
|
+ for (Text* curreent : items)
|
|
|
+ {
|
|
|
+ int i = 0;
|
|
|
+ while (i < curreent->getLength() && i < item.getLength())
|
|
|
+ {
|
|
|
+ if (curreent->getText()[i] != item.getText()[i])
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+ if (i == curreent->getLength() && i == item.getLength())
|
|
|
+ {
|
|
|
+ return; // item already exists
|
|
|
+ }
|
|
|
+ if (i < curreent->getLength() && i == item.getLength())
|
|
|
+ {
|
|
|
+ items.add(new Text(item), index);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (i < curreent->getLength() && i < item.getLength()
|
|
|
+ && curreent->getText()[i] > item.getText()[i])
|
|
|
+ {
|
|
|
+ items.add(new Text(item), index);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ index++;
|
|
|
+ }
|
|
|
+ items.add(new Text(item));
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::ConsoleListView::clear()
|
|
|
+{
|
|
|
+ items.leeren();
|
|
|
+}
|
|
|
+
|
|
|
+const RCArray<Text>& Framework::ConsoleListView::getItems() const
|
|
|
+{
|
|
|
+ return items;
|
|
|
+}
|
|
|
+
|
|
|
+int Framework::ConsoleListView::print() const
|
|
|
+{
|
|
|
+ int lines = lineOffset;
|
|
|
+ int maxLines = getNeededLines();
|
|
|
+ if (lines > maxLines - maxVisibleLines)
|
|
|
+ {
|
|
|
+ lines = maxLines - maxVisibleLines;
|
|
|
+ }
|
|
|
+ if (lines < 0)
|
|
|
+ {
|
|
|
+ lines = 0;
|
|
|
+ }
|
|
|
+ int columns = getUsedColumns();
|
|
|
+ if (!columns)
|
|
|
+ {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ int columnSize = zConsoleHandlerRef()->getWidth() / columns;
|
|
|
+ int printetLines = 0;
|
|
|
+ int currentColumn = 0;
|
|
|
+ std::cout << "\33[0K"; // clear current line
|
|
|
+ for (int i = columns * lines; i < items.getEintragAnzahl(); i++)
|
|
|
+ {
|
|
|
+ if (i >= columns * (lines + maxVisibleLines))
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (currentColumn >= columns)
|
|
|
+ {
|
|
|
+ std::cout << "\n\r\33[0K";
|
|
|
+ printetLines++;
|
|
|
+ currentColumn++;
|
|
|
+ }
|
|
|
+ if (!printetLines)
|
|
|
+ {
|
|
|
+ printetLines++;
|
|
|
+ }
|
|
|
+ std::cout << items.z(i)->getText();
|
|
|
+ for (int j = items.z(i)->getLength(); j < columnSize; j++)
|
|
|
+ {
|
|
|
+ std::cout << " ";
|
|
|
+ }
|
|
|
+ currentColumn++;
|
|
|
+ }
|
|
|
+ return printetLines;
|
|
|
+}
|
|
|
+
|
|
|
+Framework::ConsoleHandler::ConsoleHandler()
|
|
|
+ : ReferenceCounter(),
|
|
|
+ lines(0),
|
|
|
+ lineCounts(0),
|
|
|
+ contentCount(0)
|
|
|
+{
|
|
|
+#ifdef WIN32
|
|
|
+ hConsole = GetStdHandle(STD_INPUT_HANDLE);
|
|
|
+ SetConsoleMode(hConsole, ENABLE_PROCESSED_INPUT); // set raw mode
|
|
|
+ hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
+#else
|
|
|
+ struct termios t;
|
|
|
+ tcgetattr(STDIN_FILENO, &t);
|
|
|
+ t.c_lflag &= ~(ICANON | ECHO); // set raw mode
|
|
|
+ tcsetattr(STDIN_FILENO, TCSANOW, &t);
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+Framework::ConsoleHandler::~ConsoleHandler()
|
|
|
+{
|
|
|
+ for (int i = 0; i < contentCount; i++)
|
|
|
+ {
|
|
|
+ lines[i]->release();
|
|
|
+ }
|
|
|
+ delete[] lines;
|
|
|
+ delete[] lineCounts;
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::ConsoleHandler::addContent(
|
|
|
+ StickyConsoleContent* content, ConsoleContentPosition pos)
|
|
|
+{
|
|
|
+ cs.lock();
|
|
|
+ if (content->isInput())
|
|
|
+ {
|
|
|
+ for (int i = 0; i < contentCount; i++)
|
|
|
+ {
|
|
|
+ if (lines[i]->isInput())
|
|
|
+ {
|
|
|
+ content->release();
|
|
|
+ cs.unlock();
|
|
|
+ throw "There can only be one input line";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (content->zConsoleHandler)
|
|
|
+ {
|
|
|
+ cs.unlock();
|
|
|
+ throw "A console content can only be added to one console handler "
|
|
|
+ "at "
|
|
|
+ "the same time.";
|
|
|
+ }
|
|
|
+ content->zConsoleHandler = this;
|
|
|
+ contentCount++;
|
|
|
+ int* lineCounts = new int[contentCount];
|
|
|
+ StickyConsoleContent** lines = new StickyConsoleContent*[contentCount];
|
|
|
+ if (pos == ConsoleContentPosition::Top)
|
|
|
+ {
|
|
|
+ lines[0] = content;
|
|
|
+ lineCounts[0] = 0;
|
|
|
+ for (int i = 1; i < contentCount; i++)
|
|
|
+ {
|
|
|
+ lines[i] = this->lines[i - 1];
|
|
|
+ lineCounts[i] = this->lineCounts[i - 1];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ lines[contentCount - 1] = content;
|
|
|
+ lineCounts[contentCount - 1] = content->print();
|
|
|
+ for (int i = 0; i < contentCount - 1; i++)
|
|
|
+ {
|
|
|
+ lines[i] = this->lines[i];
|
|
|
+ lineCounts[i] = this->lineCounts[i];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ delete[] this->lines;
|
|
|
+ delete[] this->lineCounts;
|
|
|
+ this->lines = lines;
|
|
|
+ this->lineCounts = lineCounts;
|
|
|
+ InputLine* input = dynamic_cast<InputLine*>(content);
|
|
|
+ if (input)
|
|
|
+ {
|
|
|
+ input->start();
|
|
|
+ }
|
|
|
+ cs.unlock();
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::ConsoleHandler::removeContent(StickyConsoleContent* zContent)
|
|
|
+{
|
|
|
+ cs.lock();
|
|
|
+ int index = -1;
|
|
|
+ for (int i = 0; i < contentCount; i++)
|
|
|
+ {
|
|
|
+ if (lines[i] == zContent)
|
|
|
+ {
|
|
|
+ index = i;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (index == -1)
|
|
|
+ {
|
|
|
+ cs.unlock();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ zContent->zConsoleHandler = 0;
|
|
|
+ contentCount--;
|
|
|
+ int* lineCounts = new int[contentCount];
|
|
|
+ StickyConsoleContent** lines = new StickyConsoleContent*[contentCount];
|
|
|
+ for (int i = 0; i < index; i++)
|
|
|
+ {
|
|
|
+ lines[i] = this->lines[i];
|
|
|
+ lineCounts[i] = this->lineCounts[i];
|
|
|
+ }
|
|
|
+ for (int i = index; i < contentCount; i++)
|
|
|
+ {
|
|
|
+ lines[i] = this->lines[i + 1];
|
|
|
+ lineCounts[i] = this->lineCounts[i + 1];
|
|
|
+ }
|
|
|
+ delete[] this->lines;
|
|
|
+ delete[] this->lineCounts;
|
|
|
+ this->lines = lines;
|
|
|
+ this->lineCounts = lineCounts;
|
|
|
+ if (zContent->isInput())
|
|
|
+ {
|
|
|
+ InputLine* input = dynamic_cast<InputLine*>(zContent);
|
|
|
+ if (input)
|
|
|
+ {
|
|
|
+ input->start();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ zContent->release();
|
|
|
+ cs.unlock();
|
|
|
+}
|
|
|
+
|
|
|
+int Framework::ConsoleHandler::getWidth() const
|
|
|
+{
|
|
|
+#ifdef WIN32
|
|
|
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
|
+ GetConsoleScreenBufferInfo(hConsole, &csbi);
|
|
|
+ return csbi.dwSize.X;
|
|
|
+#else
|
|
|
+ struct winsize ws;
|
|
|
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
|
|
|
+ {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return ws.ws_col;
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+int Framework::ConsoleHandler::getHeight() const
|
|
|
+{
|
|
|
+#ifdef WIN32
|
|
|
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
|
+ GetConsoleScreenBufferInfo(hConsole, &csbi);
|
|
|
+ return csbi.dwSize.Y;
|
|
|
+#else
|
|
|
+ struct winsize ws;
|
|
|
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
|
|
|
+ {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return ws.ws_row;
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::ConsoleHandler::clear()
|
|
|
+{
|
|
|
+ cs.lock();
|
|
|
+ for (int i = 0; i < contentCount; i++)
|
|
|
+ {
|
|
|
+ lines[i]->release();
|
|
|
+ }
|
|
|
+ delete[] lines;
|
|
|
+ delete[] lineCounts;
|
|
|
+ lines = 0;
|
|
|
+ lineCounts = 0;
|
|
|
+ contentCount = 0;
|
|
|
+ cs.unlock();
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::ConsoleHandler::print()
|
|
|
+{
|
|
|
+ cs.lock();
|
|
|
+ int totalLines = 0;
|
|
|
+ bool adittionalLine = 0;
|
|
|
+ for (int i = 0; i < contentCount; i++)
|
|
|
+ {
|
|
|
+ if (lines[i]->isInput())
|
|
|
+ {
|
|
|
+ lines[i]->setCursorToBeginning();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ totalLines += lineCounts[i];
|
|
|
+ }
|
|
|
+ if (totalLines > 0)
|
|
|
+ {
|
|
|
+ std::cout << "\33[" << totalLines << "A";
|
|
|
+ }
|
|
|
+ totalLines = 0;
|
|
|
+ for (int i = 0; i < contentCount; i++)
|
|
|
+ {
|
|
|
+ lineCounts[i] = lines[i]->print();
|
|
|
+ if (lineCounts[i] > 0)
|
|
|
+ {
|
|
|
+ adittionalLine = 0;
|
|
|
+ if (i < contentCount - 1)
|
|
|
+ {
|
|
|
+ std::cout << "\n";
|
|
|
+ adittionalLine = 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ totalLines += lineCounts[i];
|
|
|
+ }
|
|
|
+ if (adittionalLine)
|
|
|
+ {
|
|
|
+ totalLines++;
|
|
|
+ }
|
|
|
+ std::cout << "\33[" << (totalLines - 1) << "A\r";
|
|
|
+ for (int i = 0; i < contentCount; i++)
|
|
|
+ {
|
|
|
+ if (lines[i]->isInput())
|
|
|
+ {
|
|
|
+ lines[i]->restoreCursorPos();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (i < contentCount - 1 && lineCounts[i])
|
|
|
+ {
|
|
|
+ std::cout << "\33[" << lineCounts[i] << "B";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ std::cout << std::flush;
|
|
|
+ cs.unlock();
|
|
|
+}
|
|
|
+
|
|
|
+void Framework::ConsoleHandler::print(Text str)
|
|
|
+{
|
|
|
+ std::cout << "\n";
|
|
|
+ cs.lock();
|
|
|
+ int totalLines = 0;
|
|
|
+ bool adittionalLine = 0;
|
|
|
+ for (int i = 0; i < contentCount; i++)
|
|
|
+ {
|
|
|
+ if (lines[i]->isInput())
|
|
|
+ {
|
|
|
+ lines[i]->setCursorToBeginning();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ totalLines += lineCounts[i];
|
|
|
+ }
|
|
|
+ if (totalLines > 0)
|
|
|
+ {
|
|
|
+ std::cout << "\33[" << totalLines + 1 << "A";
|
|
|
+ }
|
|
|
+ std::cout << "\33[0K" // clear current line
|
|
|
+ << str.getText();
|
|
|
+ if (str.getText()[str.getLength() - 1] != '\n')
|
|
|
+ {
|
|
|
+ std::cout << "\n";
|
|
|
+ }
|
|
|
+ totalLines = 0;
|
|
|
+ for (int i = 0; i < contentCount; i++)
|
|
|
+ {
|
|
|
+ lineCounts[i] = lines[i]->print();
|
|
|
+ if (lineCounts[i] > 0)
|
|
|
+ {
|
|
|
+ adittionalLine = 0;
|
|
|
+ if (i < contentCount - 1)
|
|
|
+ {
|
|
|
+ std::cout << "\n";
|
|
|
+ adittionalLine = 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ totalLines += lineCounts[i];
|
|
|
+ }
|
|
|
+ if (adittionalLine)
|
|
|
+ {
|
|
|
+ totalLines++;
|
|
|
+ }
|
|
|
+ std::cout << "\33[" << (totalLines - 1) << "A\r";
|
|
|
+ for (int i = 0; i < contentCount; i++)
|
|
|
+ {
|
|
|
+ if (lines[i]->isInput())
|
|
|
+ {
|
|
|
+ lines[i]->restoreCursorPos();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (i < contentCount - 1 && lineCounts[i])
|
|
|
+ {
|
|
|
+ std::cout << "\33[" << lineCounts[i] << "B";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ std::cout << std::flush;
|
|
|
+ cs.unlock();
|
|
|
+}
|