Sfoglia il codice sorgente

HexEditor: Add 'Go to Offset...' dialog

Brendan Coles 4 anni fa
parent
commit
efef77a154

+ 3 - 0
Userland/Applications/HexEditor/CMakeLists.txt

@@ -1,10 +1,13 @@
 compile_gml(HexEditorWindow.gml HexEditorWindowGML.h hex_editor_window_gml)
+compile_gml(GoToOffsetDialog.gml GoToOffsetDialogGML.h go_to_offset_dialog_gml)
 
 set(SOURCES
     HexEditor.cpp
     HexEditorWidget.cpp
     FindDialog.cpp
+    GoToOffsetDialog.cpp
     main.cpp
+    GoToOffsetDialogGML.h
     HexEditorWindowGML.h
 )
 

+ 153 - 0
Userland/Applications/HexEditor/GoToOffsetDialog.cpp

@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2021, the SerenityOS developers.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "GoToOffsetDialog.h"
+#include <AK/String.h>
+#include <Applications/HexEditor/GoToOffsetDialogGML.h>
+#include <LibGUI/BoxLayout.h>
+#include <LibGUI/Button.h>
+#include <LibGUI/ComboBox.h>
+#include <LibGUI/ItemListModel.h>
+#include <LibGUI/Label.h>
+#include <LibGUI/MessageBox.h>
+#include <LibGUI/Statusbar.h>
+#include <LibGUI/TextBox.h>
+#include <LibGUI/Widget.h>
+
+int GoToOffsetDialog::show(GUI::Window* parent_window, int& history_offset, int& out_offset, int selection_offset, int buffer_size)
+{
+    auto dialog = GoToOffsetDialog::construct();
+    dialog->m_selection_offset = selection_offset;
+    dialog->m_buffer_size = buffer_size;
+
+    if (parent_window)
+        dialog->set_icon(parent_window->icon());
+
+    if (history_offset)
+        dialog->m_text_editor->set_text(String::formatted("{}", history_offset));
+
+    auto result = dialog->exec();
+
+    if (result != GUI::Dialog::ExecOK)
+        return result;
+
+    auto input_offset = dialog->process_input();
+    history_offset = move(input_offset);
+
+    auto new_offset = dialog->calculate_new_offset(input_offset);
+    dbgln("Go to offset: value={}", new_offset);
+    out_offset = move(new_offset);
+
+    return GUI::Dialog::ExecOK;
+}
+
+int GoToOffsetDialog::process_input()
+{
+    auto input_offset = m_text_editor->text().trim_whitespace();
+    int offset;
+    auto type = m_offset_type_box->text().trim_whitespace();
+    if (type == "Decimal") {
+        offset = String::formatted("{}", input_offset).to_int().value_or(0);
+    } else if (type == "Hexadecimal") {
+        offset = strtol(String::formatted("{}", input_offset).characters(), nullptr, 16);
+    } else {
+        VERIFY_NOT_REACHED();
+    }
+    return offset;
+}
+
+int GoToOffsetDialog::calculate_new_offset(int input_offset)
+{
+    int new_offset;
+    auto from = m_offset_from_box->text().trim_whitespace();
+    if (from == "Start") {
+        new_offset = input_offset;
+    } else if (from == "End") {
+        new_offset = m_buffer_size - input_offset;
+    } else if (from == "Here") {
+        new_offset = input_offset + m_selection_offset;
+    } else {
+        VERIFY_NOT_REACHED();
+    }
+
+    if (new_offset > m_buffer_size)
+        new_offset = m_buffer_size;
+    if (new_offset < 0)
+        new_offset = 0;
+
+    return new_offset;
+}
+
+void GoToOffsetDialog::update_statusbar()
+{
+    auto new_offset = calculate_new_offset(process_input());
+    m_statusbar->set_text(0, String::formatted("HEX: {:#08X}", new_offset));
+    m_statusbar->set_text(1, String::formatted("DEC: {}", new_offset));
+}
+
+GoToOffsetDialog::GoToOffsetDialog()
+    : Dialog(nullptr)
+{
+    resize(300, 80);
+    center_on_screen();
+    set_resizable(false);
+    set_title("Go to Offset");
+
+    auto& main_widget = set_main_widget<GUI::Widget>();
+    if (!main_widget.load_from_gml(go_to_offset_dialog_gml))
+        VERIFY_NOT_REACHED();
+
+    m_text_editor = *main_widget.find_descendant_of_type_named<GUI::TextBox>("text_editor");
+    m_go_button = *main_widget.find_descendant_of_type_named<GUI::Button>("go_button");
+    m_offset_type_box = *main_widget.find_descendant_of_type_named<GUI::ComboBox>("offset_type");
+    m_offset_from_box = *main_widget.find_descendant_of_type_named<GUI::ComboBox>("offset_from");
+    m_statusbar = *main_widget.find_descendant_of_type_named<GUI::Statusbar>("statusbar");
+
+    m_offset_type.append("Decimal");
+    m_offset_type.append("Hexadecimal");
+    m_offset_type_box->set_model(GUI::ItemListModel<String>::create(m_offset_type));
+    m_offset_type_box->set_selected_index(0);
+    m_offset_type_box->set_only_allow_values_from_model(true);
+
+    m_offset_from.append("Start");
+    m_offset_from.append("Here");
+    m_offset_from.append("End");
+    m_offset_from_box->set_model(GUI::ItemListModel<String>::create(m_offset_from));
+    m_offset_from_box->set_selected_index(0);
+    m_offset_from_box->set_only_allow_values_from_model(true);
+
+    m_text_editor->on_return_pressed = [this] {
+        m_go_button->click();
+    };
+
+    m_go_button->on_click = [this](auto) {
+        done(ExecResult::ExecOK);
+    };
+
+    m_text_editor->on_change = [this]() {
+        auto text = m_text_editor->text();
+        if (text.starts_with("0x")) {
+            text.replace("0x", "");
+            m_offset_type_box->set_selected_index(1);
+            m_text_editor->set_text(text);
+        }
+        update_statusbar();
+    };
+
+    m_offset_type_box->on_change = [this]() {
+        update_statusbar();
+    };
+
+    m_offset_from_box->on_change = [this]() {
+        update_statusbar();
+    };
+
+    update_statusbar();
+}
+
+GoToOffsetDialog::~GoToOffsetDialog()
+{
+}

