[ghex/gtk4-port: 35/91] Implement tabbed viewing and file opening.




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]