ladybird/Libraries/LibGUI/GTextDocument.cpp
Andreas Kling f96c683543 LibGUI: Move visual line metadata from GTextDocument to GTextEditor
This patch decouples GTextDocument and GTextDocumentLine from the line
wrapping functionality of GTextEditor.

This should basically make it possible to have multiple GTextEditors
editing the same GTextDocument. Of course, that will require a bit more
work since there's no paint invalidation yet.
2019-10-27 18:00:07 +01:00

155 lines
3.4 KiB
C++

#include <LibGUI/GTextDocument.h>
#include <ctype.h>
GTextDocument::GTextDocument(Client* client)
{
if (client)
m_clients.set(client);
append_line(make<GTextDocumentLine>());
}
void GTextDocument::set_text(const StringView& text)
{
m_spans.clear();
remove_all_lines();
int start_of_current_line = 0;
auto add_line = [&](int current_position) {
int line_length = current_position - start_of_current_line;
auto line = make<GTextDocumentLine>();
if (line_length)
line->set_text(text.substring_view(start_of_current_line, current_position - start_of_current_line));
append_line(move(line));
start_of_current_line = current_position + 1;
};
int i = 0;
for (i = 0; i < text.length(); ++i) {
if (text[i] == '\n')
add_line(i);
}
add_line(i);
}
int GTextDocumentLine::first_non_whitespace_column() const
{
for (int i = 0; i < length(); ++i) {
if (!isspace(m_text[i]))
return i;
}
return length();
}
GTextDocumentLine::GTextDocumentLine()
{
clear();
}
GTextDocumentLine::GTextDocumentLine(const StringView& text)
{
set_text(text);
}
void GTextDocumentLine::clear()
{
m_text.clear();
m_text.append(0);
}
void GTextDocumentLine::set_text(const StringView& text)
{
if (text.length() == length() && !memcmp(text.characters_without_null_termination(), characters(), length()))
return;
if (text.is_empty()) {
clear();
return;
}
m_text.resize(text.length() + 1);
memcpy(m_text.data(), text.characters_without_null_termination(), text.length() + 1);
}
void GTextDocumentLine::append(const char* characters, int length)
{
int old_length = m_text.size() - 1;
m_text.resize(m_text.size() + length);
memcpy(m_text.data() + old_length, characters, length);
m_text.last() = 0;
}
void GTextDocumentLine::append(char ch)
{
insert(length(), ch);
}
void GTextDocumentLine::prepend(char ch)
{
insert(0, ch);
}
void GTextDocumentLine::insert(int index, char ch)
{
if (index == length()) {
m_text.last() = ch;
m_text.append(0);
} else {
m_text.insert(index, move(ch));
}
}
void GTextDocumentLine::remove(int index)
{
if (index == length()) {
m_text.take_last();
m_text.last() = 0;
} else {
m_text.remove(index);
}
}
void GTextDocumentLine::truncate(int length)
{
m_text.resize(length + 1);
m_text.last() = 0;
}
void GTextDocument::append_line(NonnullOwnPtr<GTextDocumentLine> line)
{
lines().append(move(line));
for (auto* client : m_clients)
client->document_did_append_line();
}
void GTextDocument::insert_line(int line_index, NonnullOwnPtr<GTextDocumentLine> line)
{
lines().insert(line_index, move(line));
for (auto* client : m_clients)
client->document_did_insert_line(line_index);
}
void GTextDocument::remove_line(int line_index)
{
lines().remove(line_index);
for (auto* client : m_clients)
client->document_did_remove_line(line_index);
}
void GTextDocument::remove_all_lines()
{
lines().clear();
for (auto* client : m_clients)
client->document_did_remove_all_lines();
}
GTextDocument::Client::~Client()
{
}
void GTextDocument::register_client(Client& client)
{
m_clients.set(&client);
}
void GTextDocument::unregister_client(Client& client)
{
m_clients.remove(&client);
}