forked from Lephenixnoir/hex-editor
401 lines
12 KiB
C
401 lines
12 KiB
C
#include "source.h"
|
|
#include "heditor.h"
|
|
|
|
#include <gint/display.h>
|
|
#include <gint/keyboard.h>
|
|
#include <gint/exc.h>
|
|
|
|
#include <justui/jscene.h>
|
|
#include <justui/jlabel.h>
|
|
#include <justui/jfkeys.h>
|
|
#include <justui/jinput.h>
|
|
#include <justui/jfileselect.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
// 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 : "<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)
|
|
{
|
|
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;
|
|
}
|