forked from Lephenixnoir/hex-editor
UI improvements, relative goto, better program icon
This commit is contained in:
parent
52b4aea3f4
commit
a08e7fa89d
Binary file not shown.
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Binary file not shown.
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
@ -27,6 +27,7 @@ heditor *heditor_create(jwidget *parent)
|
|||
e->source = NULL;
|
||||
e->scroll = 0;
|
||||
e->cursor = 0;
|
||||
e->insert = false;
|
||||
e->visible_lines = 0;
|
||||
e->bytes_per_line = 8;
|
||||
e->line_spacing = 3;
|
||||
|
@ -117,6 +118,14 @@ bool heditor_move_to(heditor *e, int target_cursor)
|
|||
return changed;
|
||||
}
|
||||
|
||||
void heditor_set_insert_mode(heditor *e, bool insert)
|
||||
{
|
||||
e->insert = false;
|
||||
if(e->source && e->source->intf->cap & SOURCE_EXPAND)
|
||||
e->insert = insert;
|
||||
e->widget.update = true;
|
||||
}
|
||||
|
||||
void heditor_set_source(heditor *e, source_t *source)
|
||||
{
|
||||
e->source = source;
|
||||
|
@ -208,9 +217,15 @@ static void heditor_poly_render(void *e0, int x, int y)
|
|||
|
||||
int fg = C_BLACK;
|
||||
if(e->cursor == o) {
|
||||
drect(bytes_x - 1, line_y - 1, bytes_x + text_w,
|
||||
line_y + e->font->line_height, C_BLACK);
|
||||
fg = C_WHITE;
|
||||
if(e->insert) {
|
||||
drect(bytes_x - 2, line_y - 1, bytes_x - 1,
|
||||
line_y + e->font->line_height, C_BLACK);
|
||||
}
|
||||
else {
|
||||
drect(bytes_x - 1, line_y - 1, bytes_x + text_w,
|
||||
line_y + e->font->line_height, C_BLACK);
|
||||
fg = C_WHITE;
|
||||
}
|
||||
}
|
||||
dtext(bytes_x, line_y, fg, byte);
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ typedef struct {
|
|||
int scroll;
|
||||
/* Current cursor position in source, in bytes */
|
||||
int cursor;
|
||||
/* Current insertion mode (false=overwrite, true=insert) */
|
||||
bool insert;
|
||||
/* Number of visible lines */
|
||||
int8_t visible_lines;
|
||||
/* Number of bytes printed per line */
|
||||
|
@ -47,6 +49,10 @@ void heditor_set_source(heditor *e, source_t *source);
|
|||
/* Scroll to the requested cursor position. */
|
||||
bool heditor_move_to(heditor *e, int offset);
|
||||
|
||||
/* Change the insertion mode; true for insertion, false for overwrite. If the
|
||||
source doesn't support insertion, this function is a no-op. */
|
||||
void heditor_set_insert_mode(heditor *e, bool insert);
|
||||
|
||||
/* Trivial properties */
|
||||
void heditor_set_font(heditor *e, font_t const *font);
|
||||
void heditor_set_line_spacing(heditor *e, int line_spacig);
|
||||
|
|
343
src/main.c
343
src/main.c
|
@ -15,38 +15,170 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void update_status(jlabel *status, jlabel *title, source_t *s)
|
||||
// Global application data //
|
||||
|
||||
struct {
|
||||
/* Title widget, status bar widget, f-keys widget. */
|
||||
jlabel *title;
|
||||
jlabel *status;
|
||||
jfkeys *fkeys;
|
||||
/* Source info widget. */
|
||||
jlabel *source_info;
|
||||
|
||||
/* Current data source. */
|
||||
source_t *source;
|
||||
/* Current edition mode. */
|
||||
bool insert;
|
||||
|
||||
/* Current view. */
|
||||
int view;
|
||||
/* Current position in the menus. */
|
||||
int fkmenu;
|
||||
} app;
|
||||
|
||||
enum {
|
||||
VIEW_EDITOR,
|
||||
VIEW_INFO,
|
||||
VIEW_STATS,
|
||||
};
|
||||
enum {
|
||||
FK_EDITOR,
|
||||
FK_EDITOR_SOURCE,
|
||||
FK_EDITOR_OPEN,
|
||||
FK_VIEW,
|
||||
};
|
||||
|
||||
// General UI update functions //
|
||||
|
||||
static void fkeys_sprintf(jfkeys *fk, char const *fmt, ...)
|
||||
{
|
||||
static char str[128];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(str, sizeof str, fmt, args);
|
||||
va_end(args);
|
||||
jfkeys_set(fk, str);
|
||||
}
|
||||
|
||||
static void update_fkeys(void)
|
||||
{
|
||||
if(app.fkmenu == FK_EDITOR) {
|
||||
if(!app.source)
|
||||
jfkeys_set(app.fkeys, "/OPEN;;;;;");
|
||||
else {
|
||||
bool can_insert = (app.source->intf->cap & SOURCE_EXPAND) != 0;
|
||||
fkeys_sprintf(app.fkeys, "/OPEN;/SOURCE;%s%s;@GOTO;;#VIEW",
|
||||
can_insert ? "@" : "#",
|
||||
app.insert ? "INSERT" : "OVERW");
|
||||
}
|
||||
}
|
||||
else if(app.fkmenu == FK_EDITOR_SOURCE) {
|
||||
jfkeys_set(app.fkeys, "@SAVE;@SAVE AS;;;;");
|
||||
}
|
||||
else if(app.fkmenu == FK_EDITOR_OPEN) {
|
||||
jfkeys_set(app.fkeys, "@FILE;@LAZY;@MEMORY;@NEW");
|
||||
}
|
||||
else if(app.fkmenu == FK_VIEW) {
|
||||
fkeys_sprintf(app.fkeys, ";;;%sEDITOR;%sINFO;%sSTATS",
|
||||
app.view == VIEW_EDITOR ? "#" : "@",
|
||||
app.view == VIEW_INFO ? "#" : "@",
|
||||
app.view == VIEW_STATS ? "#" : "@");
|
||||
}
|
||||
}
|
||||
|
||||
static void update_status(void)
|
||||
{
|
||||
/* TODO: (*) and (**) markers on title/status bar */
|
||||
source_t *s = app.source;
|
||||
|
||||
if(!s) {
|
||||
jlabel_set_text(status, "-");
|
||||
jlabel_set_text(title, "Hex Editor");
|
||||
jwidget_set_visible(app.status, false);
|
||||
jlabel_set_text(app.title, "Hex Editor");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t size = source_size(s);
|
||||
jwidget_set_visible(app.status, true);
|
||||
|
||||
if(s->origin)
|
||||
jlabel_asprintf(title, "%s (%d B) - Hex Editor", s->origin, size);
|
||||
jlabel_asprintf(app.title, "%s (%d B) - Hex Editor", s->origin, size);
|
||||
else
|
||||
jlabel_set_text(title, "(nil) - Hex Editor");
|
||||
jlabel_set_text(app.title, "(nil) - Hex Editor");
|
||||
|
||||
jlabel_asprintf(status, "Buffer: @%X %d/%d",
|
||||
jlabel_asprintf(app.status, "Buffer: @%X %d/%d",
|
||||
s->buf_offset, s->buf->data_size, s->buf->mem_size);
|
||||
}
|
||||
|
||||
static void update_source_info(void)
|
||||
{
|
||||
source_t *s = app.source;
|
||||
|
||||
if(!s) {
|
||||
jlabel_set_text(app.source_info, "No source.");
|
||||
return;
|
||||
}
|
||||
|
||||
char caps[48];
|
||||
memset(caps, 0, sizeof caps);
|
||||
|
||||
if(s->intf->cap & SOURCE_WRITE)
|
||||
strcat(caps, " WRITE");
|
||||
if(s->intf->cap & SOURCE_EXPAND)
|
||||
strcat(caps, " EXPAND");
|
||||
if(s->intf->cap & SOURCE_SHRINK)
|
||||
strcat(caps, " SHRINK");
|
||||
|
||||
jlabel_asprintf(app.source_info,
|
||||
"%s\n"
|
||||
"Size: %d bytes\n"
|
||||
"Front buffer: %p, size %d/%d (x%d)\n"
|
||||
"Front buffer maps to: @%d, size TODO\n"
|
||||
"Capabilities: %s",
|
||||
s->origin ? s->origin : "<unnamed source>",
|
||||
source_size(s),
|
||||
s->buf->mem, s->buf->data_size, s->buf->mem_size, s->buf->block_size,
|
||||
s->buf_offset, /* s->buf_size, */
|
||||
caps + 1);
|
||||
}
|
||||
|
||||
static jwidget *make_popup(void *parent0, int w, int h)
|
||||
{
|
||||
J_CAST(parent)
|
||||
|
||||
jwidget *popup = jwidget_create(parent);
|
||||
if(!popup)
|
||||
return NULL;
|
||||
|
||||
jwidget_set_floating(popup, true);
|
||||
jwidget_set_border(popup, J_BORDER_SOLID, 2, C_BLACK);
|
||||
jwidget_set_padding(popup, 4, 4, 4, 4);
|
||||
jwidget_set_background(popup, C_WHITE);
|
||||
jwidget_set_visible(popup, false);
|
||||
jwidget_set_fixed_size(popup, w, h);
|
||||
|
||||
popup->x = (DWIDTH - w) / 2;
|
||||
popup->y = (DHEIGHT - h) / 2;
|
||||
return popup;
|
||||
}
|
||||
|
||||
void hex_view(void)
|
||||
{
|
||||
source_t *s = NULL;
|
||||
app.source = NULL;
|
||||
app.insert = false;
|
||||
app.view = VIEW_EDITOR;
|
||||
app.fkmenu = FK_EDITOR;
|
||||
|
||||
jscene *scene = jscene_create_fullscreen(NULL);
|
||||
jlabel *title = jlabel_create("Hex Editor", scene);
|
||||
jwidget *stack = jwidget_create(scene);
|
||||
jfkeys *fkeys = jfkeys_create("@JUMP;@OPEN",scene);
|
||||
jfkeys *fkeys = jfkeys_create(";;;;;",scene);
|
||||
|
||||
if(!scene || !title || !stack || !fkeys) {
|
||||
jwidget_destroy(scene);
|
||||
return;
|
||||
}
|
||||
app.title = title;
|
||||
app.fkeys = fkeys;
|
||||
|
||||
jwidget_set_background(title, C_BLACK);
|
||||
jlabel_set_text_color(title, C_WHITE);
|
||||
|
@ -62,58 +194,51 @@ void hex_view(void)
|
|||
|
||||
jwidget *tab = jwidget_create(NULL);
|
||||
heditor *mem = heditor_create(tab); /* previously 321x153 */
|
||||
jinput *goto_input = jinput_create("Goto: ", 12, tab);
|
||||
jlabel *status = jlabel_create("", tab);
|
||||
app.status = status;
|
||||
|
||||
jwidget_set_margin(mem, 3, 0, 3, 0);
|
||||
jwidget_set_margin(mem, 2, 0, 0, 0);
|
||||
jwidget_set_stretch(mem, 1, 1, false);
|
||||
|
||||
jwidget_set_stretch(status, 1, 0, false);
|
||||
jwidget_set_padding(status, 2, 0, 0, 0);
|
||||
jwidget_set_borders(status, J_BORDER_SOLID, C_BLACK, 1, 0, 0, 0);
|
||||
|
||||
jwidget_set_stretch(goto_input, 1, 0, false);
|
||||
jwidget_set_visible(goto_input, false);
|
||||
|
||||
jlayout_set_vbox(tab)->spacing = 3;
|
||||
jwidget_add_child(stack, tab);
|
||||
jwidget_set_stretch(tab, 1, 1, false);
|
||||
|
||||
update_status(status, title, s);
|
||||
jscene_show_and_focus(scene, mem);
|
||||
|
||||
// File selection tab //
|
||||
|
||||
jfileselect *fileselect = jfileselect_create(stack);
|
||||
jfileselect_set_show_file_size(fileselect, true);
|
||||
jwidget_set_stretch(fileselect, 1, 1, false);
|
||||
|
||||
// Goto popup //
|
||||
// Source info popup //
|
||||
|
||||
jwidget *popup_goto = jwidget_create(scene);
|
||||
jwidget_set_floating(popup_goto, true);
|
||||
jwidget_set_border(popup_goto, J_BORDER_SOLID, 2, C_BLACK);
|
||||
jwidget_set_padding(popup_goto, 4, 4, 4, 4);
|
||||
jwidget_set_background(popup_goto, C_WHITE);
|
||||
jwidget_set_visible(popup_goto, false);
|
||||
jwidget_set_fixed_size(popup_goto, DWIDTH * 3 / 4, DHEIGHT / 2);
|
||||
popup_goto->x = DWIDTH / 8;
|
||||
popup_goto->y = DHEIGHT / 4;
|
||||
jlayout_set_vbox(popup_goto)->spacing = 3;
|
||||
jwidget *popup_source_info = make_popup(scene, DWIDTH*3/4, DHEIGHT/2);
|
||||
jlayout_set_vbox(popup_source_info)->spacing = 3;
|
||||
|
||||
jlabel *source_info = jlabel_create("", popup_source_info);
|
||||
jlabel_set_wrap_mode(source_info, J_WRAP_WORD);
|
||||
jlabel_set_line_spacing(source_info, 2);
|
||||
jlabel_set_block_alignment(source_info, J_ALIGN_LEFT, J_ALIGN_TOP);
|
||||
jwidget_set_stretch(source_info, 1, 1, false);
|
||||
|
||||
jinput *goto_input = jinput_create("Go to: ", 12, popup_goto);
|
||||
jwidget_set_stretch(goto_input, 1, 0, false);
|
||||
// Initial state //
|
||||
|
||||
jlabel *goto_help = jlabel_create(
|
||||
"In hexadecimal. Use [ALPHA] to type letters.\n"
|
||||
"Prefix with [+] or [-] to move relative to the current position.",
|
||||
popup_goto);
|
||||
jlabel_set_wrap_mode(goto_help, J_WRAP_WORD);
|
||||
jlabel_set_line_spacing(goto_help, 2);
|
||||
jlabel_set_block_alignment(goto_help, J_ALIGN_LEFT, J_ALIGN_TOP);
|
||||
jwidget_set_stretch(goto_help, 1, 1, false);
|
||||
update_status();
|
||||
update_fkeys();
|
||||
jscene_show_and_focus(scene, mem);
|
||||
|
||||
// Event handling //
|
||||
|
||||
int key = 0;
|
||||
while(key != KEY_EXIT)
|
||||
{
|
||||
bool input_focus = (jscene_focused_widget(scene) == goto_input);
|
||||
while(1) {
|
||||
jevent e = jscene_run(scene);
|
||||
|
||||
if(e.type == JSCENE_PAINT) {
|
||||
|
@ -124,50 +249,146 @@ void hex_view(void)
|
|||
if(e.type == JINPUT_VALIDATED) {
|
||||
/* Parse string into hexa */
|
||||
char const *str = jinput_value(goto_input);
|
||||
uint32_t target = strtoul(str, NULL, 16);
|
||||
heditor_move_to(mem, target);
|
||||
int mode = (*str == '+') ? 1 : (*str == '-') ? -1 : 0;
|
||||
int target = strtoul(str + (mode != 0), NULL, 16);
|
||||
heditor_move_to(mem, mode ? mem->cursor + target * mode : target);
|
||||
}
|
||||
if(e.type == JINPUT_VALIDATED || e.type == JINPUT_CANCELED) {
|
||||
jwidget_set_visible(popup_goto, false);
|
||||
jwidget_set_visible(goto_input, false);
|
||||
jscene_show_and_focus(scene, mem);
|
||||
}
|
||||
if(e.type == JFILESELECT_LOADED) {
|
||||
char const *path = jfileselect_current_folder(fileselect);
|
||||
jlabel_asprintf(title, "Browsing: %s", path ? path : "(nil)");
|
||||
}
|
||||
if(e.type == JFILESELECT_CANCELED || e.type == JFILESELECT_VALIDATED) {
|
||||
if(e.type == JFILESELECT_CANCELED) {
|
||||
jscene_show_and_focus(scene, mem);
|
||||
|
||||
if(e.type == JFILESELECT_VALIDATED) {
|
||||
char const *file = jfileselect_selected_file(fileselect);
|
||||
if(file) {
|
||||
if(s)
|
||||
source_free(s);
|
||||
s = source_loaded_file_open(file);
|
||||
if(s) {
|
||||
source_load(s, 0, 2048);
|
||||
}
|
||||
heditor_set_source(mem, s);
|
||||
}
|
||||
update_status();
|
||||
}
|
||||
if(e.type == JFILESELECT_VALIDATED) {
|
||||
char const *file = jfileselect_selected_file(fileselect);
|
||||
if(file) {
|
||||
if(app.source)
|
||||
source_free(app.source);
|
||||
app.source = source_loaded_file_open(file);
|
||||
app.fkmenu = FK_EDITOR;
|
||||
app.insert = false;
|
||||
source_load(app.source, 0, 2048);
|
||||
heditor_set_source(mem, app.source);
|
||||
heditor_set_insert_mode(mem, app.insert);
|
||||
update_fkeys();
|
||||
}
|
||||
update_status(status, title, s);
|
||||
jscene_show_and_focus(scene, mem);
|
||||
update_status();
|
||||
}
|
||||
if(e.type == HEDITOR_CHANGED) {
|
||||
update_status(status, title, s);
|
||||
update_status();
|
||||
}
|
||||
|
||||
// Fkey menu navigation //
|
||||
|
||||
if(e.type != JSCENE_KEY || e.key.type == KEYEV_UP) continue;
|
||||
key = e.key.key;
|
||||
|
||||
if(key == KEY_F1 && stack->layout_stack.active == 0 && !input_focus) {
|
||||
jinput_clear(goto_input);
|
||||
jwidget_set_visible(popup_goto, true);
|
||||
jscene_set_focused_widget(scene, goto_input);
|
||||
if(jscene_focused_widget(scene) == goto_input)
|
||||
continue;
|
||||
|
||||
if(app.fkmenu == FK_EDITOR) {
|
||||
if(key == KEY_F1) {
|
||||
app.fkmenu = FK_EDITOR_OPEN;
|
||||
update_fkeys();
|
||||
}
|
||||
else if(key == KEY_F2 && app.source) {
|
||||
app.fkmenu = FK_EDITOR_SOURCE;
|
||||
update_fkeys();
|
||||
}
|
||||
else if(key == KEY_F3 && app.source) {
|
||||
if(app.source->intf->cap & SOURCE_EXPAND) {
|
||||
app.insert = !app.insert;
|
||||
heditor_set_insert_mode(mem, app.insert);
|
||||
update_fkeys();
|
||||
}
|
||||
}
|
||||
else if(key == KEY_F4 && app.source) {
|
||||
jinput_clear(goto_input);
|
||||
jscene_show_and_focus(scene, goto_input);
|
||||
}
|
||||
else if(key == KEY_F6 && app.source) {
|
||||
app.fkmenu = FK_VIEW;
|
||||
update_fkeys();
|
||||
}
|
||||
}
|
||||
if(key == KEY_F2 && !input_focus) {
|
||||
jfileselect_browse(fileselect, "/");
|
||||
jscene_show_and_focus(scene, fileselect);
|
||||
else if(app.fkmenu == FK_EDITOR_SOURCE) {
|
||||
if(key == KEY_F1 && app.source) {
|
||||
/* TODO: Source save */
|
||||
}
|
||||
else if(key == KEY_F2 && app.source) {
|
||||
/* TODO: Source save as */
|
||||
}
|
||||
else if(key == KEY_EXIT) {
|
||||
app.fkmenu = FK_EDITOR;
|
||||
update_fkeys();
|
||||
}
|
||||
}
|
||||
else if(app.fkmenu == FK_EDITOR_OPEN) {
|
||||
if(key == KEY_F1 || key == KEY_F2) {
|
||||
/* TODO: Check if current source is unsaved? */
|
||||
jfileselect_browse(fileselect, "/");
|
||||
jscene_show_and_focus(scene, fileselect);
|
||||
}
|
||||
else if(key == KEY_F3) {
|
||||
/* TODO: Memory browser */
|
||||
}
|
||||
else if(key == KEY_F4) {
|
||||
/* TODO: New empty source */
|
||||
}
|
||||
else if(key == KEY_EXIT) {
|
||||
app.fkmenu = FK_EDITOR;
|
||||
update_fkeys();
|
||||
}
|
||||
}
|
||||
else if(app.fkmenu == FK_VIEW) {
|
||||
if(key == KEY_F4) {
|
||||
jscene_show_and_focus(scene, mem);
|
||||
app.fkmenu = FK_EDITOR;
|
||||
update_fkeys();
|
||||
}
|
||||
else if(key == KEY_F5) {
|
||||
/* TODO: Show info panel */
|
||||
// jscene_show_and_focus(scene, tab_info);
|
||||
// app.fkmenu = FK_INFO;
|
||||
// update_fkeys();
|
||||
}
|
||||
else if(key == KEY_F6) {
|
||||
/* TODO: Show statistics panel */
|
||||
// jscene_show_and_focus(scene, tab_stats);
|
||||
// app.fkmenu = FK_STATS;
|
||||
// update_fkeys();
|
||||
}
|
||||
else if(key == KEY_EXIT) {
|
||||
// if(current_tab == tab_editor)
|
||||
app.fkmenu = FK_EDITOR;
|
||||
// else if(current_tab == tab_info)
|
||||
// app.fkmenu = FK_INFO;
|
||||
// else if(current_tab == tab_stats)
|
||||
// app.fkmenu = FK_STATS;
|
||||
update_fkeys();
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
if(key == KEY_F3 && !input_focus) {
|
||||
if(!jwidget_visible(popup_source_info)) {
|
||||
update_source_info();
|
||||
jwidget_set_visible(popup_source_info, true);
|
||||
jscene_set_focused_widget(scene, source_info);
|
||||
}
|
||||
else {
|
||||
jwidget_set_visible(popup_source_info, false);
|
||||
jscene_show_and_focus(scene, mem);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
jwidget_destroy(scene);
|
||||
}
|
||||
|
|
|
@ -30,8 +30,8 @@ size_t source_size(source_t *s)
|
|||
|
||||
ssize_t source_load(source_t *s, off_t offset, size_t segment_size)
|
||||
{
|
||||
if(!buffer_resize(s->buf, segment_size))
|
||||
return false;
|
||||
if(!s || !buffer_resize(s->buf, segment_size))
|
||||
return -1;
|
||||
|
||||
s->buf_offset = offset;
|
||||
ssize_t rc = s->intf->read(s->cookie, s->buf->mem, offset, segment_size);
|
||||
|
|
Loading…
Reference in New Issue