+ 63 - 0
Userland/Applications/HexEditor/GoToOffsetDialog.gml

@@ -0,0 +1,63 @@
+@GUI::Widget {
+    name: "main"
+    fixed_width: 300
+    fixed_height: 80
+    fill_with_background_color: true
+
+    layout: @GUI::VerticalBoxLayout {
+        spacing: 2
+        margins: [0, 0, 0, 0]
+    }
+
+    @GUI::Widget {
+        layout: @GUI::HorizontalBoxLayout {
+            spacing: 2
+            margins: [2, 2, 2, 2]
+        }
+
+        @GUI::Label {
+            text: "Offset"
+            text_alignment: "CenterLeft"
+            fixed_width: 50
+        }
+
+        @GUI::TextBox {
+            name: "text_editor"
+            fixed_width: 100
+        }
+
+        @GUI::ComboBox {
+            name: "offset_type"
+            fixed_width: 100
+        }
+
+        @GUI::Button {
+            name: "go_button"
+            text: "Go"
+            fixed_width: 40
+        }
+    }
+
+    @GUI::Widget {
+        layout: @GUI::HorizontalBoxLayout {
+            spacing: 2
+            margins: [2, 2, 2, 2]
+        }
+
+        @GUI::Label {
+            text: "From"
+            text_alignment: "CenterLeft"
+            fixed_width: 50
+        }
+
+        @GUI::ComboBox {
+            name: "offset_from"
+            fixed_width: 100
+        }
+    }
+
+    @GUI::Statusbar {
+        name: "statusbar"
+        label_count: 2
+    }
+}

+ 35 - 0
Userland/Applications/HexEditor/GoToOffsetDialog.h

@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021, the SerenityOS developers.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Result.h>
+#include <AK/Vector.h>
+#include <LibGUI/Dialog.h>
+
+class GoToOffsetDialog : public GUI::Dialog {
+    C_OBJECT(GoToOffsetDialog);
+
+public:
+    static int show(GUI::Window* parent_window, int& history_offset, int& out_offset, int selection_offset, int end);
+
+private:
+    GoToOffsetDialog();
+    virtual ~GoToOffsetDialog() override;
+    void update_statusbar();
+    int process_input();
+    int calculate_new_offset(int offset);
+    int m_selection_offset { 0 };
+    int m_buffer_size { 0 };
+    Vector<String> m_offset_type;
+    Vector<String> m_offset_from;
+
+    RefPtr<GUI::TextEditor> m_text_editor;
+    RefPtr<GUI::Button> m_go_button;
+    RefPtr<GUI::ComboBox> m_offset_type_box;
+    RefPtr<GUI::ComboBox> m_offset_from_box;
+    RefPtr<GUI::Statusbar> m_statusbar;
+};

+ 2 - 0
Userland/Applications/HexEditor/HexEditor.h

@@ -29,12 +29,14 @@ public:
     bool is_readonly() const { return m_readonly; }
     void set_readonly(bool);
 
+    int buffer_size() const { return m_buffer.size(); }
     void set_buffer(const ByteBuffer&);
     void fill_selection(u8 fill_byte);
     bool write_to_file(const String& path);
 
     void select_all();
     bool has_selection() const { return !(m_selection_start == -1 || m_selection_end == -1 || (m_selection_end - m_selection_start) < 0 || m_buffer.is_empty()); }
