ImHex/plugins/builtin/include/content/views/view_hex_editor.hpp

144 lines
4.4 KiB
C++

#pragma once
#include <hex/api/content_registry.hpp>
#include <hex/ui/view.hpp>
#include <hex/helpers/concepts.hpp>
#include <hex/helpers/encoding_file.hpp>
#include <algorithm>
#include <limits>
namespace hex::plugin::builtin {
class ViewHexEditor : public View {
public:
ViewHexEditor();
void drawContent() override;
private:
constexpr static auto InvalidSelection = std::numeric_limits<u64>::max();
void registerShortcuts();
void registerEvents();
void registerMenuItems();
void drawCell(u64 address, u8 *data, size_t size, bool hovered);
void drawPopup();
void drawSelectionFrame(u32 x, u32 y, u64 byteAddress, u16 bytesPerCell, const ImVec2 &cellPos, const ImVec2 &cellSize);
public:
void setSelection(const Region &region) { this->setSelection(region.getStartAddress(), region.getEndAddress()); }
void setSelection(u128 start, u128 end) {
if (!ImHexApi::Provider::isValid())
return;
if (start == InvalidSelection && end == InvalidSelection)
return;
if (start == InvalidSelection)
start = end;
if (end == InvalidSelection)
end = start;
const size_t maxAddress = ImHexApi::Provider::get()->getActualSize() - 1;
this->m_selectionChanged = this->m_selectionStart != start || this->m_selectionEnd != end;
this->m_selectionStart = std::clamp<u128>(start, 0, maxAddress);
this->m_selectionEnd = std::clamp<u128>(end, 0, maxAddress);
if (this->m_selectionChanged) {
EventManager::post<EventRegionSelected>(this->getSelection());
}
}
[[nodiscard]] Region getSelection() const {
const auto start = std::min(this->m_selectionStart, this->m_selectionEnd);
const auto end = std::max(this->m_selectionStart, this->m_selectionEnd);
const size_t size = end - start + 1;
return { start, size };
}
[[nodiscard]] bool isSelectionValid() const {
return this->m_selectionStart != InvalidSelection && this->m_selectionEnd != InvalidSelection;
}
void jumpToSelection() {
this->m_shouldJumpToSelection = true;
}
void scrollToSelection() {
this->m_shouldScrollToSelection = true;
}
void jumpIfOffScreen() {
this->m_shouldJumpWhenOffScreen = true;
}
public:
class Popup {
public:
virtual ~Popup() = default;
virtual void draw(ViewHexEditor *editor) = 0;
};
[[nodiscard]] bool isAnyPopupOpen() const {
return this->m_currPopup != nullptr;
}
template<std::derived_from<Popup> T>
[[nodiscard]] bool isPopupOpen() const {
return dynamic_cast<T*>(this->m_currPopup.get()) != nullptr;
}
template<std::derived_from<Popup> T, typename ... Args>
void openPopup(Args && ...args) {
this->m_currPopup = std::make_unique<T>(std::forward<Args>(args)...);
this->m_shouldOpenPopup = true;
}
void closePopup() {
this->m_currPopup.reset();
}
private:
void drawEditor(const ImVec2 &size);
void drawFooter(const ImVec2 &size);
void handleSelection(u64 address, u32 bytesPerCell, const u8 *data, bool cellHovered);
private:
u16 m_bytesPerRow = 16;
ContentRegistry::HexEditor::DataVisualizer *m_currDataVisualizer;
bool m_shouldJumpToSelection = false;
bool m_shouldScrollToSelection = false;
bool m_shouldJumpWhenOffScreen = false;
bool m_selectionChanged = false;
u64 m_selectionStart = InvalidSelection;
u64 m_selectionEnd = InvalidSelection;
u16 m_visibleRowCount = 0;
std::optional<u64> m_editingAddress;
bool m_shouldModifyValue = false;
bool m_enteredEditingMode = false;
bool m_shouldUpdateEditingValue = false;
std::vector<u8> m_editingBytes;
color_t m_selectionColor = 0x00;
bool m_upperCaseHex = true;
bool m_grayOutZero = true;
bool m_showAscii = true;
bool m_shouldOpenPopup = false;
std::unique_ptr<Popup> m_currPopup;
std::optional<EncodingFile> m_currCustomEncoding;
};
}