From 84dd0ce1ae2373bd043cb3dfd6341cd0e7efdc8e Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Tue, 9 Jan 2024 20:19:45 +0000 Subject: [PATCH] HexEditor: Wrap selection and behavior in a struct --- Userland/Applications/HexEditor/HexEditor.cpp | 86 ++++++++----------- Userland/Applications/HexEditor/HexEditor.h | 12 +-- .../HexEditor/HexEditorWidget.cpp | 10 +-- Userland/Applications/HexEditor/Selection.h | 23 +++++ 4 files changed, 72 insertions(+), 59 deletions(-) create mode 100644 Userland/Applications/HexEditor/Selection.h diff --git a/Userland/Applications/HexEditor/HexEditor.cpp b/Userland/Applications/HexEditor/HexEditor.cpp index 39cdf4ea7b..a1b25197f5 100644 --- a/Userland/Applications/HexEditor/HexEditor.cpp +++ b/Userland/Applications/HexEditor/HexEditor.cpp @@ -50,8 +50,7 @@ ErrorOr HexEditor::open_new_file(size_t size) set_content_length(m_document->size()); m_position = 0; m_cursor_at_low_nibble = false; - m_selection_start = 0; - m_selection_end = 0; + m_selection.clear(); scroll_position_into_view(m_position); update(); update_status(); @@ -65,8 +64,7 @@ void HexEditor::open_file(NonnullOwnPtr file) set_content_length(m_document->size()); m_position = 0; m_cursor_at_low_nibble = false; - m_selection_start = 0; - m_selection_end = 0; + m_selection.clear(); scroll_position_into_view(m_position); update(); update_status(); @@ -80,22 +78,22 @@ ErrorOr HexEditor::fill_selection(u8 fill_byte) ByteBuffer old_values; ByteBuffer new_values; - size_t length = m_selection_end - m_selection_start; + size_t length = m_selection.size(); new_values.resize(length); old_values.resize(length); for (size_t i = 0; i < length; i++) { - size_t position = m_selection_start + i; + size_t position = m_selection.start + i; old_values[i] = m_document->get(position).value; new_values[i] = fill_byte; m_document->set(position, fill_byte); } - auto result = did_complete_action(m_selection_start, move(old_values), move(new_values)); + auto result = did_complete_action(m_selection.start, move(old_values), move(new_values)); if (result.is_error()) { for (size_t i = 0; i < length; i++) { - size_t position = m_selection_start + i; + size_t position = m_selection.start + i; m_document->set(position, old_values[i]); } return result; @@ -117,6 +115,7 @@ void HexEditor::set_position(size_t position) scroll_position_into_view(position); update_status(); } + void HexEditor::set_selection(size_t position, size_t length) { if (position > m_document->size() || position + length > m_document->size()) @@ -124,8 +123,8 @@ void HexEditor::set_selection(size_t position, size_t length) m_position = position; m_cursor_at_low_nibble = false; - m_selection_start = position; - m_selection_end = position + length; + m_selection.start = position; + m_selection.end = position + length; scroll_position_into_view(position); update_status(); } @@ -156,20 +155,13 @@ ErrorOr HexEditor::save() return {}; } -size_t HexEditor::selection_size() const -{ - if (!has_selection()) - return 0; - return m_selection_end - m_selection_start; -} - bool HexEditor::copy_selected_hex_to_clipboard() { if (!has_selection()) return false; StringBuilder output_string_builder; - for (size_t i = m_selection_start; i < m_selection_end; i++) + for (size_t i = m_selection.start; i < m_selection.end; i++) output_string_builder.appendff("{:02X} ", m_document->get(i).value); GUI::Clipboard::the().set_plain_text(output_string_builder.to_byte_string()); @@ -182,7 +174,7 @@ bool HexEditor::copy_selected_text_to_clipboard() return false; StringBuilder output_string_builder; - for (size_t i = m_selection_start; i < m_selection_end; i++) + for (size_t i = m_selection.start; i < m_selection.end; i++) output_string_builder.append(isprint(m_document->get(i).value) ? m_document->get(i).value : '.'); GUI::Clipboard::the().set_plain_text(output_string_builder.to_byte_string()); @@ -195,11 +187,11 @@ bool HexEditor::copy_selected_hex_to_clipboard_as_c_code() return false; StringBuilder output_string_builder; - output_string_builder.appendff("unsigned char raw_data[{}] = {{\n", m_selection_end - m_selection_start); + output_string_builder.appendff("unsigned char raw_data[{}] = {{\n", m_selection.end - m_selection.start); output_string_builder.append(" "sv); - for (size_t i = m_selection_start, j = 1; i < m_selection_end; i++, j++) { + for (size_t i = m_selection.start, j = 1; i < m_selection.end; i++, j++) { output_string_builder.appendff("{:#02X}", m_document->get(i).value); - if (i >= m_selection_end - 1) + if (i >= m_selection.end - 1) continue; if ((j % 12) == 0) output_string_builder.append(",\n "sv); @@ -241,11 +233,11 @@ Optional HexEditor::get_byte(size_t position) ByteBuffer HexEditor::get_selected_bytes() { - auto num_selected_bytes = m_selection_end - m_selection_start; + auto num_selected_bytes = m_selection.size(); ByteBuffer data; data.ensure_capacity(num_selected_bytes); - for (size_t i = m_selection_start; i < m_selection_end; i++) + for (size_t i = m_selection.start; i < m_selection.end; i++) data.append(m_document->get(i).value); return data; @@ -287,8 +279,8 @@ void HexEditor::mousedown_event(GUI::MouseEvent& event) m_cursor_at_low_nibble = false; m_position = offset; m_in_drag_select = true; - m_selection_start = offset; - m_selection_end = offset; + m_selection.start = offset; + m_selection.end = offset; update(); update_status(); } @@ -309,8 +301,8 @@ void HexEditor::mousedown_event(GUI::MouseEvent& event) m_position = offset; m_cursor_at_low_nibble = false; m_in_drag_select = true; - m_selection_start = offset; - m_selection_end = offset; + m_selection.start = offset; + m_selection.end = offset; m_edit_mode = EditMode::Text; update(); update_status(); @@ -353,8 +345,8 @@ void HexEditor::mousemove_event(GUI::MouseEvent& event) if (offset > m_document->size()) return; - m_selection_end = offset; - m_position = (m_selection_end <= m_selection_start) ? offset : offset - 1; + m_selection.end = offset; + m_position = (m_selection.end <= m_selection.start) ? offset : offset - 1; scroll_position_into_view(offset); } @@ -368,8 +360,8 @@ void HexEditor::mousemove_event(GUI::MouseEvent& event) if (offset > m_document->size()) return; - m_selection_end = offset; - m_position = (m_selection_end <= m_selection_start) ? offset : offset - 1; + m_selection.end = offset; + m_position = (m_selection.end <= m_selection.start) ? offset : offset - 1; scroll_position_into_view(offset); } update_status(); @@ -382,11 +374,9 @@ void HexEditor::mouseup_event(GUI::MouseEvent& event) { if (event.button() == GUI::MouseButton::Primary) { if (m_in_drag_select) { - if (m_selection_end < m_selection_start) { + if (m_selection.end < m_selection.start) { // lets flip these around - auto start = m_selection_end; - m_selection_end = m_selection_start; - m_selection_start = start; + swap(m_selection.start, m_selection.end); } m_in_drag_select = false; } @@ -415,14 +405,15 @@ void HexEditor::keydown_event(GUI::KeyEvent& event) auto move_and_update_cursor_by = [&](i64 cursor_location_change) { size_t new_position = m_position + cursor_location_change; if (event.modifiers() & Mod_Shift) { - size_t selection_pivot = m_position == m_selection_end ? m_selection_start : m_selection_end; + size_t selection_pivot = m_position == m_selection.end ? m_selection.start : m_selection.end; m_position = new_position; - m_selection_start = selection_pivot; - m_selection_end = m_position; - if (m_selection_start > m_selection_end) - swap(m_selection_start, m_selection_end); - } else - m_selection_start = m_selection_end = m_position = new_position; + m_selection.start = selection_pivot; + m_selection.end = m_position; + if (m_selection.start > m_selection.end) + swap(m_selection.start, m_selection.end); + } else { + m_selection.start = m_selection.end = m_position = new_position; + } m_cursor_at_low_nibble = false; scroll_position_into_view(m_position); update(); @@ -565,7 +556,7 @@ ErrorOr HexEditor::text_mode_keydown_event(GUI::KeyEvent& event) void HexEditor::update_status() { if (on_status_change) - on_status_change(m_position, m_edit_mode, m_selection_start, m_selection_end); + on_status_change(m_position, m_edit_mode, m_selection); } void HexEditor::did_change() @@ -659,8 +650,7 @@ void HexEditor::paint_event(GUI::PaintEvent& event) auto high_nibble = line.substring_from_byte_offset(0, 1).release_value_but_fixme_should_propagate_errors(); auto low_nibble = line.substring_from_byte_offset(1).release_value_but_fixme_should_propagate_errors(); - bool const selected = min(m_selection_start, m_selection_end) <= byte_position - && byte_position < max(m_selection_start, m_selection_end); + bool const selected = m_selection.contains(byte_position); // Styling priorities are as follows, with smaller numbers beating larger ones: // 1. Modified bytes @@ -754,8 +744,8 @@ void HexEditor::select_all() void HexEditor::highlight(size_t start, size_t end) { - m_selection_start = start; - m_selection_end = end; + m_selection.start = start; + m_selection.end = end; set_position(start); } diff --git a/Userland/Applications/HexEditor/HexEditor.h b/Userland/Applications/HexEditor/HexEditor.h index 302a46e970..999689d53a 100644 --- a/Userland/Applications/HexEditor/HexEditor.h +++ b/Userland/Applications/HexEditor/HexEditor.h @@ -11,6 +11,7 @@ #include "HexDocument.h" #include "SearchResultsModel.h" +#include "Selection.h" #include #include #include @@ -47,9 +48,9 @@ public: GUI::UndoStack& undo_stack(); void select_all(); - bool has_selection() const { return m_selection_start < m_selection_end && m_document->size() > 0; } - size_t selection_size() const; - size_t selection_start_offset() const { return m_selection_start; } + Selection const& selection() const { return m_selection; } + bool has_selection() const { return !m_selection.is_empty() && m_document->size() > 0; } + size_t selection_start_offset() const { return m_selection.start; } bool copy_selected_text_to_clipboard(); bool copy_selected_hex_to_clipboard(); bool copy_selected_hex_to_clipboard_as_c_code(); @@ -64,7 +65,7 @@ public: Optional find_and_highlight(ByteBuffer& needle, size_t start = 0); Vector find_all(ByteBuffer& needle, size_t start = 0); Vector find_all_strings(size_t min_length = 4); - Function on_status_change; // position, edit mode, selection start, selection end + Function on_status_change; Function on_change; protected: @@ -81,8 +82,7 @@ private: size_t m_content_length { 0 }; size_t m_bytes_per_row { 16 }; bool m_in_drag_select { false }; - size_t m_selection_start { 0 }; - size_t m_selection_end { 0 }; + Selection m_selection; size_t m_position { 0 }; bool m_cursor_at_low_nibble { false }; EditMode m_edit_mode { Hex }; diff --git a/Userland/Applications/HexEditor/HexEditorWidget.cpp b/Userland/Applications/HexEditor/HexEditorWidget.cpp index 2a11bb1a8f..77bd48fcd6 100644 --- a/Userland/Applications/HexEditor/HexEditorWidget.cpp +++ b/Userland/Applications/HexEditor/HexEditorWidget.cpp @@ -64,12 +64,12 @@ ErrorOr HexEditorWidget::setup() m_editor->update(); }; - m_editor->on_status_change = [this](int position, HexEditor::HexEditor::EditMode edit_mode, int selection_start, int selection_end) { + m_editor->on_status_change = [this](int position, HexEditor::HexEditor::EditMode edit_mode, auto selection) { m_statusbar->set_text(0, String::formatted("Offset: {:#08X}", position).release_value_but_fixme_should_propagate_errors()); m_statusbar->set_text(1, String::formatted("Edit Mode: {}", edit_mode == HexEditor::HexEditor::EditMode::Hex ? "Hex" : "Text").release_value_but_fixme_should_propagate_errors()); - m_statusbar->set_text(2, String::formatted("Selection Start: {}", selection_start).release_value_but_fixme_should_propagate_errors()); - m_statusbar->set_text(3, String::formatted("Selection End: {}", selection_end).release_value_but_fixme_should_propagate_errors()); - m_statusbar->set_text(4, String::formatted("Selected Bytes: {}", m_editor->selection_size()).release_value_but_fixme_should_propagate_errors()); + m_statusbar->set_text(2, String::formatted("Selection Start: {}", selection.start).release_value_but_fixme_should_propagate_errors()); + m_statusbar->set_text(3, String::formatted("Selection End: {}", selection.end).release_value_but_fixme_should_propagate_errors()); + m_statusbar->set_text(4, String::formatted("Selected Bytes: {}", selection.size()).release_value_but_fixme_should_propagate_errors()); bool has_selection = m_editor->has_selection(); m_copy_hex_action->set_enabled(has_selection); @@ -78,7 +78,7 @@ ErrorOr HexEditorWidget::setup() m_fill_selection_action->set_enabled(has_selection); if (m_value_inspector_container->is_visible() && !m_selecting_from_inspector) { - update_inspector_values(selection_start); + update_inspector_values(selection.start); } m_selecting_from_inspector = false; }; diff --git a/Userland/Applications/HexEditor/Selection.h b/Userland/Applications/HexEditor/Selection.h new file mode 100644 index 0000000000..d264d12cfc --- /dev/null +++ b/Userland/Applications/HexEditor/Selection.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +struct Selection { + size_t start { 0 }; + size_t end { 0 }; + + void clear() + { + start = 0; + end = 0; + } + bool is_empty() const { return start == end; } + size_t size() const { return (start < end) ? (end - start) : (start - end); } + bool contains(size_t position) const { return (min(start, end) <= position) && (position < max(start, end)); } +};