diff --git a/Libraries/LibGUI/GTextDocument.cpp b/Libraries/LibGUI/GTextDocument.cpp index 61d69d1125..a3217c06ea 100644 --- a/Libraries/LibGUI/GTextDocument.cpp +++ b/Libraries/LibGUI/GTextDocument.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -7,6 +8,12 @@ GTextDocument::GTextDocument(Client* client) if (client) m_clients.set(client); append_line(make(*this)); + + // TODO: Instead of a repating timer, this we should call a delayed 2 sec timer when the user types. + m_undo_timer = CTimer::construct( + 2000, [this] { + update_undo_timer(); + }); } void GTextDocument::set_text(const StringView& text) @@ -175,6 +182,11 @@ void GTextDocument::unregister_client(Client& client) } void GTextDocument::update_views(Badge) +{ + notify_did_change(); +} + +void GTextDocument::notify_did_change() { if (m_client_notifications_enabled) { for (auto* client : m_clients) @@ -182,6 +194,14 @@ void GTextDocument::update_views(Badge) } } +void GTextDocument::set_all_cursors(const GTextPosition& position) +{ + if (m_client_notifications_enabled) { + for (auto* client : m_clients) + client->document_did_set_cursor(position); + } +} + String GTextDocument::text_in_range(const GTextRange& a_range) const { auto range = a_range.normalized(); @@ -341,3 +361,196 @@ Optional GTextDocument::first_non_skippable_span_after(const } return {}; } + +void GTextDocument::undo() +{ + if (!can_undo()) + return; + + auto& undo_container = m_undo_stack[m_undo_stack_index]; + auto& undo_vector = undo_container.m_undo_vector; + + //If we try to undo a empty vector, delete it and skip over. + if (undo_vector.is_empty()) { + m_undo_stack.remove(m_undo_stack_index); + undo(); + return; + } + + for (int i = 0; i < undo_vector.size(); i++) { + auto& undo_command = undo_vector[i]; + undo_command.undo(); + } + + m_undo_stack_index++; + notify_did_change(); +} + +void GTextDocument::redo() +{ + if (!can_redo()) + return; + + auto& undo_container = m_undo_stack[m_undo_stack_index - 1]; + auto& redo_vector = undo_container.m_undo_vector; + + for (int i = redo_vector.size() - 1; i >= 0; i--) { + auto& undo_command = redo_vector[i]; + undo_command.redo(); + } + + m_undo_stack_index--; + notify_did_change(); +} + +void GTextDocument::add_to_undo_stack(NonnullOwnPtr undo_command) +{ + if (m_undo_stack.is_empty()) { + auto undo_commands_container = make(); + m_undo_stack.prepend(move(undo_commands_container)); + } + + // Clear the elements of the stack before the m_undo_stack_index (Excluding our new element) + for (int i = 1; i < m_undo_stack_index; i++) + m_undo_stack.remove(1); + + if (m_undo_stack_index > 0 && !m_undo_stack.is_empty()) + m_undo_stack[0].m_undo_vector.clear(); + + m_undo_stack_index = 0; + + m_undo_stack[0].m_undo_vector.prepend(move(undo_command)); +} + +GTextDocumentUndoCommand::GTextDocumentUndoCommand(GTextDocument& document) + : m_document(document) +{ +} + +GTextDocumentUndoCommand::~GTextDocumentUndoCommand() +{ +} + +InsertCharacterCommand::InsertCharacterCommand(GTextDocument& document, char ch, GTextPosition text_position) + : GTextDocumentUndoCommand(document) + , m_character(ch) + , m_text_position(text_position) +{ +} + +RemoveCharacterCommand::RemoveCharacterCommand(GTextDocument& document, char ch, GTextPosition text_position) + : GTextDocumentUndoCommand(document) + , m_character(ch) + , m_text_position(text_position) +{ +} + +RemoveLineCommand::RemoveLineCommand(GTextDocument& document, String line_content, GTextPosition text_position, bool has_merged_content) + : GTextDocumentUndoCommand(document) + , m_line_content(move(line_content)) + , m_text_position(text_position) + , m_has_merged_content(has_merged_content) +{ +} + +CreateLineCommand::CreateLineCommand(GTextDocument& document, Vector line_content, GTextPosition text_position) + : GTextDocumentUndoCommand(document) + , m_line_content(move(line_content)) + , m_text_position(text_position) +{ +} + +void InsertCharacterCommand::undo() +{ + m_document.lines()[m_text_position.line()].remove(m_document, (m_text_position.column() - 1)); + m_document.notify_did_change(); +} + +void InsertCharacterCommand::redo() +{ + m_document.lines()[m_text_position.line()].insert(m_document, m_text_position.column() - 1, m_character); +} + +void RemoveCharacterCommand::undo() +{ + m_document.lines()[m_text_position.line()].insert(m_document, m_text_position.column(), m_character); +} + +void RemoveCharacterCommand::redo() +{ + m_document.lines()[m_text_position.line()].remove(m_document, (m_text_position.column())); + m_document.notify_did_change(); +} + +void RemoveLineCommand::undo() +{ + // Insert back the line + m_document.insert_line(m_text_position.line(), make(m_document, m_line_content)); + + // Remove the merged line contents + if (m_has_merged_content) { + for (int i = m_line_content.length() - 1; i >= 0; i--) + m_document.lines()[m_text_position.line() - 1].remove(m_document, (m_text_position.column()) + i); + } +} + +void RemoveLineCommand::redo() +{ + // Remove the created line + m_document.remove_line(m_text_position.line()); + + // Add back the line contents + if (m_has_merged_content) { + for (int i = 0; i < m_line_content.length(); i++) + m_document.lines()[m_text_position.line() - 1].insert(m_document, (m_text_position.column()) + i, m_line_content[i]); + } +} + +void CreateLineCommand::undo() +{ + // Insert back the created line portion + for (int i = 0; i < m_line_content.size(); i++) + m_document.lines()[m_text_position.line()].insert(m_document, (m_text_position.column() - 1) + i, m_line_content[i]); + + // Move the cursor up a row back before the split. + m_document.set_all_cursors({ m_text_position.line(), m_document.lines()[m_text_position.line()].length() }); + + // Remove the created line + m_document.remove_line(m_text_position.line() + 1); +} + +void CreateLineCommand::redo() +{ + // Remove the characters that we're inserted back + for (int i = m_line_content.size() - 1; i >= 0; i--) + m_document.lines()[m_text_position.line()].remove(m_document, (m_text_position.column()) + i); + + m_document.notify_did_change(); + + // Then we want to add BACK the created line + m_document.insert_line(m_text_position.line() + 1, make(m_document, "")); + + for (int i = 0; i < m_line_content.size(); i++) + m_document.lines()[m_text_position.line() + 1].insert(m_document, i, m_line_content[i]); +} + +void GTextDocument::update_undo_timer() +{ + if (m_undo_stack.is_empty()) + return; + + auto& undo_vector = m_undo_stack[0].m_undo_vector; + + if (undo_vector.size() == m_last_updated_undo_vector_size && !undo_vector.is_empty()) { + auto undo_commands_container = make(); + m_undo_stack.prepend(move(undo_commands_container)); + // Note: Remove dbg() if we're 100% sure there are no bugs left. + dbg() << "Undo stack increased to " << m_undo_stack.size(); + + // Shift the index to the left since we're adding an empty container. + if (m_undo_stack_index > 0) + m_undo_stack_index++; + } + + m_last_updated_undo_vector_size = undo_vector.size(); +} diff --git a/Libraries/LibGUI/GTextDocument.h b/Libraries/LibGUI/GTextDocument.h index f8b51cc126..3dd2f08bf6 100644 --- a/Libraries/LibGUI/GTextDocument.h +++ b/Libraries/LibGUI/GTextDocument.h @@ -5,11 +5,13 @@ #include #include #include +#include #include #include #include class GTextEditor; +class GTextDocument; class GTextDocumentLine; struct GTextDocumentSpan { @@ -21,6 +23,62 @@ struct GTextDocumentSpan { void* data { nullptr }; }; +class GTextDocumentUndoCommand { +public: + GTextDocumentUndoCommand(GTextDocument&); + virtual ~GTextDocumentUndoCommand(); + virtual void undo() {} + virtual void redo() {} + +protected: + GTextDocument& m_document; +}; + +class InsertCharacterCommand : public GTextDocumentUndoCommand { +public: + InsertCharacterCommand(GTextDocument&, char, GTextPosition); + virtual void undo() override; + virtual void redo() override; + +private: + char m_character; + GTextPosition m_text_position; +}; + +class RemoveCharacterCommand : public GTextDocumentUndoCommand { +public: + RemoveCharacterCommand(GTextDocument&, char, GTextPosition); + virtual void undo() override; + virtual void redo() override; + +private: + char m_character; + GTextPosition m_text_position; +}; + +class RemoveLineCommand : public GTextDocumentUndoCommand { +public: + RemoveLineCommand(GTextDocument&, String, GTextPosition, bool has_merged_content); + virtual void undo() override; + virtual void redo() override; + +private: + String m_line_content; + GTextPosition m_text_position; + bool m_has_merged_content; +}; + +class CreateLineCommand : public GTextDocumentUndoCommand { +public: + CreateLineCommand(GTextDocument&, Vector line_content, GTextPosition); + virtual void undo() override; + virtual void redo() override; + +private: + Vector m_line_content; + GTextPosition m_text_position; +}; + class GTextDocument : public RefCounted { public: enum class SearchShouldWrap { @@ -37,6 +95,7 @@ public: virtual void document_did_remove_all_lines() = 0; virtual void document_did_change() = 0; virtual void document_did_set_text() = 0; + virtual void document_did_set_cursor(const GTextPosition&) = 0; }; static NonnullRefPtr create(Client* client = nullptr) @@ -84,14 +143,37 @@ public: Optional first_non_skippable_span_before(const GTextPosition&) const; Optional first_non_skippable_span_after(const GTextPosition&) const; + struct UndoCommandsContainer { + NonnullOwnPtrVector m_undo_vector; + }; + + void add_to_undo_stack(NonnullOwnPtr); + + bool can_undo() const { return m_undo_stack_index < m_undo_stack.size() && !m_undo_stack.is_empty(); } + bool can_redo() const { return m_undo_stack_index > 0 && m_undo_stack[m_undo_stack_index - 1].m_undo_vector.size() > 0 && !m_undo_stack.is_empty(); } + + void undo(); + void redo(); + + void notify_did_change(); + void set_all_cursors(const GTextPosition&); + private: explicit GTextDocument(Client* client); + void update_undo_timer(); + NonnullOwnPtrVector m_lines; Vector m_spans; HashTable m_clients; bool m_client_notifications_enabled { true }; + + NonnullOwnPtrVector m_undo_stack; + int m_undo_stack_index { 0 }; + int m_last_updated_undo_vector_size = 0; + + RefPtr m_undo_timer; }; class GTextDocumentLine { diff --git a/Libraries/LibGUI/GTextEditor.cpp b/Libraries/LibGUI/GTextEditor.cpp index fc0ff632dc..7e4b1ce92c 100644 --- a/Libraries/LibGUI/GTextEditor.cpp +++ b/Libraries/LibGUI/GTextEditor.cpp @@ -35,13 +35,6 @@ GTextEditor::GTextEditor(Type type, GWidget* parent) vertical_scrollbar().set_step(line_height()); m_cursor = { 0, 0 }; create_actions(); - - // TODO: Instead of a repating timer, this we should call a delayed 2 sec timer when the user types. - m_undo_timer = CTimer::construct( - 2000, [&] { - update_undo_timer(); - }, - this); } GTextEditor::~GTextEditor() @@ -479,47 +472,6 @@ void GTextEditor::select_all() update(); } -void GTextEditor::undo() -{ - if (!can_undo()) - return; - - auto& undo_container = m_undo_stack[m_undo_stack_index]; - auto& undo_vector = undo_container.m_undo_vector; - - //If we try to undo a empty vector, delete it and skip over. - if (undo_vector.is_empty()) { - m_undo_stack.remove(m_undo_stack_index); - undo(); - return; - } - - for (int i = 0; i < undo_vector.size(); i++) { - auto& undo_command = undo_vector[i]; - undo_command.undo(); - } - - m_undo_stack_index++; - did_change(); -} - -void GTextEditor::redo() -{ - if (!can_redo()) - return; - - auto& undo_container = m_undo_stack[m_undo_stack_index - 1]; - auto& redo_vector = undo_container.m_undo_vector; - - for (int i = redo_vector.size() - 1; i >= 0; i--) { - auto& undo_command = redo_vector[i]; - undo_command.redo(); - } - - m_undo_stack_index--; - did_change(); -} - void GTextEditor::get_selection_line_boundaries(int& first_line, int& last_line) { auto selection = normalized_selection(); @@ -817,7 +769,7 @@ void GTextEditor::keydown_event(GKeyEvent& event) for (int i = 0; i < erase_count; ++i) { int row = m_cursor.line(); int column = m_cursor.column() - 1 - i; - add_to_undo_stack(make(*this, document().line(row).characters()[column], GTextPosition(row, column))); + document().add_to_undo_stack(make(document(), document().line(row).characters()[column], GTextPosition(row, column))); current_line().remove(document(), m_cursor.column() - 1 - i); } update_content_size(); @@ -832,7 +784,7 @@ void GTextEditor::keydown_event(GKeyEvent& event) int row = m_cursor.line(); int column = previous_length; - add_to_undo_stack(make(*this, String(lines()[m_cursor.line()].view()), GTextPosition(row, column), true)); + document().add_to_undo_stack(make(document(), String(lines()[m_cursor.line()].view()), GTextPosition(row, column), true)); previous_line.append(document(), current_line().characters(), current_line().length()); document().remove_line(m_cursor.line()); @@ -938,7 +890,7 @@ void GTextEditor::insert_at_cursor(char ch) Vector line_content; for (int i = m_cursor.column(); i < document().lines()[row].length(); i++) line_content.append(document().lines()[row].characters()[i]); - add_to_undo_stack(make(*this, line_content, GTextPosition(row, column))); + document().add_to_undo_stack(make(document(), line_content, GTextPosition(row, column))); document().insert_line(m_cursor.line() + (at_tail ? 1 : 0), make(document(), new_line_contents)); update(); @@ -954,7 +906,7 @@ void GTextEditor::insert_at_cursor(char ch) Vector line_content; for (int i = 0; i < new_line->length(); i++) line_content.append(new_line->characters()[i]); - add_to_undo_stack(make(*this, line_content, GTextPosition(row, column))); + document().add_to_undo_stack(make(document(), line_content, GTextPosition(row, column))); current_line().truncate(document(), m_cursor.column()); document().insert_line(m_cursor.line() + 1, move(new_line)); @@ -977,7 +929,7 @@ void GTextEditor::insert_at_cursor(char ch) did_change(); set_cursor(m_cursor.line(), m_cursor.column() + 1); - add_to_undo_stack(make(*this, ch, m_cursor)); + document().add_to_undo_stack(make(document(), ch, m_cursor)); } int GTextEditor::content_x_for_position(const GTextPosition& position) const @@ -1090,27 +1042,6 @@ void GTextEditor::update_cursor() update(line_widget_rect(m_cursor.line())); } -void GTextEditor::update_undo_timer() -{ - if (m_undo_stack.is_empty()) - return; - - auto& undo_vector = m_undo_stack[0].m_undo_vector; - - if (undo_vector.size() == m_last_updated_undo_vector_size && !undo_vector.is_empty()) { - auto undo_commands_container = make(); - m_undo_stack.prepend(move(undo_commands_container)); - // Note: Remove dbg() if we're 100% sure there are no bugs left. - dbg() << "Undo stack increased to " << m_undo_stack.size(); - - // Shift the index to the left since we're adding an empty container. - if (m_undo_stack_index > 0) - m_undo_stack_index++; - } - - m_last_updated_undo_vector_size = undo_vector.size(); -} - void GTextEditor::set_cursor(int line, int column) { set_cursor({ line, column }); @@ -1249,7 +1180,7 @@ void GTextEditor::delete_selection() for (int i = selection.start().line() + 1; i < selection.end().line();) { int row = i; int column = lines()[i].length(); - add_to_undo_stack(make(*this, String(lines()[i].view()), GTextPosition(row, column), false)); + document().add_to_undo_stack(make(document(), String(lines()[i].view()), GTextPosition(row, column), false)); document().remove_line(i); selection.end().set_line(selection.end().line() - 1); @@ -1263,7 +1194,7 @@ void GTextEditor::delete_selection() for (int i = selection.end().column() - 1; i >= selection.start().column(); i--) { int row = selection.start().line(); int column = i; - add_to_undo_stack(make(*this, document().line(row).characters()[column], GTextPosition(row, column))); + document().add_to_undo_stack(make(document(), document().line(row).characters()[column], GTextPosition(row, column))); } if (whole_line_is_selected) { @@ -1290,16 +1221,16 @@ void GTextEditor::delete_selection() for (int i = first_line.length() - 1; i > selection.start().column() - 1; i--) { int row = selection.start().line(); int column = i; - add_to_undo_stack(make(*this, document().line(row).characters()[column], GTextPosition(row, column))); + document().add_to_undo_stack(make(document(), document().line(row).characters()[column], GTextPosition(row, column))); } - add_to_undo_stack(make(*this, String(second_line.view()), selection.end(), false)); + document().add_to_undo_stack(make(document(), String(second_line.view()), selection.end(), false)); first_line.set_text(document(), builder.to_string()); document().remove_line(selection.end().line()); for (int i = (first_line.length()) - after_selection.length(); i < first_line.length(); i++) - add_to_undo_stack(make(*this, first_line.characters()[i], GTextPosition(selection.start().line(), i + 1))); + document().add_to_undo_stack(make(document(), first_line.characters()[i], GTextPosition(selection.start().line(), i + 1))); } if (lines().is_empty()) { @@ -1464,25 +1395,6 @@ void GTextEditor::ensure_cursor_is_valid() set_cursor(cursor().line(), cursor().column() - (lines()[cursor().line()].length() - cursor().column())); } -void GTextEditor::add_to_undo_stack(NonnullOwnPtr undo_command) -{ - if (m_undo_stack.is_empty()) { - auto undo_commands_container = make(); - m_undo_stack.prepend(move(undo_commands_container)); - } - - // Clear the elements of the stack before the m_undo_stack_index (Excluding our new element) - for (int i = 1; i < m_undo_stack_index; i++) - m_undo_stack.remove(1); - - if (m_undo_stack_index > 0 && !m_undo_stack.is_empty()) - m_undo_stack[0].m_undo_vector.clear(); - - m_undo_stack_index = 0; - - m_undo_stack[0].m_undo_vector.prepend(move(undo_command)); -} - int GTextEditor::visual_line_containing(int line_index, int column) const { int visual_line_index = 0; @@ -1609,8 +1521,12 @@ void GTextEditor::document_did_insert_line(int line_index) void GTextEditor::document_did_change() { + ensure_cursor_is_valid(); recompute_all_visual_lines(); update(); + + undo_action().set_enabled(can_undo()); + redo_action().set_enabled(can_redo()); } void GTextEditor::document_did_set_text() @@ -1621,6 +1537,11 @@ void GTextEditor::document_did_set_text() document_did_change(); } +void GTextEditor::document_did_set_cursor(const GTextPosition& position) +{ + set_cursor(position); +} + void GTextEditor::set_document(GTextDocument& document) { if (m_document.ptr() == &document) @@ -1638,121 +1559,6 @@ void GTextEditor::set_document(GTextDocument& document) m_document->register_client(*this); } -GTextEditor::UndoCommand::UndoCommand(GTextEditor& text_editor) - : m_text_editor(text_editor) -{ -} - -GTextEditor::UndoCommand::~UndoCommand() -{ -} - -void GTextEditor::UndoCommand::undo() {} -void GTextEditor::UndoCommand::redo() {} - -GTextEditor::InsertCharacterCommand::InsertCharacterCommand(GTextEditor& text_editor, char ch, GTextPosition text_position) - : UndoCommand(text_editor) - , m_character(ch) - , m_text_position(text_position) -{ -} - -GTextEditor::RemoveCharacterCommand::RemoveCharacterCommand(GTextEditor& text_editor, char ch, GTextPosition text_position) - : UndoCommand(text_editor) - , m_character(ch) - , m_text_position(text_position) -{ -} - -GTextEditor::RemoveLineCommand::RemoveLineCommand(GTextEditor& text_editor, String line_content, GTextPosition text_position, bool has_merged_content) - : UndoCommand(text_editor) - , m_line_content(line_content) - , m_text_position(text_position) - , m_has_merged_content(has_merged_content) -{ -} - -GTextEditor::CreateLineCommand::CreateLineCommand(GTextEditor& text_editor, Vector line_content, GTextPosition text_position) - : UndoCommand(text_editor) - , m_line_content(line_content) - , m_text_position(text_position) -{ -} - -void GTextEditor::InsertCharacterCommand::undo() -{ - m_text_editor.lines()[m_text_position.line()].remove(m_text_editor.document(), (m_text_position.column() - 1)); - m_text_editor.ensure_cursor_is_valid(); -} - -void GTextEditor::InsertCharacterCommand::redo() -{ - m_text_editor.lines()[m_text_position.line()].insert(m_text_editor.document(), m_text_position.column() - 1, m_character); -} - -void GTextEditor::RemoveCharacterCommand::undo() -{ - m_text_editor.lines()[m_text_position.line()].insert(m_text_editor.document(), m_text_position.column(), m_character); -} - -void GTextEditor::RemoveCharacterCommand::redo() -{ - m_text_editor.lines()[m_text_position.line()].remove(m_text_editor.document(), (m_text_position.column())); - m_text_editor.ensure_cursor_is_valid(); -} - -void GTextEditor::RemoveLineCommand::undo() -{ - // Insert back the line - m_text_editor.document().insert_line(m_text_position.line(), make(m_text_editor.document(), m_line_content)); - - // Remove the merged line contents - if (m_has_merged_content) { - for (int i = m_line_content.length() - 1; i >= 0; i--) - m_text_editor.document().lines()[m_text_position.line() - 1].remove(m_text_editor.document(), (m_text_position.column()) + i); - } -} - -void GTextEditor::RemoveLineCommand::redo() -{ - // Remove the created line - m_text_editor.document().remove_line(m_text_position.line()); - - // Add back the line contents - if (m_has_merged_content) { - for (int i = 0; i < m_line_content.length(); i++) - m_text_editor.document().lines()[m_text_position.line() - 1].insert(m_text_editor.document(), (m_text_position.column()) + i, m_line_content[i]); - } -} - -void GTextEditor::CreateLineCommand::undo() -{ - // Insert back the created line portion - for (int i = 0; i < m_line_content.size(); i++) - m_text_editor.document().lines()[m_text_position.line()].insert(m_text_editor.document(), (m_text_position.column() - 1) + i, m_line_content[i]); - - // Move the cursor up a row back before the split. - m_text_editor.set_cursor(m_text_position.line(), m_text_editor.document().lines()[m_text_position.line()].length()); - - // Remove the created line - m_text_editor.document().remove_line(m_text_position.line() + 1); -} - -void GTextEditor::CreateLineCommand::redo() -{ - // Remove the characters that we're inserted back - for (int i = m_line_content.size() - 1; i >= 0; i--) - m_text_editor.document().lines()[m_text_position.line()].remove(m_text_editor.document(), (m_text_position.column()) + i); - - m_text_editor.ensure_cursor_is_valid(); - - // Then we want to add BACK the created line - m_text_editor.document().insert_line(m_text_position.line() + 1, make(m_text_editor.document(), "")); - - for (int i = 0; i < m_line_content.size(); i++) - m_text_editor.document().lines()[m_text_position.line() + 1].insert(m_text_editor.document(), i, m_line_content[i]); -} - void GTextEditor::flush_pending_change_notification_if_needed() { if (!m_has_pending_change_notification) diff --git a/Libraries/LibGUI/GTextEditor.h b/Libraries/LibGUI/GTextEditor.h index 3ad1321f95..a5be13a415 100644 --- a/Libraries/LibGUI/GTextEditor.h +++ b/Libraries/LibGUI/GTextEditor.h @@ -69,8 +69,8 @@ public: bool has_selection() const { return m_selection.is_valid(); } String selected_text() const; void set_selection(const GTextRange&); - bool can_undo() const { return m_undo_stack_index < m_undo_stack.size() && !m_undo_stack.is_empty(); } - bool can_redo() const { return m_undo_stack_index > 0 && m_undo_stack[m_undo_stack_index - 1].m_undo_vector.size() > 0 && !m_undo_stack.is_empty(); } + bool can_undo() const { return document().can_undo(); } + bool can_redo() const { return document().can_redo(); } String text() const; @@ -82,8 +82,8 @@ public: void do_delete(); void delete_current_line(); void select_all(); - void undo(); - void redo(); + void undo() { document().undo(); } + void redo() { document().redo(); } Function on_change; Function on_return_pressed; @@ -134,6 +134,7 @@ private: virtual void document_did_remove_all_lines() override; virtual void document_did_change() override; virtual void document_did_set_text() override; + virtual void document_did_set_cursor(const GTextPosition&) override; void create_actions(); void paint_ruler(Painter&); @@ -145,7 +146,6 @@ private: Rect cursor_content_rect() const; Rect content_rect_for_position(const GTextPosition&) const; void update_cursor(); - void update_undo_timer(); const NonnullOwnPtrVector& lines() const { return document().lines(); } NonnullOwnPtrVector& lines() { return document().lines(); } GTextDocumentLine& line(int index) { return document().line(index); } @@ -171,68 +171,6 @@ private: void move_selected_lines_down(); void sort_selected_lines(); - class UndoCommand { - - public: - UndoCommand(GTextEditor& text_editor); - virtual ~UndoCommand(); - virtual void undo(); - virtual void redo(); - - protected: - GTextEditor& m_text_editor; - }; - - class InsertCharacterCommand : public UndoCommand { - public: - InsertCharacterCommand(GTextEditor& text_editor, char ch, GTextPosition text_position); - virtual void undo() override; - virtual void redo() override; - - private: - char m_character; - GTextPosition m_text_position; - }; - - class RemoveCharacterCommand : public UndoCommand { - public: - RemoveCharacterCommand(GTextEditor& text_editor, char ch, GTextPosition text_position); - virtual void undo() override; - virtual void redo() override; - - private: - char m_character; - GTextPosition m_text_position; - }; - - class RemoveLineCommand : public UndoCommand { - public: - RemoveLineCommand(GTextEditor& text_editor, String, GTextPosition text_position, bool has_merged_content); - virtual void undo() override; - virtual void redo() override; - - private: - String m_line_content; - GTextPosition m_text_position; - bool m_has_merged_content; - }; - - class CreateLineCommand : public UndoCommand { - public: - CreateLineCommand(GTextEditor& text_editor, Vector line_content, GTextPosition text_position); - virtual void undo() override; - virtual void redo() override; - - private: - Vector m_line_content; - GTextPosition m_text_position; - }; - - struct UndoCommandsContainer { - NonnullOwnPtrVector m_undo_vector; - }; - - void add_to_undo_stack(NonnullOwnPtr undo_command); int visual_line_containing(int line_index, int column) const; void recompute_visual_lines(int line_index); @@ -260,10 +198,6 @@ private: RefPtr m_delete_action; CElapsedTimer m_triple_click_timer; NonnullRefPtrVector m_custom_context_menu_actions; - NonnullOwnPtrVector m_undo_stack; - int m_undo_stack_index = 0; - RefPtr m_undo_timer; - int m_last_updated_undo_vector_size = 0; RefPtr m_document;