feat: Added simple insert mode to hex editor

This commit is contained in:
WerWolv
2024-03-29 13:22:21 +01:00
parent cc593fb6c4
commit 92803c1536
5 changed files with 107 additions and 16 deletions

View File

@@ -79,6 +79,7 @@ namespace hex::prv::undo {
for (u32 i = 0; i < count; i += 1) {
i64 index = startIndex + i;
m_undoStack[index]->undo(m_provider);
operation->addOperation(std::move(m_undoStack[index]));
}

View File

@@ -776,6 +776,7 @@
"hex.builtin.view.hex_editor.menu.edit.cut": "Cut",
"hex.builtin.view.hex_editor.menu.edit.fill": "Fill...",
"hex.builtin.view.hex_editor.menu.edit.insert": "Insert...",
"hex.builtin.view.hex_editor.menu.edit.insert_mode": "Insert Mode",
"hex.builtin.view.hex_editor.menu.edit.jump_to": "Jump to",
"hex.builtin.view.hex_editor.menu.edit.jump_to.curr_pattern": "Current Pattern",
"hex.builtin.view.hex_editor.menu.edit.open_in_new_provider": "Open selection view...",

View File

@@ -1117,7 +1117,7 @@ namespace hex::plugin::builtin {
/* Remove */
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.edit", "hex.builtin.view.hex_editor.menu.edit.remove" }, ICON_VS_CLEAR_ALL, 1800, Shortcut::None,
[this] {
auto selection = ImHexApi::HexEditor::getSelection();
auto selection = ImHexApi::HexEditor::getSelection();
this->openPopup<PopupRemove>(selection->getStartAddress(), selection->getSize());
},
@@ -1126,12 +1126,30 @@ namespace hex::plugin::builtin {
/* Fill */
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.edit", "hex.builtin.view.hex_editor.menu.edit.fill" }, ICON_VS_PAINTCAN, 1810, Shortcut::None,
[this] {
auto selection = ImHexApi::HexEditor::getSelection();
auto selection = ImHexApi::HexEditor::getSelection();
this->openPopup<PopupFill>(selection->getStartAddress(), selection->getSize());
},
[] { return ImHexApi::HexEditor::isSelectionValid() && ImHexApi::Provider::isValid() && ImHexApi::Provider::get()->isWritable(); });
/* Toggle Overwrite/Insert mode */
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.edit", "hex.builtin.view.hex_editor.menu.edit.insert_mode" }, ICON_VS_PENCIL, 1820, Shortcut::None,
[this] {
if (m_hexEditor.getMode() == ui::HexEditor::Mode::Insert)
m_hexEditor.setMode(ui::HexEditor::Mode::Overwrite);
else
m_hexEditor.setMode(ui::HexEditor::Mode::Insert);
},
[] {
return ImHexApi::HexEditor::isSelectionValid() &&
ImHexApi::Provider::isValid() &&
ImHexApi::Provider::get()->isWritable() &&
ImHexApi::Provider::get()->isResizable();
},
[this] {
return m_hexEditor.getMode() == ui::HexEditor::Mode::Insert;
});
/* Jump to */
ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.edit", "hex.builtin.view.hex_editor.menu.edit.jump_to" }, ICON_VS_DEBUG_STEP_OUT, 1850,
[] {

View File

@@ -156,6 +156,11 @@ namespace hex::ui {
EventRegionSelected::post(ImHexApi::HexEditor::ProviderRegion{ { selection.address, selection.size }, m_provider });
m_shouldModifyValue = true;
}
if (m_mode == Mode::Insert) {
m_selectionStart = m_selectionEnd;
m_cursorBlinkTimer = -0.3F;
}
}
[[nodiscard]] Region getSelection() const {
@@ -294,6 +299,26 @@ namespace hex::ui {
m_editingAddress = std::nullopt;
}
enum class Mode { Overwrite, Insert };
void setMode(Mode mode) {
if (mode == Mode::Insert) {
// Don't enter insert mode if the provider doesn't support resizing the underlying data
if (!m_provider->isResizable())
return;
// Get rid of any selection in insert mode
m_selectionStart = m_selectionEnd;
m_cursorPosition = m_selectionEnd;
m_selectionChanged = true;
}
m_mode = mode;
}
[[nodiscard]] Mode getMode() const {
return m_mode;
}
private:
prv::Provider *m_provider = nullptr;
@@ -346,6 +371,9 @@ namespace hex::ui {
static void defaultTooltipCallback(u64, const u8 *, size_t) { }
std::function<std::optional<color_t>(u64, const u8 *, size_t)> m_foregroundColorCallback = defaultColorCallback, m_backgroundColorCallback = defaultColorCallback;
std::function<void(u64, const u8 *, size_t)> m_tooltipCallback = defaultTooltipCallback;
Mode m_mode = Mode::Overwrite;
float m_cursorBlinkTimer = -0.3F;
};
}

View File

@@ -91,6 +91,9 @@ namespace hex::ui {
}
std::optional<color_t> HexEditor::applySelectionColor(u64 byteAddress, std::optional<color_t> color) {
if (m_mode == Mode::Insert)
return color.value_or(0);
if (isSelectionValid()) {
auto selection = getSelection();
@@ -287,7 +290,13 @@ namespace hex::ui {
m_enteredEditingMode = true;
m_editingBytes.resize(size);
std::memcpy(m_editingBytes.data(), data, size);
if (m_mode == Mode::Overwrite)
std::memcpy(m_editingBytes.data(), data, size);
else if (m_mode == Mode::Insert) {
std::memset(m_editingBytes.data(), 0x00, size);
m_provider->insert(address, size);
}
m_editingCellType = cellType;
}
}
@@ -342,9 +351,20 @@ namespace hex::ui {
if (nextEditingAddress >= m_provider->getBaseAddress() + m_provider->getCurrentPageAddress() + m_provider->getSize())
m_editingAddress = std::nullopt;
else
else {
m_editingAddress = nextEditingAddress;
if (m_mode == Mode::Insert) {
std::memset(m_editingBytes.data(), 0x00, size);
m_provider->getUndoStack().groupOperations(2, "hex.builtin.undo_operation.insert");
m_provider->insert(nextEditingAddress, size);
}
}
} else {
if (m_mode == Mode::Insert) {
m_provider->undo();
}
m_editingAddress = std::nullopt;
}
@@ -379,21 +399,34 @@ namespace hex::ui {
const color_t SelectionFrameColor = ImGui::GetColorU32(ImGuiCol_Text);
// Draw vertical line at the left of first byte and the start of the line
if (x == 0 || byteAddress == selection.getStartAddress())
drawList->AddLine(cellPos, cellPos + ImVec2(0, cellSize.y), ImColor(SelectionFrameColor), 1_scaled);
switch (m_mode) {
case Mode::Overwrite: {
// Draw vertical line at the left of first byte and the start of the line
if (x == 0 || byteAddress == selection.getStartAddress())
drawList->AddLine(cellPos, cellPos + ImVec2(0, cellSize.y), ImColor(SelectionFrameColor), 1_scaled);
// Draw vertical line at the right of the last byte and the end of the line
if (x == u16((m_bytesPerRow / bytesPerCell) - 1) || (byteAddress + bytesPerCell) > selection.getEndAddress())
drawList->AddLine(cellPos + ImVec2(cellSize.x, -1), cellPos + cellSize, ImColor(SelectionFrameColor), 1_scaled);
// Draw vertical line at the right of the last byte and the end of the line
if (x == u16((m_bytesPerRow / bytesPerCell) - 1) || (byteAddress + bytesPerCell) > selection.getEndAddress())
drawList->AddLine(cellPos + ImVec2(cellSize.x, -1), cellPos + cellSize, ImColor(SelectionFrameColor), 1_scaled);
// Draw horizontal line at the top of the bytes
if (y == 0 || (byteAddress - m_bytesPerRow) < selection.getStartAddress())
drawList->AddLine(cellPos, cellPos + ImVec2(cellSize.x + 1, 0), ImColor(SelectionFrameColor), 1_scaled);
// Draw horizontal line at the top of the bytes
if (y == 0 || (byteAddress - m_bytesPerRow) < selection.getStartAddress())
drawList->AddLine(cellPos, cellPos + ImVec2(cellSize.x + 1, 0), ImColor(SelectionFrameColor), 1_scaled);
// Draw horizontal line at the bottom of the bytes
if ((byteAddress + m_bytesPerRow) > selection.getEndAddress())
drawList->AddLine(cellPos + ImVec2(0, cellSize.y), cellPos + cellSize + ImVec2(1, 0), ImColor(SelectionFrameColor), 1_scaled);
// Draw horizontal line at the bottom of the bytes
if ((byteAddress + m_bytesPerRow) > selection.getEndAddress())
drawList->AddLine(cellPos + ImVec2(0, cellSize.y), cellPos + cellSize + ImVec2(1, 0), ImColor(SelectionFrameColor), 1_scaled);
break;
}
case Mode::Insert: {
bool cursorVisible = (!ImGui::GetIO().ConfigInputTextCursorBlink) || (m_cursorBlinkTimer <= 0.0f) || std::fmod(m_cursorBlinkTimer, 1.20f) <= 0.80f;
if (cursorVisible && byteAddress == selection.getStartAddress()) {
// Draw vertical line at the left of first byte and the start of the line
drawList->AddLine(cellPos, cellPos + ImVec2(0, cellSize.y), ImColor(SelectionFrameColor), 1_scaled);
}
}
}
}
void HexEditor::drawEditor(const ImVec2 &size) {
@@ -422,6 +455,9 @@ namespace hex::ui {
ImGuiExt::TextFormattedCentered("{}", "hex.ui.hex_editor.no_bytes"_lang);
}
if (!m_editingAddress.has_value() && ImGui::IsKeyPressed(ImGuiKey_Escape))
m_mode = Mode::Overwrite;
Region hoveredCell = Region::Invalid();
if (ImGui::BeginChild("Hex View", size, ImGuiChildFlags_None, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
this->drawScrollbar(CharacterSize);
@@ -898,6 +934,11 @@ namespace hex::ui {
// Human-readable units
ImGuiExt::DimmedIconToggle(ICON_VS_SYMBOL_NUMERIC, &m_showHumanReadableUnits);
ImGuiExt::InfoTooltip("hex.ui.hex_editor.human_readable_units_footer"_lang);
ImGui::SameLine(0, 10_scaled);
if (m_mode == Mode::Insert) {
ImGui::TextUnformatted("[ INSERT ]");
}
}
// Collapse button
@@ -1045,6 +1086,8 @@ namespace hex::ui {
this->drawFooter(footerSize);
m_selectionChanged = false;
m_cursorBlinkTimer += ImGui::GetIO().DeltaTime;
}
}