[ghex/gtk4-port: 35/91] Implement tabbed viewing and file opening.
- From: Logan Rathbone <larathbone src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [ghex/gtk4-port: 35/91] Implement tabbed viewing and file opening.
- Date: Thu, 12 Aug 2021 23:35:10 +0000 (UTC)
commit e173974ab3e55e2ec5672a001793d0e95d036f4f
Author: Logan Rathbone <poprocks gmail com>
Date: Sat Jan 16 21:37:28 2021 -0500
Implement tabbed viewing and file opening.
Closing tabs not yet implemented, nor is saving, so beware.
I moved the scrollbar back to the GtkHex widget. It is not appearing,
likely due to bugs of overdrawing I need to work on when I switch gears
back to the widget. Scrolling still works, but the scrollbar does not
visibly appear.
src/Makefile | 4 +-
src/STUB.c | 79 +----
src/chartable.c | 72 ++---
src/chartable.h | 2 -
src/converter.c | 258 +++++++--------
src/converter.h | 23 +-
src/ghex-application-window.c | 708 +++++++++++++++++++++++++++++++++++++----
src/ghex-application-window.h | 8 +-
src/ghex-application-window.ui | 21 +-
src/gtkhex.c | 30 +-
src/gtkhex.h | 1 +
11 files changed, 843 insertions(+), 363 deletions(-)
---
diff --git a/src/Makefile b/src/Makefile
index a5ded9a6..8ef5bed1 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -3,14 +3,14 @@
DEBUG=-g -DENABLE_DEBUG
CC=gcc
GTK_CFLAGS=$(shell pkg-config --libs --cflags gtk4)
-CFLAGS=-Wall -Wextra -std=c11 -pedantic \
+CFLAGS=-Wall -Wextra -Werror=implicit -std=c11 -pedantic \
$(GTK_CFLAGS) \
-I../build -I. \
$(DEBUG)
.PHONY: clean compile-resources
-STUB: gtkhex.o hex-document.o ghex-application-window.o hex-dialog.o findreplace.o chartable.o resources.o
+STUB: gtkhex.o hex-document.o ghex-application-window.o hex-dialog.o findreplace.o chartable.o converter.o
resources.o
compile-resources:
glib-compile-resources ghex.gresource.xml --target=resources.c --generate-source
diff --git a/src/STUB.c b/src/STUB.c
index 2c8e2504..4b030a2f 100644
--- a/src/STUB.c
+++ b/src/STUB.c
@@ -6,89 +6,16 @@
static void
activate (GtkApplication *app,
- gpointer user_data)
+ gpointer user_data)
{
GtkWidget *window;
- GtkWidget *hex;
- HexDocument *doc;
- window = ghex_application_window_new ();
- doc = hex_document_new_from_file ("main.c");
- hex = gtk_hex_new (doc);
- gtk_hex_show_offsets (GTK_HEX(hex), TRUE);
-
- ghex_application_window_add_hex (GHEX_APPLICATION_WINDOW(window), GTK_HEX(hex));
-
-
-#if 0
- GtkBuilder *builder;
- GtkWidget *window;
- GtkWidget *box;
- GtkBox *conversions_box;
- GtkWidget *conversions_pane;
- GtkWidget *hex;
- GtkHeaderBar *headerbar;
- GtkWidget *label;
- GtkStatusbar *statusbar;
- guint id;
- GtkAdjustment *adj;
- GtkScrollbar *scrollbar;
- GtkStyleContext *context;
- GtkCssProvider *provider;
- HexDocument *doc;
-
- (void)user_data; /* unused for now. */
-
- builder = gtk_builder_new_from_resource ("/org/gnome/ghex/application.ui");
- doc = hex_document_new_from_file ("main.c");
- hex = gtk_hex_new (doc);
- gtk_hex_show_offsets (GTK_HEX(hex), TRUE);
-
- box = GTK_WIDGET(gtk_builder_get_object (builder, "child_box"));
- conversions_box = GTK_BOX(gtk_builder_get_object (builder, "conversions_box"));
- gtk_widget_set_name (GTK_WIDGET(conversions_box), "conversions_box");
- window = GTK_WIDGET(gtk_builder_get_object (builder, "main_window"));
- scrollbar = GTK_SCROLLBAR(gtk_builder_get_object (builder, "scrollbar"));
- headerbar = GTK_HEADER_BAR (gtk_builder_get_object (builder, "headerbar"));
- statusbar = GTK_STATUSBAR (gtk_builder_get_object (builder, "statusbar"));
-
- conversions_pane = conversions_pane_new ();
-
- label = gtk_label_new (NULL);
-
- provider = gtk_css_provider_new ();
- gtk_css_provider_load_from_path (provider, "style.css");
- context = gtk_widget_get_style_context (GTK_WIDGET(conversions_box));
- gtk_style_context_add_provider (context,
- GTK_STYLE_PROVIDER(provider),
- GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
-
- context = gtk_widget_get_style_context (label);
- gtk_style_context_add_class (context, "title");
- gtk_label_set_markup (GTK_LABEL(label), "main.c");
-
- gtk_header_bar_set_title_widget (headerbar, label);
-
- id = gtk_statusbar_get_context_id (statusbar, "offset");
- gtk_statusbar_push (statusbar, id, "Offset: 0x0");
-
- adj = gtk_hex_get_adjustment (GTK_HEX(hex));
-
- gtk_scrollbar_set_adjustment (scrollbar, adj);
-
- gtk_box_append (conversions_box, conversions_pane);
- gtk_box_prepend (GTK_BOX(box), hex);
-#endif
+ (void)user_data; /* unused */
+ window = ghex_application_window_new (app);
gtk_window_set_application (GTK_WINDOW(window), app);
-
gtk_window_present (GTK_WINDOW(window));
-// gtk_widget_show (window);
-
-#if 0
- g_object_unref (builder);
-#endif
}
int
diff --git a/src/chartable.c b/src/chartable.c
index af136d0f..078d6406 100644
--- a/src/chartable.c
+++ b/src/chartable.c
@@ -38,7 +38,8 @@
//#include "ghex-window.h"
//#include "ui.h"
-GtkWidget *char_table = NULL;
+/* STATIC GLOBALS */
+
static GtkTreeSelection *sel_row = NULL;
static GtkHex *gh_glob = NULL;
@@ -78,7 +79,7 @@ static char *ascii_non_printable_label[] = {
};
static void
-insert_char(GtkTreeView *treeview, GtkTreeModel *model)
+insert_char (GtkTreeView *treeview, GtkTreeModel *model)
{
GtkTreeIter iter;
GtkTreeSelection *selection;
@@ -107,55 +108,39 @@ insert_char(GtkTreeView *treeview, GtkTreeModel *model)
sel_row = selection;
}
-#if 0
-static gboolean select_chartable_row_cb(GtkTreeView *treeview, GdkEventButton *event, gpointer data)
+static void
+chartable_row_activated_cb (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ gpointer user_data)
{
- GtkTreeModel *model = GTK_TREE_MODEL(data);
+ GtkTreeModel *model = GTK_TREE_MODEL(user_data);
- if(event->type == GDK_2BUTTON_PRESS)
- insert_char(treeview, model);
- return FALSE;
-}
-#endif
+ g_debug("%s: start", __func__);
-static void hide_chartable_cb (GtkWidget *widget, GtkWidget *win)
-{
- /* widget may be NULL if called from keypress cb! */
- // FIXME - commenting out for now to get to build.
-// ghex_window_sync_char_table_item (NULL, FALSE);
- gtk_widget_hide (win);
+ insert_char (tree_view, model);
}
-// REWRITE FOR EVENT CONTROLLERS
-#if 0
-static gint char_table_key_press_cb (GtkWindow *w, GdkEventKey *e, gpointer data)
-{
- if (e->keyval == GDK_KEY_Escape) {
- hide_chartable_cb(NULL, GTK_WIDGET(w));
- return TRUE;
- }
- return FALSE;
-}
-static gint key_press_cb (GtkTreeView *treeview, GdkEventKey *e, gpointer data)
+#if 0
+static gboolean select_chartable_row_cb(GtkTreeView *treeview, GdkEventButton *event, gpointer data)
{
GtkTreeModel *model = GTK_TREE_MODEL(data);
- if (e->keyval == GDK_KEY_Return) {
+ if(event->type == GDK_2BUTTON_PRESS)
insert_char(treeview, model);
- return TRUE;
- }
return FALSE;
}
+#endif
-static gboolean
-char_table_delete_event_cb(GtkWidget *widget, GdkEventAny *e, gpointer user_data)
+static void hide_chartable_cb (GtkButton *button, gpointer user_data)
{
- ghex_window_sync_char_table_item(NULL, FALSE);
- gtk_widget_hide(widget);
- return TRUE;
+ GtkWindow *win = GTK_WINDOW(user_data);
+
+ (void)button; /* unused */
+
+ gtk_window_close (win);
}
-#endif
GtkWidget *create_char_table(GtkWindow *parent_win, GtkHex *gh)
{
@@ -187,13 +172,6 @@ GtkWidget *create_char_table(GtkWindow *parent_win, GtkHex *gh)
gtk_window_set_transient_for (GTK_WINDOW(ct), parent_win);
}
- // TODO
-// g_signal_connect(G_OBJECT(ct), "close-request",
-// G_CALLBACK(char_table_close_request_cb), NULL);
- // handles esc - possibly rewrite to use Event Controllers.
-// g_signal_connect(G_OBJECT(ct), "key_press_event",
-// G_CALLBACK(char_table_key_press_cb), NULL);
-//
gtk_window_set_title(GTK_WINDOW(ct), _("Character table"));
sw = gtk_scrolled_window_new ();
@@ -206,6 +184,8 @@ GtkWidget *create_char_table(GtkWindow *parent_win, GtkHex *gh)
store = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_STRING);
cell_renderer = gtk_cell_renderer_text_new();
ctv = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+ gtk_widget_set_hexpand (ctv, TRUE);
+ gtk_widget_set_vexpand (ctv, TRUE);
for (i = 0; i < 5; i++) {
column = gtk_tree_view_column_new_with_attributes (real_titles[i], cell_renderer, "text", i,
NULL);
@@ -255,6 +235,9 @@ GtkWidget *create_char_table(GtkWindow *parent_win, GtkHex *gh)
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW (ctv));
gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+ g_signal_connect (ctv, "row-activated",
+ G_CALLBACK(chartable_row_activated_cb), GTK_TREE_MODEL(store));
+
// REWRITE
#if 0
g_signal_connect(G_OBJECT(ct), "delete-event",
@@ -268,7 +251,6 @@ GtkWidget *create_char_table(GtkWindow *parent_win, GtkHex *gh)
gtk_widget_grab_focus(ctv);
cbtn = gtk_button_new_with_mnemonic (_("_Close"));
- gtk_widget_show(cbtn);
g_signal_connect(G_OBJECT (cbtn), "clicked",
G_CALLBACK(hide_chartable_cb), ct);
@@ -291,7 +273,7 @@ GtkWidget *create_char_table(GtkWindow *parent_win, GtkHex *gh)
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW(sw), ctv);
gtk_window_set_child (GTK_WINDOW (ct), vbox);
- gtk_widget_set_size_request(ct, 320, 256);
+ gtk_widget_set_size_request(ct, 320, 320);
return ct;
}
diff --git a/src/chartable.h b/src/chartable.h
index 3e47927b..a0d4640d 100644
--- a/src/chartable.h
+++ b/src/chartable.h
@@ -31,8 +31,6 @@ G_BEGIN_DECLS
GtkWidget *create_char_table (GtkWindow *parent_win, /* can-NULL */
GtkHex *gh);
-extern GtkWidget *char_table;
-
G_END_DECLS
#endif /* GHEX_CHARTABLE_H */
diff --git a/src/converter.c b/src/converter.c
index ed2fa185..ecb2af2e 100644
--- a/src/converter.c
+++ b/src/converter.c
@@ -1,3 +1,5 @@
+/* vim: colorcolumn=80 ts=4 sw=4
+ */
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* converter.c - conversion dialog
@@ -36,13 +38,44 @@
#include "gtkhex.h"
#include "converter.h"
-#include "ghex-window.h"
+//#include "ghex-window.h"
+
+/* OPAQUE DATATYPES */
+
+typedef struct _Converter {
+ GtkWidget *window;
+ GtkHex *gh;
+ GtkWidget *entry[5];
+ GtkWidget *close;
+ GtkWidget *get;
+
+ gulong value;
+} Converter;
+
+/* FUNCTION DECLARATIONS */
static void conv_entry_cb(GtkEntry *, gint);
static void get_cursor_val_cb(GtkButton *button, Converter *conv);
static void set_values(Converter *conv, gulong val);
+static gchar * clean(gchar *ptr);
+
+/* STATIC GLOBALS */
+
+static Converter *converter = NULL;
+
+/* --- */
-Converter *converter = NULL;
+/* reimplement this old function to avoid headaches. FIXME - make this common,
+ * perhaps?
+ */
+static void
+my_gtk_entry_set_text (GtkEntry *entry, char *text)
+{
+ GtkEntryBuffer *buffer;
+
+ buffer = gtk_entry_get_buffer (entry);
+ gtk_entry_buffer_set_text (buffer, text, -1);
+}
static gboolean
is_char_ok(signed char c, gint base)
@@ -59,7 +92,7 @@ is_char_ok(signed char c, gint base)
c = c - '0';
- return((c < 0) ||(c >(base - 1))) ? FALSE : TRUE;
+ return ((c < 0) ||(c >(base - 1))) ? FALSE : TRUE;
}
static void
@@ -73,7 +106,6 @@ entry_filter(GtkEditable *editable, const gchar *text, gint length,
/* thou shalt optimize for the common case */
if(length == 1) {
if(!is_char_ok(*text, base)) {
- gdk_beep();
g_signal_stop_emission_by_name(G_OBJECT(editable), "insert_text");
}
return;
@@ -91,190 +123,114 @@ entry_filter(GtkEditable *editable, const gchar *text, gint length,
if(l == length)
return;
- gdk_beep();
g_signal_stop_emission_by_name(G_OBJECT(editable), "insert_text");
-#if 0 /* Pasting is not working. gtk is doing weird things. Chema */
- g_signal_handler_block_by_func (editable, entry_filter, data);
- g_print("Inserting -->%s<--- of length %i in pos %i\n", s, l, *pos);
- gtk_editable_insert_text(editable, s, l, pos);
- g_signal_handler_unblock_by_func (editable, entry_filter, data);
-#endif
-}
-
-static gint
-entry_key_press_event_cb(GtkWidget *widget, GdkEventKey *event,
- gpointer not_used)
-{
- /* For gtk_entries don't let the gtk handle the event if
- * the alt key was pressed. Otherwise the accelerators do not work cause
- * gtk takes care of this event
- */
- if(event->state & GDK_MOD1_MASK)
- g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
-
- return FALSE;
}
#define BUFFER_LEN 40
static GtkWidget *
-create_converter_entry(const gchar *name, GtkWidget *grid,
- GtkAccelGroup *accel_group, gint pos, gint base)
+create_converter_entry(const gchar *name, GtkWidget *grid, gint pos, gint base)
{
GtkWidget *label;
GtkWidget *entry;
-#if 0
- gint accel_key;
-#endif /* 0/1 */
gchar str[BUFFER_LEN + 1];
/* label */
label = gtk_label_new_with_mnemonic(name);
- gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
gtk_grid_attach (GTK_GRID (grid), label, 0, pos, 1, 1);
-#if 0
- accel_key = gtk_label_parse_uline(GTK_LABEL(label), name);
-#endif /* 0/1 */
- gtk_widget_show(label);
/* entry */
entry = gtk_entry_new();
g_signal_connect(G_OBJECT(entry), "activate",
G_CALLBACK(conv_entry_cb), GINT_TO_POINTER(base));
- g_signal_connect(G_OBJECT(entry), "key_press_event",
- G_CALLBACK(entry_key_press_event_cb), NULL);
- g_signal_connect(G_OBJECT(entry), "insert_text",
+ g_signal_connect(G_OBJECT(entry), "insert-text",
G_CALLBACK(entry_filter), GINT_TO_POINTER(base));
+
gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
-#if 0
- gtk_widget_add_accelerator(entry, "grab_focus", accel_group, accel_key,
- GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);
-#endif /* 0/1 */
gtk_widget_set_hexpand (entry, TRUE);
gtk_grid_attach (GTK_GRID (grid), entry, 1, pos, 1, 1);
- gtk_widget_show(entry);
+ // A11Y - REWRITE IF REQUIRED
+#if 0
if (GTK_IS_ACCESSIBLE (gtk_widget_get_accessible (entry))) {
g_snprintf (str, BUFFER_LEN, "Displays the value at cursor in %s", name+1);
add_atk_namedesc (entry, name+1, str);
add_atk_relation (entry, label, ATK_RELATION_LABELLED_BY);
}
+#endif
return entry;
}
-static GtkWidget *
-create_converter_button(const gchar *name, GtkAccelGroup *accel_group)
-{
- GtkWidget *button;
- GtkWidget *label;
-#if 0
- gint accel_key;
-#endif /* 0/1 */
-
- button = gtk_button_new();
- label = gtk_label_new_with_mnemonic(name);
- gtk_misc_set_alignment(GTK_MISC(label), 0.5, 0.5);
-#if 0
- accel_key = gtk_label_parse_uline(GTK_LABEL(label), name);
-#endif /* 0/1 */
- gtk_container_add(GTK_CONTAINER(button), label);
-#if 0
- gtk_widget_add_accelerator(button, "clicked", accel_group, accel_key,
- GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);
-#endif /* 0/1 */
- gtk_label_set_mnemonic_widget(GTK_LABEL(label), button);
-
- gtk_widget_show(label);
- gtk_widget_show(button);
-
- return button;
-}
-
-/* Made global. We need to access this in ui.c */
-GtkWidget *converter_get = NULL;
-
static void
-close_converter(GtkWidget *dialog, gint response, gpointer user_data)
+close_converter(GtkWidget *dialog, int response_id, gpointer user_data)
{
- ghex_window_sync_converter_item(NULL, FALSE);
- gtk_widget_hide(GTK_WIDGET(dialog));
+ g_signal_emit_by_name (dialog, "close", G_TYPE_NONE);
}
-static gboolean
-converter_delete_event_cb(GtkWidget *widget, GdkEventAny *e, gpointer user_data)
-{
- ghex_window_sync_converter_item(NULL, FALSE);
- gtk_widget_hide(widget);
- return TRUE;
-}
-
-static gboolean
-conv_key_press_cb (GtkWidget *widget, GdkEventKey *e, gpointer user_data)
-{
- if (e->keyval == GDK_KEY_Escape) {
- converter_delete_event_cb(widget, (GdkEventAny *)e, user_data);
- return TRUE;
- }
- return FALSE;
-}
-
-Converter *
-create_converter()
+GtkWidget *create_converter (GtkWindow *parent_win, /* can-NULL */
+ GtkHex *gh)
{
Converter *conv;
GtkWidget *grid;
- GtkAccelGroup *accel_group;
- gint i;
+ GtkWidget *converter_get;
+ int i;
conv = g_new0(Converter, 1);
+ /* set global for usage in other functions. */
+ converter = conv;
+
+ /* set struct's GtkHex widget */
+ g_assert (GTK_IS_HEX(gh));
+ conv->gh = gh;
+
conv->window = gtk_dialog_new_with_buttons(_("Base Converter"),
+ /* don't set modal _now_; will be done by app window */
NULL, 0,
- GTK_STOCK_CLOSE,
- GTK_RESPONSE_OK,
+ _("_Close"),
+ GTK_RESPONSE_CLOSE,
NULL);
+
+ if (parent_win) {
+ g_assert (GTK_IS_WINDOW (parent_win));
+
+ gtk_window_set_transient_for (GTK_WINDOW(conv->window), parent_win);
+ }
+
g_signal_connect(G_OBJECT(conv->window), "response",
G_CALLBACK(close_converter), conv->window);
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), 4);
gtk_grid_set_column_spacing (GTK_GRID (grid), 4);
- gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (conv->window))), grid,
- TRUE, TRUE, 0);
- gtk_container_set_border_width(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(conv->window))),
- 4);
- gtk_box_set_spacing(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(conv->window))), 2);
- gtk_widget_show (grid);
+ gtk_box_append (GTK_BOX(gtk_dialog_get_content_area (GTK_DIALOG(conv->window))),
+ grid);
+ gtk_box_set_spacing(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(conv->window))),
+ 2);
- accel_group = gtk_accel_group_new();
-
/* entries */
conv->entry[0] = create_converter_entry (_("_Binary:"), grid,
- accel_group, 0, 2);
+ 0, 2);
conv->entry[1] = create_converter_entry (_("_Octal:"), grid,
- accel_group, 1, 8);
+ 1, 8);
conv->entry[2] = create_converter_entry (_("_Decimal:"), grid,
- accel_group, 2, 10);
+ 2, 10);
conv->entry[3] = create_converter_entry (_("_Hex:"), grid,
- accel_group, 3, 16);
+ 3, 16);
conv->entry[4] = create_converter_entry (_("_ASCII:"), grid,
- accel_group, 4, 0);
+ 4, 0);
/* get cursor button */
- converter_get = create_converter_button(_("_Get cursor value"), accel_group);
+ converter_get = gtk_button_new_with_mnemonic (_("_Get cursor value"));
- g_signal_connect(G_OBJECT(conv->window), "delete_event",
- G_CALLBACK(converter_delete_event_cb), conv);
g_signal_connect(G_OBJECT(converter_get), "clicked",
G_CALLBACK(get_cursor_val_cb), conv);
- g_signal_connect(G_OBJECT(conv->window), "key_press_event",
- G_CALLBACK(conv_key_press_cb), conv);
- gtk_grid_attach (GTK_GRID (grid), converter_get, 0, 5, 2, 1);
- /* add the accelerators */
- gtk_window_add_accel_group(GTK_WINDOW(conv->window), accel_group);
+ gtk_grid_attach (GTK_GRID (grid), converter_get, 0, 5, 2, 1);
+ // A11Y - REWRITE IF REQUIRED
+#if 0
if (GTK_IS_ACCESSIBLE(gtk_widget_get_accessible(converter_get))) {
add_atk_namedesc (converter_get, _("Get cursor value"), _("Gets the value at cursor in
binary, octal, decimal, hex and ASCII"));
for (i=0; i<5; i++) {
@@ -282,35 +238,34 @@ create_converter()
add_atk_relation (converter_get, conv->entry[i], ATK_RELATION_CONTROLLER_FOR);
}
}
+#endif
- return conv;
+ return conv->window;
}
static void
get_cursor_val_cb(GtkButton *button, Converter *conv)
{
- GtkWidget *view;
guint val, start;
- GHexWindow *win = ghex_window_get_active();
+ guint group_type;
+ HexDocument *doc;
- if(win == NULL || win->gh == NULL)
- return;
+ g_return_if_fail (GTK_IS_HEX(conv->gh));
- view = GTK_WIDGET(win->gh);
+ doc = gtk_hex_get_document (conv->gh);
+ group_type = gtk_hex_get_group_type (conv->gh);
+ start = gtk_hex_get_cursor (conv->gh);
+ start = start - start % group_type;
- if(view) {
- start = gtk_hex_get_cursor(GTK_HEX(view));
- start = start - start % GTK_HEX(view)->group_type;
- val = 0;
- do {
- val <<= 8;
- val |= gtk_hex_get_byte(GTK_HEX(view), start);
- start++;
- } while((start % GTK_HEX(view)->group_type != 0) &&
- (start < GTK_HEX(view)->document->file_size) );
+ val = 0;
+ do {
+ val <<= 8;
+ val |= gtk_hex_get_byte(conv->gh, start);
+ start++;
+ } while((start % group_type != 0) &&
+ (start < doc->file_size) );
- set_values(conv, val);
- }
+ set_values(conv, val);
}
static gchar *
@@ -344,13 +299,13 @@ set_values(Converter *conv, gulong val)
for(i = 0; i < 32; i++)
buffer[i] =((val & (1L << (31 - i)))?'1':'0');
buffer[i] = 0;
- gtk_entry_set_text(GTK_ENTRY(conv->entry[0]), clean(buffer));
+ my_gtk_entry_set_text(GTK_ENTRY(conv->entry[0]), clean(buffer));
g_snprintf(buffer, CONV_BUFFER_LEN, "%o",(unsigned int)val);
- gtk_entry_set_text(GTK_ENTRY(conv->entry[1]), buffer);
+ my_gtk_entry_set_text(GTK_ENTRY(conv->entry[1]), buffer);
g_snprintf(buffer, CONV_BUFFER_LEN, "%lu", val);
- gtk_entry_set_text(GTK_ENTRY(conv->entry[2]), buffer);
+ my_gtk_entry_set_text(GTK_ENTRY(conv->entry[2]), buffer);
for(i = 0, tmp = val; i < nhex; i++) {
buffer[nhex - i - 1] = (tmp & 0x0000000FL);
@@ -361,7 +316,7 @@ set_values(Converter *conv, gulong val)
tmp = tmp >> 4;
}
buffer[i] = '\0';
- gtk_entry_set_text(GTK_ENTRY(conv->entry[3]), buffer);
+ my_gtk_entry_set_text(GTK_ENTRY(conv->entry[3]), buffer);
for(i = 0, tmp = val; i < nbytes; i++) {
buffer[nbytes - i - 1] = tmp & 0x000000FF;
@@ -370,7 +325,7 @@ set_values(Converter *conv, gulong val)
tmp = tmp >> 8;
}
buffer[i] = 0;
- gtk_entry_set_text(GTK_ENTRY(conv->entry[4]), buffer);
+ my_gtk_entry_set_text(GTK_ENTRY(conv->entry[4]), buffer);
}
static void
@@ -379,12 +334,16 @@ conv_entry_cb(GtkEntry *entry, gint base)
gchar buffer[33];
const gchar *text;
gchar *endptr;
- gulong val = converter->value;
+ gulong val;
int i, len;
- text = gtk_entry_get_text(entry);
+ g_return_if_fail (converter);
+
+ /* shorthand. */
+ val = converter->value;
+ text = gtk_entry_buffer_get_text (gtk_entry_get_buffer (entry));
- switch(base) {
+ switch (base) {
case 0:
strncpy(buffer, text, 4);
buffer[4] = 0;
@@ -416,7 +375,8 @@ conv_entry_cb(GtkEntry *entry, gint base)
if(*endptr != 0) {
converter->value = 0;
for(i = 0; i < 5; i++)
- gtk_entry_set_text(GTK_ENTRY(converter->entry[i]), _("ERROR"));
+ my_gtk_entry_set_text(GTK_ENTRY(converter->entry[i]),
+ _("ERROR"));
gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
return;
}
diff --git a/src/converter.h b/src/converter.h
index c0c9a2b2..bd3f0dd7 100644
--- a/src/converter.h
+++ b/src/converter.h
@@ -1,3 +1,5 @@
+/* vim: colorcolumn=80 ts=4 sw=4
+ */
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* gtkhex.h - definition of a GtkHex widget, modified for use with GnomeMDI
@@ -21,28 +23,17 @@
Author: Jaka Mocnik <jaka gnu org>
*/
-#ifndef __GHEX_CONVERTER_H__
-#define __GHEX_CONVERTER_H__
+#ifndef GHEX_CONVERTER_H
+#define GHEX_CONVERTER_H
#include <gtk/gtk.h>
G_BEGIN_DECLS
-typedef struct _Converter {
- GtkWidget *window;
- GtkWidget *entry[5];
- GtkWidget *close;
- GtkWidget *get;
+GtkWidget *create_converter (GtkWindow *parent_win, /* can-NULL */
+ GtkHex *gh);
- gulong value;
-} Converter;
-
-/* Defined in converter.c: used by close_cb and converter_cb */
-extern GtkWidget *converter_get;
-extern Converter *converter;
-
-Converter *create_converter(void);
G_END_DECLS
-#endif /* !__GHEX_CONVERTER_H__ */
+#endif /* GHEX_CONVERTER_H */
diff --git a/src/ghex-application-window.c b/src/ghex-application-window.c
index 2c787531..97aa8794 100644
--- a/src/ghex-application-window.c
+++ b/src/ghex-application-window.c
@@ -1,11 +1,54 @@
/* vim: ts=4 sw=4 colorcolumn=80
*/
-
+#include <glib/gi18n.h>
#include <gtkhex.h>
+
#include "ghex-application-window.h"
#include "hex-dialog.h"
#include "findreplace.h"
#include "chartable.h"
+#include "converter.h"
+
+/* DEFINES */
+
+/* FIXME/TODO - this was an option before. Not sure I see the point in
+ * it. Will consider keeping it hardcoded - but if I do, it might need to
+ * be moved.
+ */
+#define offset_fmt "0x%X"
+
+
+/* GHexNotebookTab GOBJECT DEFINITION */
+
+/* This is just an object internal to the app window widget, so we don't
+ * need to define it publicly in the header.
+ */
+#define GHEX_TYPE_NOTEBOOK_TAB (ghex_notebook_tab_get_type ())
+G_DECLARE_FINAL_TYPE (GHexNotebookTab, ghex_notebook_tab, GHEX, NOTEBOOK_TAB,
+ GtkWidget)
+
+struct _GHexNotebookTab
+{
+ GtkWidget parent_instance;
+
+ GtkWidget *label;
+ GtkWidget *close_btn;
+ GtkHex *gh; /* GtkHex widget activated when tab is clicked */
+};
+
+G_DEFINE_TYPE (GHexNotebookTab, ghex_notebook_tab, GTK_TYPE_WIDGET)
+
+/* GHexNotebookTab - Internal Method Decls */
+
+static GtkWidget * ghex_notebook_tab_new (void);
+static void ghex_notebook_tab_add_hex (GHexNotebookTab *self, GtkHex *gh);
+static const char * ghex_notebook_tab_get_filename (GHexNotebookTab *self);
+
+/* ---- */
+
+/* ----------------------- */
+/* MAIN GOBJECT DEFINITION */
+/* ----------------------- */
struct _GHexApplicationWindow
{
@@ -16,12 +59,14 @@ struct _GHexApplicationWindow
GtkWidget *dialog_widget;
guint statusbar_id;
GtkAdjustment *adj;
+ GList *gh_list;
// TEST - NOT 100% SURE I WANNA GO THIS ROUTE YET.
GtkWidget *find_dialog;
GtkWidget *replace_dialog;
GtkWidget *jump_dialog;
GtkWidget *chartable;
+ GtkWidget *converter;
/*
* for i in `cat ghex-application-window.ui |grep -i 'id=' |sed -e 's,^\s*,,g' |sed -e 's,.*id=",,' |sed -e
's,">,,'`; do echo $i >> tmp.txt; done
@@ -29,65 +74,282 @@ struct _GHexApplicationWindow
/* for i in `cat tmp.txt`; do echo GtkWidget *${i}; done
*/
- GtkWidget *child_box;
+ GtkWidget *hex_notebook;
GtkWidget *conversions_box;
GtkWidget *findreplace_box;
+ GtkWidget *find_button;
GtkWidget *pane_toggle_button;
GtkWidget *insert_mode_button;
GtkWidget *statusbar;
- GtkWidget *scrollbar;
};
+typedef enum
+{
+ PROP_CHARTABLE_OPEN = 1,
+ PROP_CONVERTER_OPEN,
+ N_PROPERTIES
+} GHexApplicationWindowProperty;
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
G_DEFINE_TYPE (GHexApplicationWindow, ghex_application_window,
GTK_TYPE_APPLICATION_WINDOW)
-/* CALLBACKS */
+/* ---- */
+
+
+/* FUNCTION DECLARATIONS */
+
+/* NOTE: see also SET_SHOW_TEMPLATE macro defined below. */
+static void ghex_application_window_set_show_chartable (GHexApplicationWindow *self,
+ gboolean show);
+
+static void ghex_application_window_set_show_converter (GHexApplicationWindow *self,
+ gboolean show);
+
+static void set_statusbar(GHexApplicationWindow *self, const char *str);
+static void update_status_message (GHexApplicationWindow *self);
+
+
+/* PRIVATE FUNCTIONS */
static void
-cursor_moved_cb(GtkHex *gtkhex, gpointer user_data)
+notebook_switch_page_cb (GtkNotebook *notebook,
+ GtkWidget *page,
+ guint page_num,
+ gpointer user_data)
+{
+ GHexApplicationWindow *self =
+ GHEX_APPLICATION_WINDOW(user_data);
+ GHexNotebookTab *tab =
+ GHEX_NOTEBOOK_TAB(gtk_notebook_get_tab_label (notebook, page));
+
+ g_return_if_fail (GHEX_IS_NOTEBOOK_TAB(tab));
+
+ printf("%s: start - tab: %p\n",
+ __func__, tab);
+
+ if (tab->gh != self->gh) {
+ ghex_application_window_set_hex (self, tab->gh);
+ ghex_application_window_activate_tab (self, tab->gh);
+ }
+}
+
+static void
+notebook_page_added_cb (GtkNotebook *notebook,
+ GtkWidget *child,
+ guint page_num,
+ gpointer user_data)
{
GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW(user_data);
- int current_pos;
- HexDialogVal64 val;
+
+ /* Let's play this super dumb. If a page is added, that will generally
+ * mind the Find button should be activated. Change if necessary.
+ */
+ gtk_widget_set_sensitive (self->find_button, TRUE);
+}
- current_pos = gtk_hex_get_cursor(gtkhex);
-// ghex_window_update_status_message(self);
+static void
+notebook_page_removed_cb (GtkNotebook *notebook,
+ GtkWidget *child,
+ guint page_num,
+ gpointer user_data)
+{
+ GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW(user_data);
+
+ g_debug("%s: n_pages: %d",
+ __func__, gtk_notebook_get_n_pages(notebook));
- for (int i = 0; i < 8; i++)
- {
- /* returns 0 on buffer overflow, which is what we want */
- val.v[i] = gtk_hex_get_byte(gtkhex, current_pos+i);
- }
- hex_dialog_updateview (self->dialog, &val);
+ if (gtk_notebook_get_n_pages (notebook) == 0) {
+ gtk_widget_set_sensitive (self->find_button, FALSE);
+ }
}
+/* POSSIBLE TODO - this is fine for a one-off, but should have a more generic
+ * public property getter/setter function if we add more properties here.
+ */
+static void
+chartable_close_cb (GtkWindow *window,
+ gpointer user_data)
+{
+ GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW(user_data);
+ GValue val = G_VALUE_INIT;
+
+ g_value_init (&val, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&val, FALSE);
+
+ g_object_set_property (G_OBJECT(self), "chartable-open", &val);
-/* ACTIONS */
+ g_value_unset (&val);
+}
static void
-show_chartable (GtkWidget *widget,
- const char *action_name,
- GVariant *parameter)
+converter_close_cb (GtkWindow *window,
+ gpointer user_data)
{
- GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW(widget);
+ GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW(user_data);
+ GValue val = G_VALUE_INIT;
+
+ g_value_init (&val, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&val, FALSE);
+
+ g_object_set_property (G_OBJECT(self), "converter-open", &val);
+ g_value_unset (&val);
+}
+
+static void
+setup_chartable (GHexApplicationWindow *self)
+{
g_return_if_fail (GTK_IS_HEX(self->gh));
- g_return_if_fail (GTK_IS_WIDGET(self->find_dialog));
- (void)parameter, (void)action_name; /* unused */
+ self->chartable = create_char_table (GTK_WINDOW(self), self->gh);
+
+ g_signal_connect(G_OBJECT(self->chartable), "close-request",
+ G_CALLBACK(chartable_close_cb), self);
+}
+
+static void
+setup_converter (GHexApplicationWindow *self)
+{
+ g_return_if_fail (GTK_IS_HEX(self->gh));
+
+ self->converter = create_converter (GTK_WINDOW(self), self->gh);
+
+ g_signal_connect(G_OBJECT(self->converter), "close-request",
+ G_CALLBACK(converter_close_cb), self);
+}
+
+static void
+cursor_moved_cb (GtkHex *gh, gpointer user_data)
+{
+ GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW(user_data);
+ int current_pos;
+ HexDialogVal64 val;
+
+ current_pos = gtk_hex_get_cursor (gh);
+ update_status_message (self);
- if (! self->chartable) {
- self->chartable = create_char_table (GTK_WINDOW(self), self->gh);
- gtk_widget_show (self->chartable);
+ for (int i = 0; i < 8; i++)
+ {
+ /* returns 0 on buffer overflow, which is what we want */
+ val.v[i] = gtk_hex_get_byte (gh, current_pos+i);
}
- else if (gtk_widget_is_visible (self->chartable)) {
- gtk_widget_hide (self->chartable);
+ hex_dialog_updateview (self->dialog, &val);
+}
+
+/* ACTIONS */
+
+#define SET_SHOW_TEMPLATE(WIDGET, SETUP_FUNC) \
+static void
\
+ghex_application_window_set_show_ ##WIDGET (GHexApplicationWindow *self, \
+ gboolean show)
\
+{
\
+ g_debug("%s: start - show: %d", __func__, show); \
+
\
+ if (show)
\
+ {
\
+ if (! GTK_IS_WIDGET(self->WIDGET)) {
\
+ SETUP_FUNC;
\
+ }
\
+ gtk_widget_show (self->WIDGET);
\
+ }
\
+ else
\
+ {
\
+ if (gtk_widget_is_visible (self->WIDGET))
\
+ gtk_widget_hide (self->WIDGET);
\
+ }
\
+}
/* SET_SHOW_TEMPLATE */
+
+SET_SHOW_TEMPLATE(chartable, setup_chartable(self))
+SET_SHOW_TEMPLATE(converter, setup_converter(self))
+
+/* convenience helper function to build a GtkHex widget pre-loaded with
+ * a hex document, from a GFile *.
+ */
+static GtkHex *
+new_gh_from_gfile (GFile *file)
+{
+ char *path;
+ GFileInfo *info;
+ const char *name;
+ GError *error = NULL;
+ HexDocument *doc;
+ GtkHex *gh;
+
+ path = g_file_get_path (file);
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+ G_FILE_QUERY_INFO_NONE, // GFileQueryInfoFlags flags
+ NULL, // GCancellable
*cancellable
+ &error);
+ name = g_file_info_get_display_name (info);
+
+ g_debug("%s: path acc. to GFile: %s",
+ __func__, path);
+
+ doc = hex_document_new_from_file (path);
+ gh = GTK_HEX(gtk_hex_new (doc));
+
+ // FIXME - should be based on settings
+ gtk_hex_show_offsets (gh, TRUE);
+
+ if (error) g_error_free (error);
+ g_clear_object (&info);
+ g_clear_object (&file);
+
+ return gh;
+}
+
+static void
+open_response_cb (GtkDialog *dialog,
+ int resp,
+ gpointer user_data)
+{
+ GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW(user_data);
+ GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog);
+ GFile *file;
+ GtkHex *gh;
+
+ if (resp == GTK_RESPONSE_OK)
+ {
+ file = gtk_file_chooser_get_file (chooser);
+ gh = new_gh_from_gfile (file);
+
+ ghex_application_window_add_hex (self, gh);
+ ghex_application_window_set_hex (self, gh);
+ ghex_application_window_activate_tab (self, gh);
}
- else {
- gtk_widget_show (self->chartable);
+ else
+ {
+ g_debug ("%s: User didn't click Open. Bail out.",
+ __func__);
}
+ gtk_window_destroy (GTK_WINDOW(dialog));
}
+static void
+open_file (GtkWidget *widget,
+ const char *action_name,
+ GVariant *parameter)
+{
+ GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW(widget);
+ GtkWidget *file_sel;
+ GtkResponseType resp;
+
+ file_sel = gtk_file_chooser_dialog_new (_("Select a file to open"),
+ GTK_WINDOW(self),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Open"), GTK_RESPONSE_OK,
+ NULL);
+
+ gtk_window_set_modal (GTK_WINDOW(file_sel), TRUE);
+ gtk_widget_show (file_sel);
+
+ g_signal_connect (file_sel, "response",
+ G_CALLBACK(open_response_cb), self);
+}
static void
show_find_pane (GtkWidget *widget,
@@ -174,7 +436,7 @@ toggle_insert_mode (GtkWidget *widget,
(void)parameter, (void)action_name; /* unused */
/* this tests whether the button is pressed AFTER its state has changed. */
- if (gtk_toggle_button_get_active (self->insert_mode_button)) {
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(self->insert_mode_button))) {
g_debug("%s: TOGGLING INSERT MODE", __func__);
gtk_hex_set_insert_mode(self->gh, TRUE);
} else {
@@ -185,7 +447,6 @@ toggle_insert_mode (GtkWidget *widget,
/* --- */
-// DUMB TEST
static void
set_statusbar(GHexApplicationWindow *self, const char *str)
{
@@ -193,11 +454,260 @@ set_statusbar(GHexApplicationWindow *self, const char *str)
gtk_statusbar_get_context_id (GTK_STATUSBAR(self->statusbar),
"status");
+ gtk_statusbar_pop (GTK_STATUSBAR(self->statusbar), id);
gtk_statusbar_push (GTK_STATUSBAR(self->statusbar), id, str);
}
static void
-ghex_application_window_init(GHexApplicationWindow *self)
+clear_statusbar (GHexApplicationWindow *self)
+{
+ set_statusbar (self, " ");
+}
+
+/* FIXME: This is one of the few functions lifted from the old 'ghex-window.c'.
+ * I don't care for the pragmas, etc., so this could be a good candidate for a
+ * possible future rewrite.
+ */
+#define FMT_LEN 128
+#define STATUS_LEN 128
+
+static void
+update_status_message (GHexApplicationWindow *self)
+{
+ gchar fmt[FMT_LEN], status[STATUS_LEN];
+ gint current_pos;
+ gint ss, se, len;
+
+ if (! GTK_IS_HEX(self->gh)) {
+ clear_statusbar (self);
+ return;
+ }
+
+#if defined(__GNUC__) && (__GNUC__ > 4)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+#endif
+ current_pos = gtk_hex_get_cursor(self->gh);
+ if (g_snprintf(fmt, FMT_LEN, _("Offset: %s"), offset_fmt) < FMT_LEN) {
+ g_snprintf(status, STATUS_LEN, fmt, current_pos);
+ if (gtk_hex_get_selection(self->gh, &ss, &se)) {
+ if (g_snprintf(fmt, FMT_LEN, _("; %s bytes from %s to %s selected"),
+ offset_fmt, offset_fmt, offset_fmt) < FMT_LEN) {
+ len = strlen(status);
+ if (len < STATUS_LEN) {
+ /* Variables 'ss' and 'se' denotes the offsets of the first
+ * and the last bytes that are part of the selection. */
+ g_snprintf(status + len, STATUS_LEN - len, fmt, se - ss +
+ 1, ss, se);
+ }
+ }
+ }
+#if defined(__GNUC__) && (__GNUC__ > 4)
+#pragma GCC diagnostic pop
+#endif
+ set_statusbar(self, status);
+ }
+ else
+ clear_statusbar (self);
+}
+#undef FMT_LEN
+#undef STATUS_LEN
+
+
+/* PROPERTIES */
+
+static void
+ghex_application_window_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW (object);
+
+ switch ((GHexApplicationWindowProperty) property_id)
+ {
+ case PROP_CHARTABLE_OPEN:
+ ghex_application_window_set_show_chartable (self,
+ g_value_get_boolean (value));
+ break;
+
+ case PROP_CONVERTER_OPEN:
+ ghex_application_window_set_show_converter (self,
+ g_value_get_boolean (value));
+ break;
+
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+ghex_application_window_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW (object);
+
+ switch ((GHexApplicationWindowProperty) property_id)
+ {
+ case PROP_CHARTABLE_OPEN:
+ g_value_set_boolean (value,
+ GTK_IS_WIDGET(self->chartable) &&
+ gtk_widget_get_visible (self->chartable));
+ break;
+
+ case PROP_CONVERTER_OPEN:
+ g_value_set_boolean (value,
+ GTK_IS_WIDGET(self->converter) &&
+ gtk_widget_get_visible (self->converter));
+ break;
+
+ default:
+ /* We don't have any other property... */
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+/* GHexNotebookTab -- CALLBACKS */
+
+static void
+dumb_click_cb (GtkButton *button,
+ gpointer user_data)
+{
+ g_debug("%s: clicked btn: %p",
+ __func__, (void *)button);
+}
+
+
+/* GHexNotebookTab -- CONSTRUCTORS AND DESTRUCTORS */
+
+static void
+ghex_notebook_tab_init (GHexNotebookTab *self)
+{
+ GtkWidget *widget = GTK_WIDGET (self);
+ GtkLayoutManager *layout_manager;
+
+ /* Set spacing between label and close button. */
+
+ layout_manager = gtk_widget_get_layout_manager (widget);
+ gtk_box_layout_set_spacing (GTK_BOX_LAYOUT(layout_manager), 12);
+
+ /* Set up our label to hold the document name and the close button. */
+
+ self->label = gtk_label_new (_("Untitled document"));
+ self->close_btn = gtk_button_new ();
+
+ gtk_widget_set_halign (self->close_btn, GTK_ALIGN_END);
+ gtk_button_set_icon_name (GTK_BUTTON(self->close_btn),
+ "window-close-symbolic");
+ gtk_button_set_has_frame (GTK_BUTTON(self->close_btn), FALSE);
+
+ gtk_widget_set_parent (self->label, widget);
+ gtk_widget_set_parent (self->close_btn, widget);
+
+ /* SIGNALS */
+ /* Cross-reference: notebook_switch_page_cb which we can't set here,
+ * because this only pertains to the label of the tab and not the
+ * tab as a whole.
+ */
+ g_signal_connect(self->close_btn, "clicked",
+ G_CALLBACK(dumb_click_cb), self);
+}
+
+static void
+ghex_notebook_tab_dispose (GObject *object)
+{
+ GHexNotebookTab *self = GHEX_NOTEBOOK_TAB(object);
+
+ /* Boilerplate: chain up
+ */
+ G_OBJECT_CLASS(ghex_notebook_tab_parent_class)->dispose(object);
+}
+
+static void
+ghex_notebook_tab_finalize (GObject *gobject)
+{
+ /* here, you would free stuff. I've got nuthin' for ya. */
+
+ /* --- */
+
+ /* Boilerplate: chain up
+ */
+ G_OBJECT_CLASS(ghex_notebook_tab_parent_class)->finalize(gobject);
+}
+
+static void
+ghex_notebook_tab_class_init (GHexNotebookTabClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = ghex_notebook_tab_dispose;
+ object_class->finalize = ghex_notebook_tab_finalize;
+
+ /* Layout manager: box-style layout. */
+ gtk_widget_class_set_layout_manager_type (widget_class,
+ GTK_TYPE_BOX_LAYOUT);
+}
+
+/* GHexNotebookTab - Internal Methods */
+
+static GtkWidget *
+ghex_notebook_tab_new (void)
+{
+ return g_object_new (GHEX_TYPE_NOTEBOOK_TAB,
+ /* no properties to set */
+ NULL);
+}
+
+static void
+ghex_notebook_tab_add_hex (GHexNotebookTab *self, GtkHex *gh)
+{
+ HexDocument *doc;
+ char *basename;
+
+ /* Do some sanity checks, as this method requires that some ducks be in
+ * a row -- we need a valid GtkHex that is pre-loaded with a valid
+ * HexDocument.
+ */
+ g_return_if_fail (GHEX_IS_NOTEBOOK_TAB (self));
+ g_return_if_fail (GTK_IS_HEX (gh));
+
+ doc = gtk_hex_get_document (gh);
+ g_return_if_fail (HEX_IS_DOCUMENT (doc));
+
+ /* Associate this notebook tab with a GtkHex widget. */
+ self->gh = gh;
+
+ /* Set name of tab. */
+ basename = g_path_get_basename (doc->file_name);
+
+ gtk_label_set_text (GTK_LABEL(self->label), basename);
+
+ g_free (basename);
+}
+
+static const char *
+ghex_notebook_tab_get_filename (GHexNotebookTab *self)
+{
+ g_return_val_if_fail (GTK_IS_LABEL (GTK_LABEL(self->label)),
+ NULL);
+
+ return gtk_label_get_text (GTK_LABEL(self->label));
+}
+
+
+/* ---- */
+
+
+/* GHexApplicationWindow -- CONSTRUCTORS AND DESTRUCTORS */
+
+static void
+ghex_application_window_init (GHexApplicationWindow *self)
{
GtkWidget *widget = GTK_WIDGET(self);
GtkStyleContext *context;
@@ -226,6 +736,17 @@ ghex_application_window_init(GHexApplicationWindow *self)
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+ /* Setup notebook */
+
+ g_signal_connect (self->hex_notebook, "switch-page",
+ G_CALLBACK(notebook_switch_page_cb), self);
+
+ g_signal_connect (self->hex_notebook, "page-added",
+ G_CALLBACK(notebook_page_added_cb), self);
+
+ g_signal_connect (self->hex_notebook, "page-removed",
+ G_CALLBACK(notebook_page_removed_cb), self);
+
/* Get find_dialog and friends geared up */
self->find_dialog = find_dialog_new ();
@@ -243,8 +764,10 @@ ghex_application_window_init(GHexApplicationWindow *self)
gtk_box_append (GTK_BOX(self->findreplace_box), self->jump_dialog);
gtk_widget_hide (self->jump_dialog);
- // TEST
- set_statusbar(self, "Offset: 0x0");
+ clear_statusbar (self);
+
+ /* Grey out Find button at the beginning */
+ gtk_widget_set_sensitive (self->find_button, FALSE);
}
static void
@@ -275,16 +798,35 @@ ghex_application_window_class_init(GHexApplicationWindowClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS(klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
- /* <boilerplate> */
object_class->dispose = ghex_application_window_dispose;
object_class->finalize = ghex_application_window_finalize;
- /* </boilerplate> */
+ object_class->get_property = ghex_application_window_get_property;
+ object_class->set_property = ghex_application_window_set_property;
- gtk_widget_class_set_template_from_resource (widget_class,
- "/org/gnome/ghex/ghex-application-window.ui");
+ /* PROPERTIES */
+
+ properties[PROP_CHARTABLE_OPEN] =
+ g_param_spec_boolean ("chartable-open",
+ "Character table open",
+ "Whether the character table dialog is currently open",
+ FALSE, // gboolean default_value
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_CONVERTER_OPEN] =
+ g_param_spec_boolean ("converter-open",
+ "Base converter open",
+ "Whether the base converter dialog is currently open",
+ FALSE, // gboolean default_value
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPERTIES, properties);
/* ACTIONS */
+ gtk_widget_class_install_action (widget_class, "ghex.open",
+ NULL, // GVariant string param_type
+ open_file);
+
gtk_widget_class_install_action (widget_class, "ghex.show-conversions",
NULL, // GVariant string param_type
toggle_conversions);
@@ -305,55 +847,113 @@ ghex_application_window_class_init(GHexApplicationWindowClass *klass)
NULL, // GVariant string param_type
show_jump_pane);
- gtk_widget_class_install_action (widget_class, "ghex.chartable",
- NULL, // GVariant string param_type
- show_chartable);
+ gtk_widget_class_install_property_action (widget_class,
+ "ghex.chartable", "chartable-open");
+
+ gtk_widget_class_install_property_action (widget_class,
+ "ghex.converter", "converter-open");
+
+ /* WIDGET TEMPLATE .UI */
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/org/gnome/ghex/ghex-application-window.ui");
/*
* for i in `cat tmp.txt`; do echo "gtk_widget_class_bind_template_child (widget_class,
GHexApplicationWindow, ${i});"; done
*/
gtk_widget_class_bind_template_child (widget_class, GHexApplicationWindow,
- child_box);
+ hex_notebook);
gtk_widget_class_bind_template_child (widget_class, GHexApplicationWindow,
conversions_box);
gtk_widget_class_bind_template_child (widget_class, GHexApplicationWindow,
findreplace_box);
+ gtk_widget_class_bind_template_child (widget_class, GHexApplicationWindow,
+ find_button);
gtk_widget_class_bind_template_child (widget_class, GHexApplicationWindow,
pane_toggle_button);
gtk_widget_class_bind_template_child (widget_class, GHexApplicationWindow,
insert_mode_button);
gtk_widget_class_bind_template_child (widget_class, GHexApplicationWindow,
statusbar);
- gtk_widget_class_bind_template_child (widget_class, GHexApplicationWindow,
- scrollbar);
-
-
}
GtkWidget *
-ghex_application_window_new(void)
+ghex_application_window_new (GtkApplication *app)
{
- return g_object_new(GHEX_TYPE_APPLICATION_WINDOW, NULL);
+ return g_object_new (GHEX_TYPE_APPLICATION_WINDOW,
+ "application", app,
+ NULL);
+}
+
+void
+ghex_application_window_activate_tab (GHexApplicationWindow *self,
+ GtkHex *gh)
+{
+ GtkNotebook *notebook = GTK_NOTEBOOK(self->hex_notebook);
+ int page_num;
+
+ g_return_if_fail (GTK_IS_HEX (gh));
+ g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
+
+ page_num = gtk_notebook_page_num (notebook, GTK_WIDGET(gh));
+ g_debug ("%s: got page_num: %d - setting notebook to that page.",
+ __func__, page_num);
+
+ gtk_notebook_set_current_page (notebook, page_num);
}
void
-ghex_application_window_add_hex(GHexApplicationWindow *self, GtkHex *gh)
+ghex_application_window_set_hex (GHexApplicationWindow *self,
+ GtkHex *gh)
{
+ HexDocument *doc = gtk_hex_get_document (gh);
+
g_return_if_fail (GTK_IS_HEX(gh));
+ g_return_if_fail (HEX_IS_DOCUMENT(doc));
+
+ g_debug ("%s: Setting active gh to: %p",
+ __func__, (void *)gh);
self->gh = gh;
+}
- gtk_box_prepend (GTK_BOX(self->child_box), GTK_WIDGET(gh));
+void
+ghex_application_window_add_hex (GHexApplicationWindow *self,
+ GtkHex *gh)
+{
+ GtkWidget *tab;
+ HexDocument *doc = gtk_hex_get_document (gh);
+
+ g_return_if_fail (GTK_IS_HEX(gh));
+ g_return_if_fail (HEX_IS_DOCUMENT(doc));
+
+ /* Add this GtkHex to our internal list */
+ self->gh_list = g_list_append (self->gh_list, gh);
+
+ /* Set this GtkHex as the current viewed gh if there is no currently
+ * open document */
+ if (! self->gh)
+ ghex_application_window_set_hex (self, gh);
+
+ /* Generate a tab */
+ tab = ghex_notebook_tab_new ();
+ printf ("%s: CREATED TAB -- %p\n", __func__, (void *)tab);
+ ghex_notebook_tab_add_hex (GHEX_NOTEBOOK_TAB(tab), gh);
+
+ gtk_notebook_append_page (GTK_NOTEBOOK(self->hex_notebook),
+ GTK_WIDGET(gh),
+ tab);
+
+ /* set text of context menu tab switcher to the filename rather than
+ * 'Page X' */
+ gtk_notebook_set_menu_label_text (GTK_NOTEBOOK(self->hex_notebook),
+ GTK_WIDGET(gh),
+ ghex_notebook_tab_get_filename (GHEX_NOTEBOOK_TAB(tab)));
/* Setup signals */
g_signal_connect(G_OBJECT(gh), "cursor-moved",
G_CALLBACK(cursor_moved_cb), self);
- /* Setup scrollbar */
- self->adj = gtk_hex_get_adjustment(gh);
- gtk_scrollbar_set_adjustment (GTK_SCROLLBAR(self->scrollbar), self->adj);
-
/* Setup find_dialog & friends. */
find_dialog_set_hex (FIND_DIALOG(self->find_dialog), self->gh);
diff --git a/src/ghex-application-window.h b/src/ghex-application-window.h
index 4c8ce4d2..66272a9f 100644
--- a/src/ghex-application-window.h
+++ b/src/ghex-application-window.h
@@ -11,8 +11,12 @@ G_DECLARE_FINAL_TYPE (GHexApplicationWindow, ghex_application_window,
GHEX, APPLICATION_WINDOW,
GtkApplicationWindow)
-GtkWidget * ghex_application_window_new(void);
-void ghex_application_window_add_hex(GHexApplicationWindow *self,
+GtkWidget * ghex_application_window_new (GtkApplication *app);
+void ghex_application_window_add_hex (GHexApplicationWindow *self,
+ GtkHex *gh);
+void ghex_application_window_set_hex (GHexApplicationWindow *self,
+ GtkHex *gh);
+void ghex_application_window_activate_tab (GHexApplicationWindow *self,
GtkHex *gh);
#endif
diff --git a/src/ghex-application-window.ui b/src/ghex-application-window.ui
index 5eed23ad..7f3952a1 100644
--- a/src/ghex-application-window.ui
+++ b/src/ghex-application-window.ui
@@ -52,7 +52,7 @@
</item>
<item>
<attribute name="label" translatable="yes">_Base Converter</attribute>
- <attribute name="action">ghex.base-converter</attribute>
+ <attribute name="action">ghex.converter</attribute>
</item>
</submenu>
@@ -121,7 +121,7 @@
</child>
<child type="end">
- <object class="GtkButton">
+ <object class="GtkButton" id="find_button">
<property name="valign">center</property>
<property name="icon-name">edit-find-symbolic</property>
<property name="action-name">ghex.find</property>
@@ -143,16 +143,19 @@
<child> <!-- main vert gtkbox -->
<object class="GtkBox"> <!-- main vert gtkbox -->
<property name="orientation">vertical</property>
- <child> <!-- child_box for gtkhex widget and scrollbar -->
+
+ <child> <!-- child_box for notebook -->
<object class="GtkBox" id="child_box">
<property name="orientation">horizontal</property>
<property name="homogeneous">false</property>
- <child>
- <object class="GtkScrollbar" id="scrollbar">
- <property
name="orientation">vertical</property>
- <property name="hexpand">false</property>
- <property name="halign">end</property>
- </object>
+
+ <child> <!-- hex_notebook - tabs for GtkHex widget -->
+ <object class="GtkNotebook" id="hex_notebook">
+ <property name="enable-popup">true</property>
+ <property name="group-name">hex</property>
+ <property name="scrollable">true</property>
+ <property name="vexpand">true</property>
+ </object>
</child>
</object> <!-- child_box -->
</child> <!-- child_box -->
diff --git a/src/gtkhex.c b/src/gtkhex.c
index 3ee3cab3..c8c018d5 100644
--- a/src/gtkhex.c
+++ b/src/gtkhex.c
@@ -141,6 +141,7 @@ struct _GtkHex
GtkWidget *xdisp, *adisp; /* DrawingArea */
GtkWidget *offsets; /* DrawingArea */
+ GtkWidget *scrollbar;
PangoLayout *xlayout, *alayout, *olayout;
@@ -2816,7 +2817,7 @@ static void gtk_hex_document_changed(HexDocument* doc, gpointer change_data,
static void
-gtk_hex_class_init(GtkHexClass *klass)
+gtk_hex_class_init (GtkHexClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
@@ -3009,10 +3010,6 @@ gtk_hex_init(GtkHex *gh)
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
- /* Initialize Adjustment */
-
- gh->adj = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
-
/* Setup offsets widget. */
gh->offsets = gtk_drawing_area_new();
@@ -3044,7 +3041,7 @@ gtk_hex_init(GtkHex *gh)
/* Setup our Hex drawing area. */
gh->xdisp = gtk_drawing_area_new();
- gtk_widget_set_parent (gh->xdisp, GTK_WIDGET (gh));
+ gtk_widget_set_parent (gh->xdisp, widget);
gtk_widget_set_hexpand (gh->xdisp, TRUE);
/* Create the pango layout for the widget */
@@ -3088,7 +3085,7 @@ gtk_hex_init(GtkHex *gh)
/* Setup our ASCII widget. */
gh->adisp = gtk_drawing_area_new();
- gtk_widget_set_parent (gh->adisp, GTK_WIDGET (gh));
+ gtk_widget_set_parent (gh->adisp, widget);
gtk_widget_set_halign (gh->adisp, GTK_ALIGN_START);
gtk_widget_set_hexpand (gh->adisp, FALSE);
gtk_widget_set_name (gh->adisp, "asciidisplay");
@@ -3135,6 +3132,15 @@ gtk_hex_init(GtkHex *gh)
DEFAULT_DA_SIZE, DEFAULT_DA_SIZE);
+ /* Initialize Adjustment */
+
+ gh->adj = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+
+ /* Setup scrollbar. */
+ gh->scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL,
+ gh->adj);
+ gtk_widget_set_parent (gh->scrollbar, widget);
+
/* Connect gestures to ascii/hex drawing areas.
*/
@@ -3517,7 +3523,7 @@ void gtk_hex_set_geometry(GtkHex *gh, gint cpl, gint vis_lines)
GtkAdjustment *
gtk_hex_get_adjustment(GtkHex *gh)
{
- g_return_if_fail (GTK_IS_ADJUSTMENT(gh->adj));
+ g_return_val_if_fail (GTK_IS_ADJUSTMENT(gh->adj), NULL);
return gh->adj;
}
@@ -3537,3 +3543,11 @@ gtk_hex_get_insert_mode (GtkHex *gh)
return gh->insert;
}
+
+guint
+gtk_hex_get_group_type (GtkHex *gh)
+{
+ g_assert (GTK_IS_HEX (gh));
+
+ return gh->group_type;
+}
diff --git a/src/gtkhex.h b/src/gtkhex.h
index 283a08dc..bfd77bca 100644
--- a/src/gtkhex.h
+++ b/src/gtkhex.h
@@ -63,6 +63,7 @@ guint gtk_hex_get_cursor(GtkHex *);
guchar gtk_hex_get_byte(GtkHex *, guint);
void gtk_hex_set_group_type(GtkHex *, guint);
+guint gtk_hex_get_group_type (GtkHex *gh);
void gtk_hex_set_starting_offset(GtkHex *, gint);
void gtk_hex_show_offsets(GtkHex *, gboolean);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]