|
@@ -0,0 +1,109 @@
|
|
|
+/*
|
|
|
+ * Copyright (c) 2024, Sam Atkins <atkinssj@serenityos.org>
|
|
|
+ *
|
|
|
+ * SPDX-License-Identifier: BSD-2-Clause
|
|
|
+ */
|
|
|
+
|
|
|
+#include "EditAnnotationDialog.h"
|
|
|
+#include <LibGUI/MessageBox.h>
|
|
|
+
|
|
|
+static Gfx::Color s_most_recent_color { Color::from_argb(0xfffce94f) };
|
|
|
+
|
|
|
+GUI::Dialog::ExecResult EditAnnotationDialog::show_create_dialog(GUI::Window* parent_window, HexDocument& document, Selection selection)
|
|
|
+{
|
|
|
+ auto dialog_or_error = EditAnnotationDialog::try_create(parent_window, document, selection);
|
|
|
+ if (dialog_or_error.is_error()) {
|
|
|
+ GUI::MessageBox::show(parent_window, MUST(String::formatted("{}", dialog_or_error.error())), "Error while opening Create Annotation dialog"sv, GUI::MessageBox::Type::Error);
|
|
|
+ return ExecResult::Aborted;
|
|
|
+ }
|
|
|
+
|
|
|
+ auto dialog = dialog_or_error.release_value();
|
|
|
+ return dialog->exec();
|
|
|
+}
|
|
|
+
|
|
|
+GUI::Dialog::ExecResult EditAnnotationDialog::show_edit_dialog(GUI::Window* parent_window, HexDocument& document, Annotation& annotation)
|
|
|
+{
|
|
|
+ auto dialog_or_error = EditAnnotationDialog::try_create(parent_window, document, &annotation);
|
|
|
+ if (dialog_or_error.is_error()) {
|
|
|
+ GUI::MessageBox::show(parent_window, MUST(String::formatted("{}", dialog_or_error.error())), "Error while opening Edit Annotation dialog"sv, GUI::MessageBox::Type::Error);
|
|
|
+ return ExecResult::Aborted;
|
|
|
+ }
|
|
|
+
|
|
|
+ auto dialog = dialog_or_error.release_value();
|
|
|
+ return dialog->exec();
|
|
|
+}
|
|
|
+
|
|
|
+ErrorOr<NonnullRefPtr<EditAnnotationDialog>> EditAnnotationDialog::try_create(GUI::Window* parent_window, HexDocument& hex_document, Variant<Annotation*, Selection> selection_or_annotation)
|
|
|
+{
|
|
|
+ auto widget = TRY(HexEditor::EditAnnotationWidget::try_create());
|
|
|
+ return adopt_nonnull_ref_or_enomem(new (nothrow) EditAnnotationDialog(parent_window, move(widget), hex_document, move(selection_or_annotation)));
|
|
|
+}
|
|
|
+
|
|
|
+EditAnnotationDialog::EditAnnotationDialog(GUI::Window* parent_window, NonnullRefPtr<HexEditor::EditAnnotationWidget> widget, HexDocument& hex_document, Variant<Annotation*, Selection> selection_or_annotation)
|
|
|
+ : GUI::Dialog(parent_window)
|
|
|
+ , m_document(hex_document.make_weak_ptr())
|
|
|
+{
|
|
|
+ resize(260, 140);
|
|
|
+ set_resizable(false);
|
|
|
+ set_main_widget(widget);
|
|
|
+
|
|
|
+ m_start_offset = find_descendant_of_type_named<GUI::NumericInput>("start_offset");
|
|
|
+ m_end_offset = find_descendant_of_type_named<GUI::NumericInput>("end_offset");
|
|
|
+ m_background_color = find_descendant_of_type_named<GUI::ColorInput>("background_color");
|
|
|
+ m_save_button = find_descendant_of_type_named<GUI::DialogButton>("save_button");
|
|
|
+ m_cancel_button = find_descendant_of_type_named<GUI::DialogButton>("cancel_button");
|
|
|
+
|
|
|
+ // FIXME: This could be specified in GML, but the GML doesn't like property setters that aren't `set_FOO()`.
|
|
|
+ m_background_color->set_color_has_alpha_channel(false);
|
|
|
+
|
|
|
+ // NOTE: The NumericInput stores an i64, so not all size_t values can fit. But I don't think we'll be
|
|
|
+ // hex-editing files larger than 9000 petabytes for the foreseeable future!
|
|
|
+ VERIFY(hex_document.size() <= NumericLimits<i64>::max());
|
|
|
+ m_start_offset->set_min(0);
|
|
|
+ m_start_offset->set_max(hex_document.size() - 1);
|
|
|
+ m_end_offset->set_min(0);
|
|
|
+ m_end_offset->set_max(hex_document.size() - 1);
|
|
|
+
|
|
|
+ selection_or_annotation.visit(
|
|
|
+ [this](Annotation*& annotation) {
|
|
|
+ m_annotation = *annotation;
|
|
|
+ set_title("Edit Annotation"sv);
|
|
|
+ set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/annotation.png"sv).release_value_but_fixme_should_propagate_errors());
|
|
|
+ VERIFY(m_annotation->start_offset <= NumericLimits<i64>::max());
|
|
|
+ VERIFY(m_annotation->end_offset <= NumericLimits<i64>::max());
|
|
|
+ m_start_offset->set_value(m_annotation->start_offset);
|
|
|
+ m_end_offset->set_value(m_annotation->end_offset);
|
|
|
+ m_background_color->set_color(m_annotation->background_color);
|
|
|
+ },
|
|
|
+ [this](Selection& selection) {
|
|
|
+ set_title("Add Annotation"sv);
|
|
|
+ set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/annotation-add.png"sv).release_value_but_fixme_should_propagate_errors());
|
|
|
+ // Selection start is inclusive, and end is exclusive.
|
|
|
+ // Therefore, if the selection isn't empty, we need to subtract 1 from the end offset.
|
|
|
+ m_start_offset->set_value(selection.start);
|
|
|
+ m_end_offset->set_value(selection.is_empty() ? selection.end : selection.end - 1);
|
|
|
+ // Default to the most recently used annotation color.
|
|
|
+ m_background_color->set_color(s_most_recent_color);
|
|
|
+ });
|
|
|
+
|
|
|
+ m_save_button->on_click = [this](auto) {
|
|
|
+ auto start_offset = static_cast<size_t>(m_start_offset->value());
|
|
|
+ auto end_offset = static_cast<size_t>(m_end_offset->value());
|
|
|
+ Annotation result {
|
|
|
+ .start_offset = min(start_offset, end_offset),
|
|
|
+ .end_offset = max(start_offset, end_offset),
|
|
|
+ .background_color = m_background_color->color(),
|
|
|
+ };
|
|
|
+ if (m_annotation.has_value()) {
|
|
|
+ *m_annotation = move(result);
|
|
|
+ } else {
|
|
|
+ if (m_document)
|
|
|
+ m_document->add_annotation(result);
|
|
|
+ }
|
|
|
+ s_most_recent_color = m_background_color->color();
|
|
|
+ done(ExecResult::OK);
|
|
|
+ };
|
|
|
+ m_cancel_button->on_click = [this](auto) {
|
|
|
+ done(ExecResult::Cancel);
|
|
|
+ };
|
|
|
+}
|