1
0
mirror of https://github.com/Swordfish90/cool-retro-term.git synced 2025-04-18 16:50:47 +01:00
2013-11-23 13:59:05 +01:00

438 lines
14 KiB
C++

/**************************************************************************************************
* Copyright (c) 2012 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 "line.h"
#include "text.h"
#include "screen.h"
#include <QtQuick/QQuickView>
#include <QtQuick/QQuickItem>
#include <QtCore/QDebug>
#include <algorithm>
QDebug operator<<(QDebug debug, TextStyleLine line)
{
debug << "TextStyleLine: [" << line.start_index << ":" << line.end_index << "] : style:" << line.style;
return debug;
}
Line::Line(Screen *screen)
: QObject(screen)
, m_screen(screen)
, m_index(0)
, m_old_index(-1)
, m_visible(true)
, m_changed(true)
{
m_text_line.resize(screen->width());
m_style_list.reserve(25);
clear();
}
Line::~Line()
{
releaseTextObjects();
}
Screen *Line::screen() const
{
return m_screen;
}
void Line::releaseTextObjects()
{
m_changed = true;
for (int i = 0; i < m_style_list.size(); i++) {
if (m_style_list.at(i).text_segment) {
m_style_list.at(i).text_segment->setVisible(false);
delete m_style_list.at(i).text_segment;
m_style_list[i].text_segment = 0;
}
}
}
void Line::clear()
{
m_text_line.fill(QChar(' '));
for (int i = 0; i < m_style_list.size(); i++) {
if (m_style_list.at(i).text_segment)
releaseTextSegment(m_style_list.at(i).text_segment);
}
m_style_list.clear();
m_style_list.append(TextStyleLine(m_screen->defaultTextStyle(),0,m_text_line.size() -1));
m_changed = true;
}
void Line::clearToEndOfLine(int index)
{
m_changed = true;
QString empty(m_text_line.size() - index, QChar(' '));
m_text_line.replace(index, m_text_line.size()-index,empty);
bool found = false;
for (int i = 0; i < m_style_list.size(); i++) {
const TextStyleLine current_style = m_style_list.at(i);
if (found) {
if (current_style.text_segment)
releaseTextSegment(current_style.text_segment);
m_style_list.remove(i);
i--;
} else {
if (index <= current_style.end_index) {
found = true;
if (current_style.start_index == index) {
if (current_style.text_segment)
releaseTextSegment(current_style.text_segment);
m_style_list.remove(i);
i--;
} else {
m_style_list[i].end_index = index - 1;
m_style_list[i].text_dirty = true;
}
}
}
}
if (m_style_list.size() && m_style_list.last().isCompatible(m_screen->defaultTextStyle())) {
m_style_list.last().end_index = m_text_line.size() -1;
} else {
m_style_list.append(TextStyleLine(m_screen->defaultTextStyle(),index, m_text_line.size() -1));
}
}
void Line::clearCharacters(int from, int to)
{
QString empty(to-from, QChar(' '));
const TextStyle &defaultTextStyle = m_screen->defaultTextStyle();
replaceAtPos(from, empty, defaultTextStyle);
}
void Line::deleteCharacters(int from, int to)
{
m_changed = true;
int removed = 0;
const int size = (to + 1) - from;
bool found = false;
for (int i = 0; i < m_style_list.size(); i++) {
TextStyleLine &current_style = m_style_list[i];
if (found) {
current_style.start_index -= removed;
current_style.end_index -= removed;
current_style.index_dirty = true;
if (removed != size) {
int current_style_size = current_style.end_index + 1 - current_style.start_index;
if (current_style_size <= size - removed) {
removed += current_style.end_index + 1 - current_style.start_index;
if (current_style.text_segment)
releaseTextSegment(current_style.text_segment);
m_style_list.remove(i);
i--;
} else {
current_style.end_index -= size - removed;
removed = size;
}
}
} else {
if (current_style.start_index <= from && current_style.end_index >= from) {
found = true;
int left_in_style = (current_style.end_index + 1) - from;
int subtract = std::min(left_in_style, size);
current_style.end_index -= subtract;
current_style.text_dirty = true;
removed = subtract;
if (current_style.end_index < current_style.start_index) {
if (current_style.text_segment)
releaseTextSegment(current_style.text_segment);
m_style_list.remove(i);
i--;
}
}
}
}
TextStyle defaultStyle = m_screen->defaultTextStyle();
if (m_style_list.last().isCompatible(defaultStyle)) {
m_style_list.last().end_index += size;
m_style_list.last().text_dirty = true;
} else {
m_style_list.append(TextStyleLine(defaultStyle, m_style_list.last().end_index + 1,
m_style_list.last().end_index + size));
}
m_text_line.remove(from, size);
QString empty(size,' ');
m_text_line.append(empty);
}
void Line::setWidth(int width)
{
bool emit_changed = m_text_line.size() != width;
if (m_text_line.size() > width) {
m_text_line.chop(m_text_line.size() - width);
} else if (m_text_line.size() < width) {
m_text_line.append(QString(width - m_text_line.size(), QChar(' ')));
}
if (emit_changed)
emit widthChanged();
}
int Line::width() const
{
return m_style_list.size();
}
void Line::replaceAtPos(int pos, const QString &text, const TextStyle &style)
{
Q_ASSERT(pos + text.size() <= m_text_line.size());
m_changed = true;
m_text_line.replace(pos,text.size(),text);
bool found = false;
for (int i = 0; i < m_style_list.size(); i++) {
TextStyleLine &current_style = m_style_list[i];
if (found) {
if (current_style.end_index <= pos + text.size()) {
if (current_style.text_segment)
releaseTextSegment(current_style.text_segment);
m_style_list.remove(i);
i--;
} else if (current_style.start_index <= pos + text.size()) {
current_style.start_index = pos + text.size();
current_style.style_dirty = true;
current_style.text_dirty = true;
current_style.index_dirty = true;
} else {
break;
}
} else if (pos >= current_style.start_index && pos <= current_style.end_index) {
found = true;
if (pos + text.size() -1 <= current_style.end_index) {
if (current_style.isCompatible(style)) {
current_style.text_dirty = true;
} else {
if (current_style.start_index == pos && current_style.end_index == pos + text.size() - 1) {
current_style.setStyle(style);
} else if (current_style.start_index == pos) {
current_style.start_index = pos + text.size();
current_style.text_dirty = true;
m_style_list.insert(i, TextStyleLine(style,pos, pos+text.size() -1));
} else if (current_style.end_index == pos + text.size()) {
current_style.end_index = pos - 1;
current_style.text_dirty = true;
m_style_list.insert(i+1, TextStyleLine(style,pos, pos+text.size()));
} else {
int old_end = current_style.end_index;
current_style.end_index = pos - 1;
current_style.text_dirty = true;
m_style_list.insert(i+1, TextStyleLine(style,pos, pos + text.size() - 1));
if (pos + text.size() < m_text_line.size()) {
m_style_list.insert(i+2, TextStyleLine(current_style,pos + text.size(), old_end));
}
}
}
break;
} else {
if (current_style.isCompatible(style)) {
current_style.end_index = pos + text.size() - 1;
current_style.text_dirty = true;
} else {
if (current_style.start_index == pos) {
current_style.end_index = pos + text.size() - 1;
current_style.style = style.style;
current_style.forground = style.forground;
current_style.background = style.background;
current_style.text_dirty = true;
current_style.style_dirty = true;
} else {
current_style.end_index = pos - 1;
current_style.text_dirty = true;
m_style_list.insert(i+1, TextStyleLine(style, pos, pos + text.size() -1));
i++;
}
}
}
}
}
}
void Line::insertAtPos(int pos, const QString &text, const TextStyle &style)
{
m_changed = true;
m_text_line.insert(pos,text);
m_text_line.chop(text.size());
bool found = false;
for (int i = 0; i < m_style_list.size(); i++) {
TextStyleLine &current_style = m_style_list[i];
if (found) {
current_style.start_index += text.size();
current_style.end_index += text.size();
current_style.index_dirty = true;
if (current_style.start_index >= m_text_line.size()) {
releaseTextSegment(current_style.text_segment);
m_style_list.remove(i);
i--;
} else if (current_style.end_index >= m_text_line.size()) {
current_style.end_index = m_text_line.size()-1;
}
} else if (pos >= current_style.start_index && pos <= current_style.end_index) {
found = true;
if (current_style.start_index == pos) {
current_style.start_index += text.size();
current_style.end_index += text.size();
current_style.index_dirty = true;
m_style_list.insert(i, TextStyleLine(style, pos, pos+ text.size() - 1));
i++;
} else if (current_style.end_index == pos) {
current_style.end_index--;
current_style.text_dirty = true;
m_style_list.insert(i+1, TextStyleLine(style, pos, pos+ text.size() - 1));
i++;
} else {
int old_end = current_style.end_index;
current_style.end_index = pos -1;
current_style.text_dirty = true;
m_style_list.insert(i+1, TextStyleLine(style, pos, pos + text.size() - 1));
if (pos + text.size() < m_text_line.size()) {
int segment_end = std::min(m_text_line.size() -1, old_end + text.size());
m_style_list.insert(i+2, TextStyleLine(current_style, pos + text.size(), segment_end));
i+=2;
} else {
i++;
}
}
}
}
}
int Line::index() const
{
return m_index;
}
void Line::setIndex(int index)
{
m_index = index;
}
QString *Line::textLine()
{
return &m_text_line;
}
void Line::setVisible(bool visible)
{
if (visible != m_visible) {
m_visible = visible;
emit visibleChanged();
}
}
bool Line::visible() const
{
return m_visible;
}
void Line::dispatchEvents()
{
if (m_index != m_old_index) {
m_old_index = m_index;
emit indexChanged();
}
if (!m_changed) {
return;
}
for (int i = 0; i < m_style_list.size(); i++) {
TextStyleLine &current_style = m_style_list[i];
if (current_style.text_segment == 0) {
current_style.text_segment = createTextSegment(current_style);
}
if (current_style.style_dirty) {
current_style.text_segment->setTextStyle(current_style);
current_style.style_dirty = false;
}
if (current_style.index_dirty || current_style.text_dirty) {
current_style.text_segment->setStringSegment(current_style.start_index, current_style.end_index, current_style.text_dirty);
current_style.index_dirty = false;
current_style.text_dirty = false;
}
m_style_list.at(i).text_segment->dispatchEvents();
}
m_changed = false;
for (int i = 0; i< m_to_delete.size(); i++) {
delete m_to_delete[i];
}
m_to_delete.clear();
}
QVector<TextStyleLine> Line::style_list()
{
return m_style_list;
}
Text *Line::createTextSegment(const TextStyleLine &style_line)
{
Text *to_return;
if (m_to_delete.size()) {
to_return = m_to_delete.takeLast();
} else {
to_return = new Text(screen());
to_return->setLine(this);
emit textCreated(to_return);
}
to_return->setVisible(true);
return to_return;
}
void Line::releaseTextSegment(Text *text)
{
text->setVisible(false);
m_to_delete.append(text);
}
void Line::printStyleList() const
{
for (int i= 0; i < m_style_list.size(); i++) {
qDebug() << "i" << m_style_list.at(i);
}
}