+    int 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();

+ 18 - 20
Userland/Applications/HexEditor/HexEditorWidget.cpp

@@ -6,6 +6,7 @@
 
 #include "HexEditorWidget.h"
 #include "FindDialog.h"
+#include "GoToOffsetDialog.h"
 #include <AK/Optional.h>
 #include <AK/StringBuilder.h>
 #include <Applications/HexEditor/HexEditorWindowGML.h>
@@ -131,6 +132,20 @@ HexEditorWidget::HexEditorWidget()
         }
     });
 
+    m_goto_offset_action = GUI::Action::create("&Go to Offset ...", { Mod_Ctrl, Key_G }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-to.png"), [this](const GUI::Action&) {
+        int new_offset;
+        auto result = GoToOffsetDialog::show(
+            window(),
+            m_goto_history,
+            new_offset,
+            m_editor->selection_start_offset(),
+            m_editor->buffer_size());
+        if (result == GUI::InputBox::ExecOK) {
+            m_editor->highlight(new_offset, new_offset);
+            m_editor->update();
+        }
+    });
+
     m_layout_toolbar_action = GUI::Action::create_checkable("&Toolbar", [&](auto& action) {
         m_toolbar_container->set_visible(action.is_checked());
     });
@@ -140,6 +155,7 @@ HexEditorWidget::HexEditorWidget()
     m_toolbar->add_action(*m_save_action);
     m_toolbar->add_separator();
     m_toolbar->add_action(*m_find_action);
+    m_toolbar->add_action(*m_goto_offset_action);
 
     m_editor->set_focus(true);
 }
@@ -162,23 +178,6 @@ void HexEditorWidget::initialize_menubar(GUI::Menubar& menubar)
         GUI::Application::the()->quit();
     }));
 
-    m_goto_decimal_offset_action = GUI::Action::create("&Go to Offset (Decimal)...", { Mod_Ctrl | Mod_Shift, Key_G }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"), [this](const GUI::Action&) {
-        String value;
-        if (GUI::InputBox::show(window(), value, "Enter decimal offset:", "Go to Offset") == GUI::InputBox::ExecOK && !value.is_empty()) {
-            auto new_offset = value.to_int();
-            if (new_offset.has_value())
-                m_editor->set_position(new_offset.value());
-        }
-    });
-
-    m_goto_hex_offset_action = GUI::Action::create("Go to &Offset (Hex)...", { Mod_Ctrl, Key_G }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"), [this](const GUI::Action&) {
-        String value;
-        if (GUI::InputBox::show(window(), value, "Enter hexadecimal offset:", "Go to Offset") == GUI::InputBox::ExecOK && !value.is_empty()) {
-            auto new_offset = strtol(value.characters(), nullptr, 16);
-            m_editor->set_position(new_offset);
-        }
-    });
-
     auto& edit_menu = menubar.add_menu("&Edit");
     edit_menu.add_action(GUI::CommonActions::make_select_all_action([this](auto&) {
         m_editor->select_all();
@@ -192,9 +191,6 @@ void HexEditorWidget::initialize_menubar(GUI::Menubar& menubar)
         }
     }));
     edit_menu.add_separator();
-    edit_menu.add_action(*m_goto_decimal_offset_action);
-    edit_menu.add_action(*m_goto_hex_offset_action);
-    edit_menu.add_separator();
     edit_menu.add_action(GUI::Action::create("Copy &Hex", { Mod_Ctrl, Key_C }, [&](const GUI::Action&) {
         m_editor->copy_selected_hex_to_clipboard();
     }));
@@ -220,6 +216,8 @@ void HexEditorWidget::initialize_menubar(GUI::Menubar& menubar)
         m_editor->update();
         m_last_found_index = result;
     }));
+    edit_menu.add_separator();
+    edit_menu.add_action(*m_goto_offset_action);
 
     auto& view_menu = menubar.add_menu("&View");
     view_menu.add_action(*m_layout_toolbar_action);

+ 2 - 2
Userland/Applications/HexEditor/HexEditorWidget.h

@@ -35,6 +35,7 @@ private:
     String m_name;
     String m_extension;
 
+    int m_goto_history { 0 };
     String m_search_text;
     ByteBuffer m_search_buffer;
     int last_found_index() const { return m_last_found_index == -1 ? 0 : m_last_found_index; }
@@ -45,8 +46,7 @@ private:
     RefPtr<GUI::Action> m_save_action;
     RefPtr<GUI::Action> m_save_as_action;
     RefPtr<GUI::Action> m_find_action;
-    RefPtr<GUI::Action> m_goto_decimal_offset_action;
-    RefPtr<GUI::Action> m_goto_hex_offset_action;
+    RefPtr<GUI::Action> m_goto_offset_action;
     RefPtr<GUI::Action> m_layout_toolbar_action;
 
     GUI::ActionGroup m_bytes_per_row_actions;