mirror of
https://github.com/Swordfish90/cool-retro-term.git
synced 2025-02-07 13:41:27 +00:00
542 lines
13 KiB
C++
542 lines
13 KiB
C++
/******************************************************************************
|
|
* Copyright (c) 2013 Jørgen Lind
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
*******************************************************************************/
|
|
|
|
#include "cursor.h"
|
|
|
|
#include "block.h"
|
|
#include "screen_data.h"
|
|
|
|
#include <QTextCodec>
|
|
|
|
Cursor::Cursor(Screen* screen)
|
|
: QObject(screen)
|
|
, m_screen(screen)
|
|
, m_current_text_style(screen->defaultTextStyle())
|
|
, m_position(0,0)
|
|
, m_new_position(0,0)
|
|
, m_document_width(screen->width())
|
|
, m_document_height(screen->height())
|
|
, m_top_margin(0)
|
|
, m_bottom_margin(0)
|
|
, m_scroll_margins_set(false)
|
|
, m_origin_at_margin(false)
|
|
, m_notified(false)
|
|
, m_visible(true)
|
|
, m_new_visibillity(true)
|
|
, m_blinking(false)
|
|
, m_new_blinking(false)
|
|
, m_wrap_around(true)
|
|
, m_content_height_changed(false)
|
|
, m_insert_mode(Replace)
|
|
{
|
|
connect(screen, SIGNAL(widthAboutToChange(int)), this, SLOT(setDocumentWidth(int)));
|
|
connect(screen, SIGNAL(heightAboutToChange(int, int, int)), this, SLOT(setDocumentHeight(int, int, int)));
|
|
connect(screen, SIGNAL(contentHeightChanged()), this, SLOT(contentHeightChanged()));
|
|
|
|
m_gl_text_codec = QTextCodec::codecForName("utf-8")->makeDecoder();
|
|
m_gr_text_codec = QTextCodec::codecForName("utf-8")->makeDecoder();
|
|
|
|
for (int i = 0; i < m_document_width; i++) {
|
|
if (i % 8 == 0) {
|
|
m_tab_stops.append(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
Cursor::~Cursor()
|
|
{
|
|
|
|
}
|
|
|
|
void Cursor::setDocumentWidth(int width)
|
|
{
|
|
if (width > m_document_width) {
|
|
for (int i = m_document_width -1; i < width; i++) {
|
|
if (i % 8 == 0) {
|
|
m_tab_stops.append(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
m_document_width = width;
|
|
if (new_x() >= width) {
|
|
new_rx() = width - 1;
|
|
notifyChanged();
|
|
}
|
|
}
|
|
|
|
void Cursor::setDocumentHeight(int height, int currentCursorBlock, int currentScrollBackHeight)
|
|
{
|
|
Q_UNUSED(currentCursorBlock);
|
|
resetScrollArea();
|
|
if (m_document_height > height) {
|
|
const int to_remove = m_document_height - height;
|
|
const int removeLinesBelowCursor =
|
|
std::min(m_document_height - new_y(), to_remove);
|
|
const int removeLinesAtTop = to_remove - removeLinesBelowCursor;
|
|
if (!removeLinesAtTop) {
|
|
new_ry() -= removeLinesAtTop;
|
|
notifyChanged();
|
|
}
|
|
} else {
|
|
int height_diff = height - m_document_height;
|
|
if (currentScrollBackHeight >= height_diff) {
|
|
new_ry() += height_diff;
|
|
} else if (currentScrollBackHeight > 0) {
|
|
const int move = height_diff - currentScrollBackHeight;
|
|
new_ry() += move;
|
|
}
|
|
}
|
|
|
|
m_document_height = height;
|
|
|
|
if (new_y() >= height) {
|
|
new_ry() = height - 1;
|
|
notifyChanged();
|
|
}
|
|
if (new_y() <= 0) {
|
|
new_ry() = 0;
|
|
}
|
|
}
|
|
|
|
bool Cursor::visible() const
|
|
{
|
|
return m_visible;
|
|
}
|
|
void Cursor::setVisible(bool visible)
|
|
{
|
|
m_new_visibillity = visible;
|
|
}
|
|
|
|
bool Cursor::blinking() const
|
|
{
|
|
return m_blinking;
|
|
}
|
|
|
|
void Cursor::setBlinking(bool blinking)
|
|
{
|
|
m_new_blinking = blinking;
|
|
}
|
|
|
|
void Cursor::setTextStyle(TextStyle::Style style, bool add)
|
|
{
|
|
if (add) {
|
|
m_current_text_style.style |= style;
|
|
} else {
|
|
m_current_text_style.style &= !style;
|
|
}
|
|
}
|
|
|
|
void Cursor::resetStyle()
|
|
{
|
|
m_current_text_style.background = ColorPalette::DefaultBackground;
|
|
m_current_text_style.forground = ColorPalette::DefaultForground;
|
|
m_current_text_style.style = TextStyle::Normal;
|
|
}
|
|
|
|
void Cursor::scrollUp(int lines)
|
|
{
|
|
if (new_y() < top() || new_y() > bottom())
|
|
return;
|
|
for (int i = 0; i < lines; i++) {
|
|
screen_data()->moveLine(bottom(), top());
|
|
}
|
|
}
|
|
|
|
void Cursor::scrollDown(int lines)
|
|
{
|
|
if (new_y() < top() || new_y() > bottom())
|
|
return;
|
|
for (int i = 0; i < lines; i++) {
|
|
screen_data()->moveLine(top(), bottom());
|
|
}
|
|
}
|
|
|
|
void Cursor::setTextCodec(QTextCodec *codec)
|
|
{
|
|
m_gl_text_codec = codec->makeDecoder();
|
|
}
|
|
|
|
void Cursor::setInsertMode(InsertMode mode)
|
|
{
|
|
m_insert_mode = mode;
|
|
}
|
|
|
|
TextStyle Cursor::currentTextStyle() const
|
|
{
|
|
return m_current_text_style;
|
|
}
|
|
|
|
void Cursor::setTextStyleColor(ushort color)
|
|
{
|
|
Q_ASSERT(color >= 30 && color < 50);
|
|
if (color < 38) {
|
|
m_current_text_style.forground = ColorPalette::Color(color - 30);
|
|
} else if (color == 39) {
|
|
m_current_text_style.forground = ColorPalette::DefaultForground;
|
|
} else if (color >= 40 && color < 48) {
|
|
m_current_text_style.background = ColorPalette::Color(color - 40);
|
|
} else if (color == 49) {
|
|
m_current_text_style.background = ColorPalette::DefaultBackground;
|
|
} else {
|
|
qDebug() << "Failed to set color";
|
|
}
|
|
}
|
|
|
|
ColorPalette *Cursor::colorPalette() const
|
|
{
|
|
return m_screen->colorPalette();
|
|
}
|
|
|
|
QPoint Cursor::position() const
|
|
{
|
|
return m_position;
|
|
}
|
|
|
|
int Cursor::x() const
|
|
{
|
|
return m_position.x();
|
|
}
|
|
|
|
int Cursor::y() const
|
|
{
|
|
return (m_screen->currentScreenData()->contentHeight() - m_screen->height()) + m_position.y();
|
|
}
|
|
|
|
void Cursor::moveOrigin()
|
|
{
|
|
m_new_position = QPoint(0,adjusted_top());
|
|
notifyChanged();
|
|
}
|
|
|
|
void Cursor::moveBeginningOfLine()
|
|
{
|
|
new_rx() = 0;
|
|
notifyChanged();
|
|
}
|
|
|
|
void Cursor::moveUp(int lines)
|
|
{
|
|
int adjusted_new_y = this->adjusted_new_y();
|
|
if (!adjusted_new_y || !lines)
|
|
return;
|
|
|
|
if (lines < adjusted_new_y) {
|
|
new_ry() -= lines;
|
|
} else {
|
|
new_ry() = adjusted_top();
|
|
}
|
|
notifyChanged();
|
|
}
|
|
|
|
void Cursor::moveDown(int lines)
|
|
{
|
|
int bottom = adjusted_bottom();
|
|
if (new_y() == bottom || !lines)
|
|
return;
|
|
|
|
if (new_y() + lines <= bottom) {
|
|
new_ry() += lines;
|
|
} else {
|
|
new_ry() = bottom;
|
|
}
|
|
notifyChanged();
|
|
}
|
|
|
|
void Cursor::moveLeft(int positions)
|
|
{
|
|
if (!new_x() || !positions)
|
|
return;
|
|
if (positions < new_x()) {
|
|
new_rx() -= positions;
|
|
} else {
|
|
new_rx() = 0;
|
|
}
|
|
notifyChanged();
|
|
}
|
|
|
|
void Cursor::moveRight(int positions)
|
|
{
|
|
int width = m_screen->width();
|
|
if (new_x() == width -1 || !positions)
|
|
return;
|
|
if (positions < width - new_x()) {
|
|
new_rx() += positions;
|
|
} else {
|
|
new_rx() = width -1;
|
|
}
|
|
|
|
notifyChanged();
|
|
}
|
|
|
|
void Cursor::move(int new_x, int new_y)
|
|
{
|
|
int width = m_screen->width();
|
|
|
|
if (m_origin_at_margin) {
|
|
new_y += m_top_margin;
|
|
}
|
|
|
|
if (new_x < 0) {
|
|
new_x = 0;
|
|
} else if (new_x >= width) {
|
|
new_x = width - 1;
|
|
}
|
|
|
|
if (new_y < adjusted_top()) {
|
|
new_y = adjusted_top();
|
|
} else if (new_y > adjusted_bottom()) {
|
|
new_y = adjusted_bottom();
|
|
}
|
|
|
|
if (this->new_y() != new_y || this->new_x() != new_x) {
|
|
m_new_position = QPoint(new_x, new_y);
|
|
notifyChanged();
|
|
}
|
|
}
|
|
|
|
void Cursor::moveToLine(int line)
|
|
{
|
|
const int height = m_screen->height();
|
|
if (line < adjusted_top()) {
|
|
line = 0;
|
|
} else if (line > adjusted_bottom()) {
|
|
line = height -1;
|
|
}
|
|
|
|
if (line != new_y()) {
|
|
new_rx() = line;
|
|
notifyChanged();
|
|
}
|
|
}
|
|
|
|
void Cursor::moveToCharacter(int character)
|
|
{
|
|
const int width = m_screen->width();
|
|
if (character < 0) {
|
|
character = 1;
|
|
} else if (character > width) {
|
|
character = width;
|
|
}
|
|
if (character != new_x()) {
|
|
new_rx() = character;
|
|
notifyChanged();
|
|
}
|
|
}
|
|
|
|
void Cursor::moveToNextTab()
|
|
{
|
|
for (int i = 0; i < m_tab_stops.size(); i++) {
|
|
if (new_x() < m_tab_stops.at(i)) {
|
|
moveToCharacter(std::min(m_tab_stops.at(i), m_document_width -1));
|
|
return;
|
|
}
|
|
}
|
|
moveToCharacter(m_document_width - 1);
|
|
}
|
|
|
|
void Cursor::setTabStop()
|
|
{
|
|
int i;
|
|
for (i = 0; i < m_tab_stops.size(); i++) {
|
|
if (new_x() == m_tab_stops.at(i))
|
|
return;
|
|
if (new_x() > m_tab_stops.at(i)) {
|
|
continue;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
m_tab_stops.insert(i,new_x());
|
|
}
|
|
|
|
void Cursor::removeTabStop()
|
|
{
|
|
for (int i = 0; i < m_tab_stops.size(); i++) {
|
|
if (new_x() == m_tab_stops.at(i)) {
|
|
m_tab_stops.remove(i);
|
|
return;
|
|
} else if (new_x() < m_tab_stops.at(i)) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Cursor::clearTabStops()
|
|
{
|
|
m_tab_stops.clear();
|
|
}
|
|
|
|
void Cursor::clearToBeginningOfLine()
|
|
{
|
|
screen_data()->clearToBeginningOfLine(m_new_position);
|
|
}
|
|
|
|
void Cursor::clearToEndOfLine()
|
|
{
|
|
screen_data()->clearToEndOfLine(m_new_position);
|
|
}
|
|
|
|
void Cursor::clearToBeginningOfScreen()
|
|
{
|
|
clearToBeginningOfLine();
|
|
if (new_y() > 0)
|
|
screen_data()->clearToBeginningOfScreen(m_new_position.y()-1);
|
|
}
|
|
|
|
void Cursor::clearToEndOfScreen()
|
|
{
|
|
clearToEndOfLine();
|
|
if (new_y() < m_screen->height() -1) {
|
|
screen_data()->clearToEndOfScreen(m_new_position.y()+1);
|
|
}
|
|
}
|
|
|
|
void Cursor::clearLine()
|
|
{
|
|
screen_data()->clearLine(m_new_position);
|
|
}
|
|
|
|
void Cursor::deleteCharacters(int characters)
|
|
{
|
|
screen_data()->deleteCharacters(m_new_position, new_x() + characters -1);
|
|
}
|
|
|
|
void Cursor::setWrapAround(bool wrap)
|
|
{
|
|
m_wrap_around = wrap;
|
|
}
|
|
|
|
void Cursor::addAtCursor(const QByteArray &data, bool only_latin)
|
|
{
|
|
if (m_insert_mode == Replace) {
|
|
replaceAtCursor(data, only_latin);
|
|
} else {
|
|
insertAtCursor(data, only_latin);
|
|
}
|
|
}
|
|
|
|
void Cursor::replaceAtCursor(const QByteArray &data, bool only_latin)
|
|
{
|
|
const QString text = m_gl_text_codec->toUnicode(data);
|
|
|
|
if (!m_wrap_around && new_x() + text.size() > m_screen->width()) {
|
|
const int size = m_document_width - new_x();
|
|
QString toBlock = text.mid(0,size);
|
|
toBlock.replace(toBlock.size() - 1, 1, text.at(text.size()-1));
|
|
screen_data()->replace(m_new_position, toBlock, m_current_text_style, only_latin);
|
|
new_rx() += toBlock.size();
|
|
} else {
|
|
auto diff = screen_data()->replace(m_new_position, text, m_current_text_style, only_latin);
|
|
new_rx() += diff.character;
|
|
new_ry() += diff.line;
|
|
}
|
|
|
|
if (new_y() >= m_document_height)
|
|
new_ry() = m_document_height - 1;
|
|
|
|
notifyChanged();
|
|
}
|
|
|
|
void Cursor::insertAtCursor(const QByteArray &data, bool only_latin)
|
|
{
|
|
const QString text = m_gl_text_codec->toUnicode(data);
|
|
auto diff = screen_data()->insert(m_new_position, text, m_current_text_style, only_latin);
|
|
new_rx() += diff.character;
|
|
new_ry() += diff.line;
|
|
if (new_y() >= m_document_height)
|
|
new_ry() = m_document_height - 1;
|
|
if (new_x() >= m_document_width)
|
|
new_rx() = m_document_width - 1;
|
|
}
|
|
|
|
void Cursor::lineFeed()
|
|
{
|
|
if(new_y() >= bottom()) {
|
|
screen_data()->insertLine(bottom(), top());
|
|
} else {
|
|
new_ry()++;
|
|
notifyChanged();
|
|
}
|
|
}
|
|
|
|
void Cursor::reverseLineFeed()
|
|
{
|
|
if (new_y() == top()) {
|
|
scrollUp(1);
|
|
} else {
|
|
new_ry()--;
|
|
notifyChanged();
|
|
}
|
|
}
|
|
|
|
void Cursor::setOriginAtMargin(bool atMargin)
|
|
{
|
|
m_origin_at_margin = atMargin;
|
|
m_new_position = QPoint(0, adjusted_top());
|
|
notifyChanged();
|
|
}
|
|
|
|
void Cursor::setScrollArea(int from, int to)
|
|
{
|
|
m_top_margin = from;
|
|
m_bottom_margin = std::min(to,m_document_height -1);
|
|
m_scroll_margins_set = true;
|
|
}
|
|
|
|
void Cursor::resetScrollArea()
|
|
{
|
|
m_top_margin = 0;
|
|
m_bottom_margin = 0;
|
|
m_scroll_margins_set = false;
|
|
}
|
|
|
|
void Cursor::dispatchEvents()
|
|
{
|
|
if (m_new_position != m_position|| m_content_height_changed) {
|
|
bool emit_x_changed = m_new_position.x() != m_position.x();
|
|
bool emit_y_changed = m_new_position.y() != m_position.y();
|
|
m_position = m_new_position;
|
|
if (emit_x_changed)
|
|
emit xChanged();
|
|
if (emit_y_changed || m_content_height_changed)
|
|
emit yChanged();
|
|
}
|
|
|
|
if (m_new_visibillity != m_visible) {
|
|
m_visible = m_new_visibillity;
|
|
emit visibilityChanged();
|
|
}
|
|
|
|
if (m_new_blinking != m_blinking) {
|
|
m_blinking = m_new_blinking;
|
|
emit blinkingChanged();
|
|
}
|
|
}
|
|
|
|
void Cursor::contentHeightChanged()
|
|
{
|
|
m_content_height_changed = true;
|
|
}
|
|
|