#include <numeric>
#include "../muscles.h"
#include "../ui.h"
#include "dialog.h"
void View_Source::update_ui(Camera& view) {
reposition_box_buttons(cross, maxm, box.w, cross_size);
float w = title.font->render.text_width(title.text.c_str()) / view.scale;
float max_title_w = box.w - 5 * cross_size;
w = w < max_title_w ? w : max_title_w;
title.pos = {
(box.w - w) / 2,
2,
w,
title.font->render.text_height() * 1.1f / view.scale
};
float scroll_w = 14;
float data_x = border;
float data_y = title.pos.y + title.pos.h + 8;
float goto_w = (float)goto_digits + 5.0f;
goto_w *= goto_box.font->render.glyph_for('0')->box_w / view.scale;
float goto_x = data_x;
float goto_h = goto_box.pos.h;
float goto_y = box.h - border - goto_h;
if (menu_type == MenuFile) {
goto_box.pos = {
goto_x,
goto_y,
goto_w,
goto_h
};
}
else {
goto_x = div.position - goto_w / 2;
goto_box.pos = {
goto_x,
goto_y,
goto_w,
goto_h
};
float y = data_y;
div.pos = {
div.position,
y,
div.breadth,
goto_y - y - border
};
div.maximum = box.w - div.minimum;
float pad = div.padding;
data_x = div.pos.x + div.pos.w + pad;
reg_title.pos = {
border,
y,
div.pos.x - border - pad,
reg_title.font->render.text_height() * 1.1f / view.scale
};
y += reg_title.pos.h;
reg_scroll.pos = {
reg_title.pos.x + reg_title.pos.w - scroll_w,
y,
scroll_w,
goto_y - y - scroll_w - 3*border
};
reg_lat_scroll.pos = {
border,
goto_y - scroll_w - 2*border,
reg_scroll.pos.x - 2*border,
scroll_w
};
reg_table.pos = {
reg_lat_scroll.pos.x,
reg_scroll.pos.y,
reg_lat_scroll.pos.w,
reg_scroll.pos.h
};
reg_search.pos = {
border,
goto_y,
120,
goto_h
};
}
hex_title.pos = {
data_x,
data_y,
box.w - data_x - border,
hex_title.font->render.text_height() * 1.1f / view.scale
};
float data_w = hex_scroll.pos.x - hex_title.pos.x - border;
float size_w = size_label.font->render.text_width(size_label.text.c_str()) * 1.1f / view.scale;
float size_font_h = size_label.font->render.text_height();
float size_h = size_font_h * 1.1f / view.scale;
float size_x, size_y;
if (menu_type == MenuProcess) {
size_x = data_x + data_w - size_w;
size_y = goto_y - size_h - border;
}
else {
size_x = goto_x + goto_w + border;
size_y = goto_y + goto_box.text_off_y * size_font_h; //box.h - goto_h + (goto_h - size_h) - border;
}
size_label.pos = {
size_x,
size_y,
size_w,
size_h
};
if (menu_type == MenuProcess) {
reg_name.pos = {
data_x,
goto_y - border - hex_title.pos.h,
data_w - size_w,
hex_title.pos.h
};
}
data_y += hex_title.pos.h;
float data_h = menu_type == MenuProcess ? size_y : goto_y;
data_h -= data_y + border;
hex_scroll.pos = {
box.w - border - scroll_w,
data_y,
scroll_w,
data_h
};
hex.pos = {
hex_title.pos.x,
data_y,
data_w,
data_h
};
ascii_box.pos.x = box.w - border - ascii_box.pos.w;
ascii_box.pos.y = box.h - border - ascii_box.pos.h;
hex_box.pos.x = ascii_box.pos.x - hex_box.pos.w;
hex_box.pos.y = ascii_box.pos.y;
addr_box.pos.x = hex_box.pos.x - addr_box.pos.w;
addr_box.pos.y = hex_box.pos.y;
columns.pos.x = addr_box.pos.x - columns.pos.w - 2*border;
columns.pos.y = addr_box.pos.y;
hex.show_addrs = addr_box.checked;
hex.show_hex = hex_box.checked;
hex.show_ascii = ascii_box.checked;
hex.update_view(view.scale);
if (menu_type == MenuProcess)
min_width = div.position + box.w - div.maximum;
char buf[20];
snprintf(buf, 19, "%#llx", hex.region_address + hex.offset);
goto_box.placeholder = buf;
}
void View_Source::update_regions_table() {
int sel = reg_table.sel_row;
if (sel < 0) {
reg_name.text = "";
return;
}
sel = reg_table.data->index_from_filtered(sel);
char *name = (char*)reg_table.data->columns[0][sel];
reg_name.text = name ? name : "";
u64 address = (u64)reg_table.data->columns[1][sel];
u64 size = (u64)reg_table.data->columns[2][sel];
selected_region = address;
hex.set_region(address, size);
hex_scroll.position = 0;
}
void View_Source::refresh_region_list(Point *cursor) {
int n_rows = table.row_count();
bool hovered = cursor && reg_table.pos.contains(*cursor);
hex.source->block_region_refresh = hovered;
if (!n_rows)
needs_region_update = true;
if (!needs_region_update && !hex.source->region_refreshed)
return;
int n_sources = hex.source->regions.size();
if (n_sources != region_list.size()) {
u64 sel_addr = 0;
reg_table.data->resize(n_sources);
region_list.resize(n_sources);
std::iota(region_list.begin(), region_list.end(), 0);
}
auto& names = (std::vector<char*>&)table.columns[0];
auto& addrs = (std::vector<u64>&)table.columns[1];
auto& sizes = (std::vector<u64>&)table.columns[2];
int idx = 0, sel = -1;
for (auto& r : region_list) {
auto& reg = hex.source->regions[r];
if (selected_region == reg.base)
sel = idx;
addrs[idx] = reg.base;
sizes[idx] = reg.size;
names[idx] = reg.name;
idx++;
}
goto_digits = idx <= 0 ? 4 : count_hex_digits(hex.source->regions[idx-1].base) + 2;
if (table.filtered > 0) {
table.update_filter(reg_search.editor.text);
sel = table.filtered_from_index(sel);
}
reg_table.sel_row = sel;
needs_region_update = false;
}
void View_Source::goto_address(u64 address) {
u64 base = 0;
if (menu_type == MenuProcess) {
refresh_region_list(nullptr);
int sel_row = -1;
for (auto& idx : region_list) {
auto& reg = hex.source->regions[idx];
if (address >= reg.base && address < reg.base + reg.size) {
sel_row = idx;
base = reg.base;
hex.set_region(reg.base, reg.size);
break;
}
}
if (sel_row > 0)
reg_table.data->clear_filter();
reg_table.sel_row = sel_row;
update_regions_table();
if (sel_row < 0)
return;
}
hex.set_offset(address - base);
}
void regions_handler(UI_Element *elem, Camera& view, bool dbl_click) {
auto ui = dynamic_cast<View_Source*>(elem->parent);
ui->reg_table.sel_row = ui->reg_table.hl_row;
ui->selected_region = 0;
ui->update_regions_table();
}
void goto_handler(Edit_Box *edit, Input& input) {
if (input.enter != 1 || !edit->editor.text.size())
return;
auto ui = dynamic_cast<View_Source*>(edit->parent);
ui->goto_address(strtoull(edit->editor.text.c_str(), nullptr, 16));
edit->editor.clear();
}
void region_search_handler(Edit_Box *edit, Input& input) {
auto ui = dynamic_cast<View_Source*>(edit->parent);
ui->reg_table.data->update_filter(edit->editor.text);
ui->reg_table.hl_row = ui->reg_table.sel_row = -1;
ui->reg_scroll.position = 0;
ui->selected_region = 0;
ui->reg_table.needs_redraw = true;
}
void set_hex_columns(Number_Edit *edit, Input& input) {
auto ui = dynamic_cast<View_Source*>(edit->parent);
ui->hex.columns = edit->number > 0 ? edit->number : 16;
}
void View_Source::open_source(Source *s) {
title.text = "View Source - ";
title.text += s->name;
hex.source = s;
refresh(nullptr);
}
void View_Source::refresh(Point *cursor) {
if (menu_type == MenuProcess) {
refresh_region_list(cursor);
}
else {
hex.set_region(0, hex.source->regions[0].size);
goto_digits = count_hex_digits(hex.region_size) + 2;
}
u64 n = hex.region_size;
if (n > 0) {
int n_digits = count_hex_digits(n);
char buf[40];
if (menu_type == MenuProcess)
snprintf(buf, 40, "0x%0*llx/0x%0*llx", n_digits, (u64)hex.offset, n_digits, hex.region_size);
else
snprintf(buf, 40, "/ 0x%0*llx", n_digits, hex.region_size);
size_label.text = buf;
}
}
void View_Source::handle_zoom(Workspace& ws, float new_scale) {
cross.img = ws.cross;
maxm.img = ws.maxm;
if (menu_type == MenuProcess)
div.make_icon(new_scale);
columns.make_icon(new_scale);
float goto_h = goto_box.font->render.text_height() * 1.4f / new_scale;
goto_box.update_icon(IconGoto, goto_h, new_scale);
if (menu_type == MenuProcess)
reg_search.update_icon(IconGlass, goto_h, new_scale);
}
void View_Source::on_close() {
hex.source->deactivate_span(hex.span_idx);
}
View_Source::View_Source(Workspace& ws, MenuType mtype) {
float scale = get_default_camera().scale;
cross.action = get_delete_box();
cross.img = ws.cross;
ui.push_back(&cross);
maxm.action = get_maximize_box();
maxm.img = ws.maxm;
ui.push_back(&maxm);
title.font = ws.default_font;
title.padding = 0;
ui.push_back(&title);
if (mtype == MenuProcess) {
div.default_color = ws.colors.div;
div.breadth = 2;
div.vertical = true;
div.moveable = true;
div.minimum = 8 * title.font->render.text_height();
div.position = div.minimum + 100;
div.cursor_type = CursorResizeWestEast;
ui.push_back(&div);
reg_title.text = "Regions";
reg_title.font = ws.make_font(10, ws.colors.text, scale);
ui.push_back(®_title);
reg_scroll.back_color = ws.colors.scroll_back;
reg_scroll.default_color = ws.colors.scroll;
reg_scroll.hl_color = ws.colors.scroll_hl;
reg_scroll.sel_color = ws.colors.scroll_sel;
reg_scroll.breadth = 12;
ui.push_back(®_scroll);
reg_lat_scroll.back_color = reg_scroll.back_color;
reg_lat_scroll.default_color = reg_scroll.default_color;
reg_lat_scroll.hl_color = reg_scroll.hl_color;
reg_lat_scroll.sel_color = reg_scroll.sel_color;
reg_lat_scroll.vertical = false;
reg_lat_scroll.breadth = 12;
ui.push_back(®_lat_scroll);
reg_table.action = regions_handler;
reg_table.font = ws.make_font(9, ws.colors.text, scale);
reg_table.default_color = ws.colors.dark;
reg_table.hl_color = ws.colors.hl;
reg_table.sel_color = ws.colors.light;
reg_table.column_spacing = 0.5;
reg_table.vscroll = ®_scroll;
reg_table.vscroll->content = ®_table;
reg_table.hscroll = ®_lat_scroll;
reg_table.hscroll->content = ®_table;
float digit_units = (float)reg_table.font->render.digit_width() / (float)reg_table.font->render.text_height();
float addr_w = 16 * digit_units;
float size_w = 8 * digit_units;
Column cols[] = {
{ColumnString, 0, 0, 0, 0, "Name"},
{ColumnHex, 16, 0.2, addr_w, addr_w, "Address"},
{ColumnHex, 8, 0.1, size_w, size_w, "Size"}
};
table.init(cols, nullptr, nullptr, nullptr, 3, 0);
reg_table.data = &table;
ui.push_back(®_table);
reg_search.font = reg_title.font;
reg_search.caret = ws.colors.caret;
reg_search.default_color = ws.colors.dark;
reg_search.icon_color = ws.colors.text;
reg_search.icon_color.a = 0.6;
reg_search.key_action = region_search_handler;
ui.push_back(®_search);
reg_name.font = reg_title.font;
ui.push_back(®_name);
}
else {
div.visible = false;
reg_title.visible = false;
reg_scroll.visible = false;
reg_lat_scroll.visible = false;
reg_table.visible = false;
reg_search.visible = false;
reg_name.visible = false;
}
float goto_font_size = 10;
hex_title.text = "Data";
hex_title.font = ws.make_font(goto_font_size, ws.colors.text, scale);
ui.push_back(&hex_title);
size_label.font = hex_title.font;
if (mtype == MenuFile)
size_label.padding = 0;
ui.push_back(&size_label);
hex_scroll.back_color = ws.colors.scroll_back;
hex_scroll.default_color = ws.colors.scroll;
hex_scroll.hl_color = ws.colors.scroll_hl;
hex_scroll.sel_color = ws.colors.scroll_sel;
ui.push_back(&hex_scroll);
hex.font = ws.make_font(8, ws.colors.text, scale);
hex.default_color = ws.colors.dark;
hex.back_color = ws.colors.back;
hex.caret = ws.colors.caret;
hex.scroll = &hex_scroll;
ui.push_back(&hex);
goto_box.caret = ws.colors.caret;
goto_box.default_color = ws.colors.dark;
goto_box.font = hex_title.font;
goto_box.key_action = goto_handler;
goto_box.icon_color = ws.colors.text;
goto_box.icon_color.a = 0.6;
goto_box.ph_font = ws.make_font(goto_font_size, goto_box.icon_color, scale);
ui.push_back(&goto_box);
addr_box.font = size_label.font;
addr_box.text = "O";
addr_box.default_color = ws.colors.scroll_back;
addr_box.hl_color = ws.colors.light;
addr_box.sel_color = ws.colors.cb;
addr_box.pos.h = addr_box.font->render.text_height() * 1.1f / scale;
addr_box.pos.w = 2 * addr_box.pos.h;
addr_box.checked = true;
addr_box.action = nullptr;
ui.push_back(&addr_box);
hex_box.font = size_label.font;
hex_box.text = "H";
hex_box.default_color = addr_box.default_color;
hex_box.hl_color = addr_box.hl_color;
hex_box.sel_color = addr_box.sel_color;
hex_box.pos = addr_box.pos;
hex_box.checked = true;
hex_box.action = addr_box.action;
ui.push_back(&hex_box);
ascii_box.font = size_label.font;
ascii_box.text = "A";
ascii_box.default_color = hex_box.default_color;
ascii_box.hl_color = addr_box.hl_color;
ascii_box.sel_color = addr_box.sel_color;
ascii_box.pos = addr_box.pos;
ascii_box.action = addr_box.action;
ui.push_back(&ascii_box);
columns.font = size_label.font;
columns.default_color = ws.colors.dark;
columns.sel_color = ws.colors.dark;
columns.editor.text = "16";
columns.pos.w = 40;
columns.pos.h = addr_box.pos.h;
columns.key_action = set_hex_columns;
ui.push_back(&columns);
refresh_every = 1;
initial_width = 600;
initial_height = 400;
min_width = 400;
min_height = 300;
expungeable = true;
}