#include "source.h" #include "heditor.h" #include #include #include #include #include #include #include #include #include #include #include // 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) { 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(app.title, "%s (%d B) - Hex Editor", s->origin, size); else jlabel_set_text(app.title, "(nil) - Hex Editor"); 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 : "", 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) { 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(";;;;;",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); jwidget_set_stretch(title, 1, 0, false); jwidget_set_padding(title, 3, 6, 3, 6); jlayout_set_vbox(scene)->spacing = 3; jlayout_set_stack(stack); jwidget_set_padding(stack, 0, 6, 0, 6); jwidget_set_stretch(stack, 1, 1, false); // Main tab // 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, 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); // File selection tab // jfileselect *fileselect = jfileselect_create(stack); jfileselect_set_show_file_size(fileselect, true); jwidget_set_stretch(fileselect, 1, 1, false); // Source info popup // 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); // Initial state // update_status(); update_fkeys(); jscene_show_and_focus(scene, mem); // Event handling // int key = 0; while(1) { jevent e = jscene_run(scene); if(e.type == JSCENE_PAINT) { dclear(C_WHITE); jscene_render(scene); dupdate(); } if(e.type == JINPUT_VALIDATED) { /* Parse string into hexa */ char const *str = jinput_value(goto_input); 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(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) { jscene_show_and_focus(scene, mem); 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(); } jscene_show_and_focus(scene, mem); update_status(); } if(e.type == HEDITOR_CHANGED) { update_status(); } // Fkey menu navigation // if(e.type != JSCENE_KEY || e.key.type == KEYEV_UP) continue; key = e.key.key; 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(); } } 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); } int main(void) { hex_view(); return 1; }