summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AK/String.cpp2
-rw-r--r--Applications/TextEditor/main.cpp10
-rw-r--r--LibGUI/GTextEditor.cpp76
-rw-r--r--LibGUI/GTextEditor.h7
4 files changed, 88 insertions, 7 deletions
diff --git a/AK/String.cpp b/AK/String.cpp
index 8fd8e2fec2..4c3e2692ac 100644
--- a/AK/String.cpp
+++ b/AK/String.cpp
@@ -38,6 +38,8 @@ String String::isolated_copy() const
String String::substring(ssize_t start, ssize_t length) const
{
+ if (!length)
+ return empty();
ASSERT(m_impl);
ASSERT(start + length <= m_impl->length());
// FIXME: This needs some input bounds checking.
diff --git a/Applications/TextEditor/main.cpp b/Applications/TextEditor/main.cpp
index 9200794c22..3ee579032c 100644
--- a/Applications/TextEditor/main.cpp
+++ b/Applications/TextEditor/main.cpp
@@ -8,7 +8,6 @@
#include <LibGUI/GTextEditor.h>
#include <LibGUI/GAction.h>
#include <LibGUI/GFontDatabase.h>
-#include <LibGUI/GClipboard.h>
#include <AK/StringBuilder.h>
#include <unistd.h>
#include <stdio.h>
@@ -80,18 +79,15 @@ int main(int argc, char** argv)
});
auto cut_action = GAction::create("Cut", { Mod_Ctrl, Key_X }, GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/cut16.rgb", { 16, 16 }), [&] (const GAction&) {
- dbgprintf("FIXME: Implement Edit/Cut");
+ text_editor->cut();
});
auto copy_action = GAction::create("Copy", { Mod_Ctrl, Key_C }, GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/copyfile16.rgb", { 16, 16 }), [&] (const GAction&) {
- auto selected_text = text_editor->selected_text();
- printf("Copy: \"%s\"\n", selected_text.characters());
- GClipboard::the().set_data(selected_text);
+ text_editor->copy();
});
auto paste_action = GAction::create("Paste", { Mod_Ctrl, Key_V }, GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, "/res/icons/paste16.rgb", { 16, 16 }), [&] (const GAction&) {
- auto paste_text = GClipboard::the().data();
- printf("Paste: \"%s\"\n", paste_text.characters());
+ text_editor->paste();
});
auto menubar = make<GMenuBar>();
diff --git a/LibGUI/GTextEditor.cpp b/LibGUI/GTextEditor.cpp
index 6a2652685e..696bd80c5e 100644
--- a/LibGUI/GTextEditor.cpp
+++ b/LibGUI/GTextEditor.cpp
@@ -1,6 +1,7 @@
#include <LibGUI/GTextEditor.h>
#include <LibGUI/GScrollBar.h>
#include <LibGUI/GFontDatabase.h>
+#include <LibGUI/GClipboard.h>
#include <SharedGraphics/Painter.h>
#include <Kernel/KeyCode.h>
#include <AK/StringBuilder.h>
@@ -344,6 +345,14 @@ void GTextEditor::keydown_event(GKeyEvent& event)
return GWidget::keydown_event(event);
}
+void GTextEditor::insert_at_cursor(const String& text)
+{
+ // FIXME: This should obviously not be implemented this way.
+ for (int i = 0; i < text.length(); ++i) {
+ insert_at_cursor(text[i]);
+ }
+}
+
void GTextEditor::insert_at_cursor(char ch)
{
bool at_head = m_cursor.column() == 0;
@@ -449,6 +458,7 @@ void GTextEditor::set_cursor(int line, int column)
void GTextEditor::set_cursor(const GTextPosition& position)
{
+ ASSERT(!m_lines.is_empty());
if (m_cursor == position)
return;
auto old_cursor_line_rect = line_widget_rect(m_cursor.line());
@@ -596,3 +606,69 @@ String GTextEditor::selected_text() const
return builder.to_string();
}
+
+void GTextEditor::delete_selection()
+{
+ if (!has_selection())
+ return;
+
+ auto normalized_selection_start = m_selection_start;
+ auto normalized_selection_end = m_cursor;
+ if (m_cursor < m_selection_start)
+ swap(normalized_selection_start, normalized_selection_end);
+
+ for (int i = normalized_selection_start.line(); i <= normalized_selection_end.line();) {
+ auto& line = *m_lines[i];
+ int selection_start_column_on_line = normalized_selection_start.line() == i ? normalized_selection_start.column() : 0;
+ int selection_end_column_on_line = normalized_selection_end.line() == i ? normalized_selection_end.column() : line.length();
+ bool whole_line_is_selected = selection_start_column_on_line == 0 && selection_end_column_on_line == line.length();
+ if (whole_line_is_selected) {
+ m_lines.remove(i);
+ normalized_selection_end.set_line(normalized_selection_end.line() - 1);
+ continue;
+ }
+ auto before_selection = String(line.characters(), line.length()).substring(0, selection_start_column_on_line);
+ auto after_selection = String(line.characters(), line.length()).substring(selection_end_column_on_line, line.length() - selection_end_column_on_line);
+ StringBuilder builder(before_selection.length() + after_selection.length());
+ builder.append(before_selection);
+ builder.append(after_selection);
+ line.set_text(builder.to_string());
+ ++i;
+ }
+
+ if (m_lines.is_empty())
+ m_lines.append(make<Line>());
+
+ m_selection_start = { };
+ set_cursor(normalized_selection_start);
+ update();
+}
+
+void GTextEditor::insert_at_cursor_or_replace_selection(const String& text)
+{
+ if (has_selection())
+ delete_selection();
+ insert_at_cursor(text);
+}
+
+void GTextEditor::cut()
+{
+ auto selected_text = this->selected_text();
+ printf("Cut: \"%s\"\n", selected_text.characters());
+ GClipboard::the().set_data(selected_text);
+ delete_selection();
+}
+
+void GTextEditor::copy()
+{
+ auto selected_text = this->selected_text();
+ printf("Copy: \"%s\"\n", selected_text.characters());
+ GClipboard::the().set_data(selected_text);
+}
+
+void GTextEditor::paste()
+{
+ auto paste_text = GClipboard::the().data();
+ printf("Paste: \"%s\"\n", paste_text.characters());
+ insert_at_cursor_or_replace_selection(paste_text);
+}
diff --git a/LibGUI/GTextEditor.h b/LibGUI/GTextEditor.h
index 6ccc4b3297..145b42f1f6 100644
--- a/LibGUI/GTextEditor.h
+++ b/LibGUI/GTextEditor.h
@@ -56,6 +56,10 @@ public:
bool has_selection() const { return m_selection_start.is_valid(); }
String selected_text() const;
+ void cut();
+ void copy();
+ void paste();
+
private:
virtual void paint_event(GPaintEvent&) override;
virtual void resize_event(GResizeEvent&) override;
@@ -98,9 +102,12 @@ private:
const Line& current_line() const { return *m_lines[m_cursor.line()]; }
GTextPosition text_position_at(const Point&) const;
void insert_at_cursor(char);
+ void insert_at_cursor(const String&);
int ruler_width() const;
Rect ruler_content_rect(int line) const;
void toggle_selection_if_needed_for_event(const GKeyEvent&);
+ void insert_at_cursor_or_replace_selection(const String&);
+ void delete_selection();
GScrollBar* m_vertical_scrollbar { nullptr };
GScrollBar* m_horizontal_scrollbar { nullptr };