mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
HexEditor: Add 'Go to Offset...' dialog
This commit is contained in:
parent
dd6921bfc6
commit
efef77a154
Notes:
sideshowbarker
2024-07-18 17:30:08 +09:00
Author: https://github.com/bcoles Commit: https://github.com/SerenityOS/serenity/commit/efef77a154c Pull-request: https://github.com/SerenityOS/serenity/pull/7406
7 changed files with 276 additions and 22 deletions
|
@ -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
Userland/Applications/HexEditor/GoToOffsetDialog.cpp
Normal file
153
Userland/Applications/HexEditor/GoToOffsetDialog.cpp
Normal file
|
@ -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
Userland/Applications/HexEditor/GoToOffsetDialog.gml
Normal file
63
Userland/Applications/HexEditor/GoToOffsetDialog.gml
Normal file
|
@ -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
Userland/Applications/HexEditor/GoToOffsetDialog.h
Normal file
35
Userland/Applications/HexEditor/GoToOffsetDialog.h
Normal file
|
@ -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;
|
||||
};
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue