[ghex/gtk4-port: 47/91] Implement: Dark mode, undo, redo




commit 704a6bd3aab2079d302698a86828afdee30769a1
Author: Logan Rathbone <poprocks gmail com>
Date:   Fri Jan 22 00:08:56 2021 -0500

    Implement: Dark mode, undo, redo

 data/org.gnome.GHex.gschema.xml.in |  10 +++-
 src/Makefile                       |   2 +-
 src/common-ui.c                    | 100 +++++++++++++++++++-------------
 src/common-ui.h                    |   6 +-
 src/configuration.c                |  27 +++++++++
 src/configuration.h                |  29 +++++++---
 src/ghex-application-window.c      |  81 +++++++++++++++++++++++---
 src/gtkhex.c                       | 113 +++++++++++++++++++++++++++++++++++--
 src/hex-document.c                 |  25 ++++++++
 src/hex-document.h                 |  34 +++++------
 src/preferences.c                  |  82 ++++++++++++++++++++++++++-
 src/preferences.ui                 |  27 +++++++++
 src/print.c                        |  70 ++++++++++++-----------
 src/print.h                        |  43 +++++++-------
 14 files changed, 513 insertions(+), 136 deletions(-)
---
diff --git a/data/org.gnome.GHex.gschema.xml.in b/data/org.gnome.GHex.gschema.xml.in
index 32f8a54d..79dc2b87 100644
--- a/data/org.gnome.GHex.gschema.xml.in
+++ b/data/org.gnome.GHex.gschema.xml.in
@@ -10,6 +10,11 @@
                <value nick="words" value="2"/>
                <value nick="longwords" value="4"/>
        </enum>
+       <enum id="org.gnome.GHex.DarkMode">
+               <value nick="off" value="0"/>
+               <value nick="on" value="1"/>
+               <value nick="system" value="2"/>
+       </enum>
 
        <schema id="org.gnome.GHex" path="/org/gnome/ghex/">
                <key name="font" type="s">
@@ -25,12 +30,15 @@
                        <default>'Sans 12'</default>
                </key>
                <key name="print-shaded-rows" type="u">
-                       <range min="0" max=@XML_SHADED_BOX_MAX@>
+                       <range min="0" max=@XML_SHADED_BOX_MAX@/>
                        <default>0</default>
                </key>
                <key name="show-offsets" type="b">
                        <default>false</default>
                </key>
+               <key name="dark-mode" enum="org.gnome.GHex.DarkMode">
+                       <default>'system'</default>
+               </key>
        </schema>
 
 </schemalist>
diff --git a/src/Makefile b/src/Makefile
index b383dc7c..9a1a6bed 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -12,7 +12,7 @@ CFLAGS=-Wall -Wextra -Werror=implicit -std=c11 -pedantic \
 
 .PHONY: clean compile-resources
 
-STUB: gtkhex.o hex-document.o ghex-application-window.o hex-dialog.o findreplace.o chartable.o converter.o 
resources.o configuration.o preferences.o common-ui.o
+STUB: gtkhex.o hex-document.o ghex-application-window.o hex-dialog.o findreplace.o chartable.o converter.o 
resources.o configuration.o preferences.o common-ui.o print.o
 
 compile-resources:
        glib-compile-resources ghex.gresource.xml --target=resources.c --generate-source
diff --git a/src/common-ui.c b/src/common-ui.c
index 3ce691d4..40ccaa41 100644
--- a/src/common-ui.c
+++ b/src/common-ui.c
@@ -33,6 +33,7 @@
 #if 0
 static void ghex_print(GtkHex *gh, gboolean preview);
 
+
 guint group_type[3] = {
        GROUP_BYTE,
        GROUP_WORD,
@@ -252,7 +253,7 @@ common_about_cb (GtkWindow *parent)
 
        /* Translators: these two strings here indicate the copyright time span,
           e.g. 1998-2018. */
-       copyright = g_strdup_printf (_("Copyright © %Id–%Id The GHex authors"),
+       copyright = g_strdup_printf (_("Copyright © %d–%d The GHex authors"),
                        1998, 2021);
 
        gtk_show_about_dialog (parent,
@@ -821,6 +822,7 @@ revert_cb (GtkAction *action,
                gtk_widget_destroy (mbox);
        }
 }
+#endif
 
 /**
  * ghex_print
@@ -829,75 +831,93 @@ to display the print dialog.
  *
  * Prints or previews the current document.
  **/
-static void
-ghex_print(GtkHex *gh, gboolean preview)
+void
+common_print (GtkWindow *parent, GtkHex *gh, gboolean preview)
 {
-       GHexPrintJobInfo *pji;
+       GHexPrintJobInfo *job;
        HexDocument *doc;
        GtkPrintOperationResult result;
        GError *error = NULL;
-       gchar *basename;
-       gchar *gtk_file_name;
+       char *basename;
+       char *gtk_file_name;
 
-       doc = gh->document;
+       g_return_if_fail (GTK_IS_HEX (gh));
+
+       doc = gtk_hex_get_document (gh);
+       g_return_if_fail (HEX_IS_DOCUMENT (doc));
 
        gtk_file_name = g_filename_to_utf8 (doc->file_name, -1, NULL, NULL, NULL);
        basename = g_filename_display_basename (gtk_file_name);
 
-       pji = ghex_print_job_info_new(doc, gh->group_type);
-       pji->master = gtk_print_operation_new ();
-       pji->config = gtk_print_settings_new ();
-       gtk_print_settings_set (pji->config, GTK_PRINT_SETTINGS_OUTPUT_BASENAME, basename);
-       gtk_print_settings_set_paper_size (pji->config, gtk_paper_size_new (GTK_PAPER_NAME_A4));
-       gtk_print_operation_set_unit (pji->master, GTK_UNIT_POINTS);
-       gtk_print_operation_set_print_settings (pji->master, pji->config);
-       gtk_print_operation_set_embed_page_setup (pji->master, TRUE);
-       gtk_print_operation_set_show_progress (pji->master, TRUE);
-       g_signal_connect (pji->master, "draw-page",
-                         G_CALLBACK (print_page), pji);
-       g_signal_connect (pji->master, "begin-print",
-                         G_CALLBACK (begin_print), pji);
-
-       if (!pji)
+       job = ghex_print_job_info_new (doc, gtk_hex_get_group_type (gh));
+       job->master = gtk_print_operation_new ();
+       job->config = gtk_print_settings_new ();
+       gtk_print_settings_set (job->config, GTK_PRINT_SETTINGS_OUTPUT_BASENAME,
+                       basename);
+       gtk_print_settings_set_paper_size (job->config,
+                       gtk_paper_size_new (NULL));     /* system default */
+       gtk_print_operation_set_unit (job->master, GTK_UNIT_POINTS);
+       gtk_print_operation_set_print_settings (job->master, job->config);
+       gtk_print_operation_set_embed_page_setup (job->master, TRUE);
+       gtk_print_operation_set_show_progress (job->master, TRUE);
+       g_signal_connect (job->master, "draw-page",
+                         G_CALLBACK (print_page), job);
+       g_signal_connect (job->master, "begin-print",
+                         G_CALLBACK (begin_print), job);
+
+       if (!job)
                return;
 
-       pji->preview = preview;
+       job->preview = preview;
 
-       if (!pji->preview)
-               result = gtk_print_operation_run (pji->master, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, NULL, 
&error);
-       else
-               result = gtk_print_operation_run (pji->master, GTK_PRINT_OPERATION_ACTION_PREVIEW, NULL, 
&error);
+       result = gtk_print_operation_run (job->master,
+                       (job->preview ? GTK_PRINT_OPERATION_ACTION_PREVIEW :
+                                                       GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG),
+                       parent,
+                       &error);
 
        if (result == GTK_PRINT_OPERATION_RESULT_ERROR) {
-               g_print ("%s\n", error->message);
+               char *tmp;
+
+               /* Translators: This is an error string for a print-related error
+                * dialog.  The %s is the error generated by GError. */
+               tmp = g_strdup_printf (_("An error has occurred: %s"),
+                                       error->message);
+
+               display_error_dialog (parent, tmp);
+               
+               g_free (tmp);
                g_error_free (error);
        }
-       ghex_print_job_info_destroy (pji);
+       ghex_print_job_info_destroy (job);
        g_free (basename);
        g_free (gtk_file_name);
 }
 
 void
-display_error_dialog (GHexWindow *win, const gchar *msg)
+display_error_dialog (GtkWindow *parent, const char *msg)
 {
        GtkWidget *error_dlg;
 
-       g_return_if_fail (win != NULL);
-       g_return_if_fail (msg != NULL);
-       error_dlg = gtk_message_dialog_new (
-                       GTK_WINDOW (win),
+       g_return_if_fail (GTK_IS_WINDOW(parent));
+       g_return_if_fail (msg);
+
+       error_dlg = gtk_message_dialog_new (parent,
                        GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                        GTK_MESSAGE_ERROR,
-                       GTK_BUTTONS_OK,
-                       "%s",
-                       msg);
+                       GTK_BUTTONS_CLOSE,
+                       "%s", msg);
 
-       gtk_dialog_set_default_response (GTK_DIALOG (error_dlg), GTK_RESPONSE_OK);
+       gtk_dialog_set_default_response (GTK_DIALOG (error_dlg),
+                       GTK_RESPONSE_CLOSE);
        gtk_window_set_resizable (GTK_WINDOW (error_dlg), FALSE);
-       gtk_dialog_run (GTK_DIALOG (error_dlg));
-       gtk_widget_destroy (error_dlg);
+       gtk_widget_show (error_dlg);
+
+       g_signal_connect (error_dlg, "response",
+                       G_CALLBACK (gtk_window_destroy), NULL);
 }
 
+#if 0
 void
 display_info_dialog (GHexWindow *win, const gchar *msg, ...)
 {
diff --git a/src/common-ui.h b/src/common-ui.h
index d90c6c5c..40ae4728 100644
--- a/src/common-ui.h
+++ b/src/common-ui.h
@@ -3,7 +3,7 @@
 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 /* common-ui.h - Common UI utility functions
 
-   Copyright (C) 2004 Free Software Foundation
+   Copyright © 2004 Free Software Foundation
    Copyright © 2021 Logan Rathbone
 
    GHex is free software; you can redistribute it and/or
@@ -29,8 +29,10 @@
 
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
+#include <gtkhex.h>
 
 #include "configuration.h"
+#include "print.h"
 
 G_BEGIN_DECLS
 
@@ -38,6 +40,8 @@ G_BEGIN_DECLS
 
 void common_help_cb (GtkWindow *parent);
 void common_about_cb (GtkWindow *parent);
+void common_print (GtkWindow *parent, GtkHex *gh, gboolean preview);
+void display_error_dialog (GtkWindow *parent, const char *msg);
 
 G_END_DECLS
 
diff --git a/src/configuration.c b/src/configuration.c
index c747695b..91b4d931 100644
--- a/src/configuration.c
+++ b/src/configuration.c
@@ -37,6 +37,10 @@ char *header_font_name;
 char *data_font_name;
 guint shaded_box_size;
 gboolean show_offsets_column;
+int def_dark_mode;
+/* Will default to false here. We can't set it until we get a 'screen', so
+ * we'll save calling get_sys_default_is_dark() until GHex launches. */
+gboolean sys_default_is_dark;
 
 static void
 offsets_column_changed_cb (GSettings   *settings,
@@ -56,6 +60,25 @@ group_changed_cb (GSettings   *settings,
     def_group_type = g_settings_get_enum (settings, key);
 }
 
+static void
+dark_mode_changed_cb (GSettings   *settings,
+                  const gchar *key,
+                  gpointer     user_data)
+{
+    def_dark_mode = g_settings_get_enum (settings, key);
+}
+
+void
+get_sys_default_is_dark (void)
+{
+       GtkSettings *gtk_settings;
+
+       gtk_settings = gtk_settings_get_default ();
+       g_object_get (gtk_settings,
+                       "gtk-application-prefer-dark-theme", &sys_default_is_dark,
+                       NULL);
+}
+
 static void
 box_size_changed_cb (GSettings   *settings,
                      const gchar *key,
@@ -113,6 +136,10 @@ void ghex_init_configuration ()
                       G_CALLBACK (group_changed_cb), NULL);
     group_changed_cb (settings, GHEX_PREF_GROUP, NULL);
 
+    g_signal_connect (settings, "changed::" GHEX_PREF_DARK_MODE,
+                      G_CALLBACK (dark_mode_changed_cb), NULL);
+    dark_mode_changed_cb (settings, GHEX_PREF_DARK_MODE, NULL);
+
     g_signal_connect (settings, "changed::" GHEX_PREF_BOX_SIZE,
                       G_CALLBACK (box_size_changed_cb), NULL);
     box_size_changed_cb (settings, GHEX_PREF_BOX_SIZE, NULL);
diff --git a/src/configuration.h b/src/configuration.h
index 46847001..fba191f2 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -4,6 +4,7 @@
 /* configuration.h - constants and declarations for GSettings
 
    Copyright (C) 1998 - 2004 Free Software Foundation
+   Copyright © 2021 Logan Rathbone
 
    GHex is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -20,7 +21,7 @@
    If not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
-   Author: Jaka Mocnik <jaka gnu org>
+   Original Author: Jaka Mocnik <jaka gnu org>
 */
 
 #ifndef GHEX_CONFIGURATION_H
@@ -31,12 +32,19 @@
 G_BEGIN_DECLS
 
 /* GSettings keys */
-#define GHEX_PREF_FONT               "font"
-#define GHEX_PREF_GROUP              "group-data-by"
-#define GHEX_PREF_DATA_FONT          "print-font-data"
-#define GHEX_PREF_HEADER_FONT        "print-font-header"
-#define GHEX_PREF_BOX_SIZE           "print-shaded-rows"
-#define GHEX_PREF_OFFSETS_COLUMN     "show-offsets"
+#define GHEX_PREF_FONT                         "font"
+#define GHEX_PREF_GROUP                                "group-data-by"
+#define GHEX_PREF_DATA_FONT                    "print-font-data"
+#define GHEX_PREF_HEADER_FONT          "print-font-header"
+#define GHEX_PREF_BOX_SIZE                     "print-shaded-rows"
+#define GHEX_PREF_OFFSETS_COLUMN       "show-offsets"
+#define GHEX_PREF_DARK_MODE                    "dark-mode"
+
+enum dark_mode {
+       DARK_MODE_OFF,
+       DARK_MODE_ON,
+       DARK_MODE_SYSTEM
+};
 
 /* Our preferred settings; as only one copy of them is required,
  * we'll make them global vars, though this is a bit ugly.
@@ -45,15 +53,20 @@ extern char                 *def_font_name;
 extern char                    *data_font_name, *header_font_name;
 extern char                    *offset_fmt;
 extern gboolean                show_offsets_column;
-
 extern guint           shaded_box_size;
 extern int                     def_group_type;
+extern int                     def_dark_mode;
+extern gboolean                sys_default_is_dark;
 
 extern GSettings       *settings;
 
 /* Initializes the gsettings client */
 void ghex_init_configuration (void);
 
+/* Cache the system default of prefer-dark-theme as gtk does not do this for
+ * us. */
+void get_sys_default_is_dark (void);
+
 G_END_DECLS
 
 #endif /* GHEX_CONFIGURATION_H */
diff --git a/src/ghex-application-window.c b/src/ghex-application-window.c
index 59fd98f8..bab0bbee 100644
--- a/src/ghex-application-window.c
+++ b/src/ghex-application-window.c
@@ -113,6 +113,9 @@ static GParamSpec *properties[N_PROPERTIES] = { NULL, };
  */
 static const char *main_actions[] = {
        "ghex.save-as",
+       "ghex.print",
+       "ghex.print-preview",
+       "win.group-data-by",
        "ghex.show-conversions",
        "ghex.insert-mode",
        "ghex.find",
@@ -299,7 +302,7 @@ set_css_provider_font_from_settings (GHexApplicationWindow *self)
 
        g_debug("%s: css_str: %s", __func__, css_str);
 
-       gtk_css_provider_load_from_data (GTK_STYLE_PROVIDER(self->provider),
+       gtk_css_provider_load_from_data (self->provider,
                        css_str, -1);
 }
 
@@ -330,6 +333,28 @@ set_gtkhex_group_type_from_settings (GtkHex *gh)
        gtk_hex_set_group_type (gh, def_group_type);
 }
 
+static void
+set_dark_mode_from_settings (GHexApplicationWindow *self)
+{
+       GtkSettings *gtk_settings;
+
+       gtk_settings = gtk_settings_get_default ();
+
+       g_debug ("%s: def_dark_mode: %d", __func__, def_dark_mode);
+
+       if (def_dark_mode == DARK_MODE_SYSTEM) {
+               g_object_set (G_OBJECT(gtk_settings),
+                               "gtk-application-prefer-dark-theme",
+                               sys_default_is_dark,
+                               NULL);
+       } else {
+               g_object_set (G_OBJECT(gtk_settings),
+                               "gtk-application-prefer-dark-theme",
+                               def_dark_mode == DARK_MODE_ON ? TRUE : FALSE,
+                               NULL);
+       }
+}
+       
 
 /* Common macro for the `settings*changed_cb` stuff.
  * Between _START and _END, put in function calls that use `gh` to be applied
@@ -709,6 +734,12 @@ ghex_application_window_document_changed_cb (HexDocument *doc,
 
        g_return_if_fail (GHEX_IS_APPLICATION_WINDOW (self));
 
+       /* The appwindow as a whole not interested in any document changes that
+        * don't pertain to the one that is actually in view.
+        */
+       if (doc != gtk_hex_get_document (self->gh))
+               return;
+
        ghex_application_window_set_can_save (self,
                        hex_document_has_changed (doc));
 }
@@ -1099,6 +1130,32 @@ save_as (GtkWidget *widget,
        g_clear_object (&existing_file);
 }
 
+static void
+print_preview (GtkWidget *widget,
+               const char *action_name,
+               GVariant *parameter)
+{
+       GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW(widget);
+
+       g_return_if_fail (GTK_IS_HEX(self->gh));
+       (void)widget, (void)action_name, (void)parameter;       /* unused */
+
+       common_print (GTK_WINDOW(self), self->gh, /* preview: */ TRUE);
+}
+
+static void
+do_print (GtkWidget *widget,
+               const char *action_name,
+               GVariant *parameter)
+{
+       GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW(widget);
+
+       g_return_if_fail (GTK_IS_HEX(self->gh));
+       (void)widget, (void)action_name, (void)parameter;       /* unused */
+
+       common_print (GTK_WINDOW(self), self->gh, /* preview: */ FALSE);
+}
+
 /* convenience helper function to build a GtkHex widget pre-loaded with
  * a hex document, from a GFile *.
  */
@@ -1637,6 +1694,12 @@ ghex_application_window_init (GHexApplicationWindow *self)
 
        gtk_widget_init_template (widget);
 
+       /* Cache system default of prefer-dark-mode; gtk does not do this. This
+        * is run here as it cannot be done until we have a 'screen'. */
+       get_sys_default_is_dark ();
+       /* Do dark mode if requested */
+       set_dark_mode_from_settings (self);
+
        /* Setup conversions box and pane */
        self->dialog = hex_dialog_new ();
        self->dialog_widget = hex_dialog_getview (self->dialog);
@@ -1679,6 +1742,8 @@ ghex_application_window_init (GHexApplicationWindow *self)
     g_signal_connect (settings, "changed::" GHEX_PREF_GROUP,
                       G_CALLBACK (settings_group_type_changed_cb), self);
 
+    g_signal_connect_swapped (settings, "changed::" GHEX_PREF_DARK_MODE,
+                      G_CALLBACK (set_dark_mode_from_settings), self);
 
        /* Actions - SETTINGS */
 
@@ -1850,6 +1915,14 @@ ghex_application_window_class_init(GHexApplicationWindowClass *klass)
                        NULL,   // GVariant string param_type
                        save_as);
 
+       gtk_widget_class_install_action (widget_class, "ghex.print",
+                       NULL,   // GVariant string param_type
+                       do_print);
+
+       gtk_widget_class_install_action (widget_class, "ghex.print-preview",
+                       NULL,   // GVariant string param_type
+                       print_preview);
+
        gtk_widget_class_install_action (widget_class, "ghex.show-conversions",
                        NULL,   // GVariant string param_type
                        toggle_conversions);
@@ -1883,12 +1956,6 @@ ghex_application_window_class_init(GHexApplicationWindowClass *klass)
                        "ghex.converter", "converter-open");
 
 
-
-//     gtk_widget_class_install_action (widget_class, "win.group-data-by",
-//                     "i",    // GVariant string param_type
-//                     NULL);
-
-
        /* WIDGET TEMPLATE .UI */
 
        gtk_widget_class_set_template_from_resource (widget_class,
diff --git a/src/gtkhex.c b/src/gtkhex.c
index ad42bc4e..42255a2e 100644
--- a/src/gtkhex.c
+++ b/src/gtkhex.c
@@ -240,7 +240,7 @@ popup_context_menu(GtkWidget *widget, double x, double y)
        g_object_unref (builder);
 }
 
-/* ACTIONS - just wrappers around callbacks for this widget. */
+/* ACTIONS */
 
 static void
 copy_action (GtkWidget *widget,
@@ -256,6 +256,72 @@ copy_action (GtkWidget *widget,
        gtk_hex_copy_to_clipboard (gh);
 }
 
+static void
+redo_action (GtkWidget *widget,
+               const char *action_name,
+               GVariant *parameter)
+{
+       GtkHex *gh = GTK_HEX(widget);
+       HexDocument *doc;
+       HexChangeData *cd;
+
+       (void)action_name, (void)parameter;
+
+       g_return_if_fail (GTK_IS_HEX(gh));
+       g_return_if_fail (HEX_IS_DOCUMENT(gh->document));
+
+       /* shorthand. */
+       doc = gh->document;
+
+       if (doc->undo_stack && doc->undo_top != doc->undo_stack) {
+               hex_document_redo(doc);
+
+               cd = doc->undo_top->data;
+
+               gtk_hex_set_cursor(gh, cd->start);
+               gtk_hex_set_nibble(gh, cd->lower_nibble);
+       }
+}
+
+static void
+doc_undo_redo_cb (HexDocument *doc, gpointer user_data)
+{
+       GtkHex *gh = GTK_HEX(user_data);
+       g_return_if_fail (GTK_IS_HEX (gh));
+       
+       gtk_widget_action_set_enabled (GTK_WIDGET(gh),
+                       "gtkhex.undo", hex_document_can_undo (doc));
+       gtk_widget_action_set_enabled (GTK_WIDGET(gh),
+                       "gtkhex.redo", hex_document_can_redo (doc));
+}
+
+static void
+undo_action (GtkWidget *widget,
+               const char *action_name,
+               GVariant *parameter)
+{
+       GtkHex *gh = GTK_HEX(widget);
+       HexDocument *doc;
+       HexChangeData *cd;
+
+       (void)action_name, (void)parameter;
+
+       g_return_if_fail (GTK_IS_HEX(gh));
+       g_return_if_fail (HEX_IS_DOCUMENT(gh->document));
+
+       /* shorthand */
+       doc = gh->document;
+
+       if (doc->undo_top) {
+               cd = doc->undo_top->data;
+
+               hex_document_undo(doc);
+
+               gtk_hex_set_cursor(gh, cd->start);
+               gtk_hex_set_nibble(gh, cd->lower_nibble);
+       }
+}
+
 /*
  * ?_to_pointer translates mouse coordinates in hex/ascii view
  * to cursor coordinates.
@@ -2809,12 +2875,22 @@ gtk_hex_snapshot (GtkWidget *widget, GtkSnapshot *snapshot)
        }
 }
 
-static void gtk_hex_document_changed(HexDocument* doc, gpointer change_data,
+static void
+gtk_hex_document_changed (HexDocument* doc, gpointer change_data,
         gboolean push_undo, gpointer data)
 {
-    gtk_hex_real_data_changed (GTK_HEX(data), change_data);
-}
+       GtkHex *gh = GTK_HEX(data);
+       g_return_if_fail (GTK_IS_HEX (gh));
 
+       TEST_DEBUG_FUNCTION_START
+
+    gtk_hex_real_data_changed (gh, change_data);
+
+       gtk_widget_action_set_enabled (GTK_WIDGET(gh),
+                       "gtkhex.undo", hex_document_can_undo (doc));
+       gtk_widget_action_set_enabled (GTK_WIDGET(gh),
+                       "gtkhex.redo", hex_document_can_redo (doc));
+}
 
 static void
 gtk_hex_class_init (GtkHexClass *klass)
@@ -2894,9 +2970,16 @@ gtk_hex_class_init (GtkHexClass *klass)
                        NULL,   // GVariant string param_type
                        copy_action);
 
+       gtk_widget_class_install_action (widget_class, "gtkhex.undo",
+                       NULL,   // GVariant string param_type
+                       undo_action);
 
-       /* SHORTCUTS (not to be confused with keybindings, which are set up
-        * in gtk_hex_init) */
+       gtk_widget_class_install_action (widget_class, "gtkhex.redo",
+                       NULL,   // GVariant string param_type
+                       redo_action);
+
+       /* SHORTCUTS FOR ACTIONS (not to be confused with keybindings, which are
+        * set up in gtk_hex_init) */
 
        /* Ctrl+c - copy */
        gtk_widget_class_add_binding_action (widget_class,
@@ -3240,6 +3323,14 @@ gtk_hex_init(GtkHex *gh)
 
        g_signal_connect(G_OBJECT(gh->adj), "value-changed",
                                         G_CALLBACK(display_scrolled), gh);
+
+       /* ACTIONS - Undo / Redo should start out disabled. */
+
+       gtk_widget_action_set_enabled (GTK_WIDGET(gh),
+                       "gtkhex.undo", FALSE);
+       gtk_widget_action_set_enabled (GTK_WIDGET(gh),
+                       "gtkhex.redo", FALSE);
+
 }
 
 GtkWidget *gtk_hex_new(HexDocument *owner) {
@@ -3249,8 +3340,18 @@ GtkWidget *gtk_hex_new(HexDocument *owner) {
        g_return_val_if_fail (gh != NULL, NULL);
 
        gh->document = owner;
+
+       /* Setup document signals (can't do in _init because we don't have
+        * access to that object yet.
+        */
     g_signal_connect (G_OBJECT (gh->document), "document-changed",
             G_CALLBACK (gtk_hex_document_changed), gh);
+
+    g_signal_connect (G_OBJECT (gh->document), "undo",
+            G_CALLBACK (doc_undo_redo_cb), gh);
+       
+    g_signal_connect (G_OBJECT (gh->document), "redo",
+            G_CALLBACK (doc_undo_redo_cb), gh);
        
        return GTK_WIDGET(gh);
 }
diff --git a/src/hex-document.c b/src/hex-document.c
index abc6e451..806d35fd 100644
--- a/src/hex-document.c
+++ b/src/hex-document.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 -*- */
 /* hex-document.c - implementation of a hex document
 
@@ -1157,3 +1159,26 @@ hex_document_change_file_name (HexDocument *doc, const char *new_file_name)
                return FALSE;
        }
 }
+
+gboolean
+hex_document_can_undo (HexDocument *doc)
+{
+       if (! doc->undo_max)
+               return FALSE;
+       else if (doc->undo_top)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+gboolean
+hex_document_can_redo (HexDocument *doc)
+{
+       if (! doc->undo_stack)
+               return FALSE;
+       else if (doc->undo_stack != doc->undo_top)
+               return TRUE;
+       else
+               return FALSE;
+}
+
diff --git a/src/hex-document.h b/src/hex-document.h
index 53c8940e..bb27c2c2 100644
--- a/src/hex-document.h
+++ b/src/hex-document.h
@@ -21,8 +21,8 @@
    Author: Jaka Mocnik <jaka gnu org>
  */
 
-#ifndef __HEX_DOCUMENT_H__
-#define __HEX_DOCUMENT_H__
+#ifndef HEX_DOCUMENT_H
+#define HEX_DOCUMENT_H
 
 #include <stdio.h>
 
@@ -94,42 +94,44 @@ GType       hex_document_get_type(void);
 HexDocument *hex_document_new(void);
 HexDocument *hex_document_new_from_file(const gchar *name);
 void        hex_document_set_data(HexDocument *doc, guint offset,
-                                                                 guint len, guint rep_len, guchar *data,
-                                                                 gboolean undoable);
+               guint len, guint rep_len, guchar *data,
+               gboolean undoable);
 void        hex_document_set_byte(HexDocument *doc, guchar val, guint offset,
-                                                                 gboolean insert, gboolean undoable);
+               gboolean insert, gboolean undoable);
 void        hex_document_set_nibble(HexDocument *doc, guchar val,
-                                                                       guint offset, gboolean lower_nibble,
-                                                                       gboolean insert, gboolean undoable);
+               guint offset, gboolean lower_nibble,
+               gboolean insert, gboolean undoable);
 guchar      hex_document_get_byte(HexDocument *doc, guint offset);
 guchar      *hex_document_get_data(HexDocument *doc, guint offset, guint len);
 void        hex_document_delete_data(HexDocument *doc, guint offset,
-                                                                        guint len, gboolean undoable);
+               guint len, gboolean undoable);
 gint        hex_document_read(HexDocument *doc);
 gint        hex_document_write(HexDocument *doc);
 gint        hex_document_write_to_file(HexDocument *doc, FILE *file);
 gint        hex_document_export_html(HexDocument *doc,
-                                                                        gchar *html_path, gchar *base_name,
-                                                                        guint start, guint end,
-                                                                        guint cpl, guint lpp, guint cpw);
+               gchar *html_path, gchar *base_name,
+               guint start, guint end,
+               guint cpl, guint lpp, guint cpw);
 gboolean    hex_document_has_changed(HexDocument *doc);
 void        hex_document_changed(HexDocument *doc, gpointer change_data,
-                                                                gboolean push_undo);
+               gboolean push_undo);
 void        hex_document_set_max_undo(HexDocument *doc, guint max_undo);
 gboolean    hex_document_undo(HexDocument *doc);
 gboolean    hex_document_redo(HexDocument *doc);
 gint        hex_document_compare_data(HexDocument *doc, guchar *s2,
-                                                                         gint pos, gint len);
+               gint pos, gint len);
 gint        hex_document_find_forward(HexDocument *doc, guint start,
-                                                                         guchar *what, gint len, guint 
*found);
+               guchar *what, gint len, guint *found);
 gint        hex_document_find_backward(HexDocument *doc, guint start,
-                                                                          guchar *what, gint len, guint 
*found);
+               guchar *what, gint len, guint *found);
 void        hex_document_remove_view(HexDocument *doc, GtkWidget *view);
 GtkWidget   *hex_document_add_view(HexDocument *doc);
 const GList *hex_document_get_list(void);
 gboolean    hex_document_is_writable(HexDocument *doc);
 gboolean    hex_document_change_file_name (HexDocument *doc, const char *new_file_name);
+gboolean    hex_document_can_undo (HexDocument *doc);
+gboolean    hex_document_can_redo (HexDocument *doc);
 
 G_END_DECLS
 
-#endif /* __HEX_DOCUMENT_H__ */
+#endif /* HEX_DOCUMENT_H */
diff --git a/src/preferences.c b/src/preferences.c
index cc85def0..f7989f69 100644
--- a/src/preferences.c
+++ b/src/preferences.c
@@ -86,6 +86,8 @@ static GtkWidget *long_chkbtn;
 static GtkWidget *shaded_box_chkbtn;
 static GtkWidget *shaded_box_spinbtn;
 static GtkWidget *shaded_box_box;
+static GtkWidget *dark_mode_switch;
+static GtkWidget *system_default_chkbtn;
 static GtkWidget *close_button;
 static GtkWidget *help_button;
 
@@ -307,6 +309,52 @@ monospace_only (GtkWidget *font_button)
                        NULL, NULL);    /* no user data, no destroy func for same. */
 }
 
+static gboolean
+dark_mode_set_cb (GtkSwitch *widget,
+               gboolean state,
+               gpointer user_data)
+{
+       int dark_mode;
+
+       (void)user_data;        /* unused */
+
+       if (state)
+               dark_mode = DARK_MODE_ON;
+       else
+               dark_mode = DARK_MODE_OFF;
+
+       g_settings_set_enum (settings,
+                       GHEX_PREF_DARK_MODE,
+                       dark_mode);
+
+       return GDK_EVENT_PROPAGATE;
+}
+
+static void
+system_default_set_cb (GtkCheckButton *checkbutton,
+               gpointer user_data)
+{
+       gboolean checked;
+       int dark_mode;
+
+       (void)user_data;        /* unused */
+
+       checked = gtk_check_button_get_active (checkbutton);
+
+       gtk_widget_set_sensitive (dark_mode_switch,
+                       checked ? FALSE : TRUE);
+
+       if (checked) {
+               dark_mode = DARK_MODE_SYSTEM;
+       } else {
+               dark_mode = gtk_switch_get_active (GTK_SWITCH(dark_mode_switch)) ?
+                       DARK_MODE_ON : DARK_MODE_OFF;
+       }
+       g_settings_set_enum (settings,
+                       GHEX_PREF_DARK_MODE,
+                       dark_mode);
+}
+
 static void
 setup_signals (void)
 {
@@ -321,6 +369,14 @@ setup_signals (void)
        g_signal_connect (header_font_button, "font-set",
                        G_CALLBACK(font_set_cb), GINT_TO_POINTER(HEADER_FONT));
 
+       /* dark mode */
+
+       g_signal_connect (dark_mode_switch, "state-set",
+                       G_CALLBACK(dark_mode_set_cb), NULL);
+
+       g_signal_connect (system_default_chkbtn, "toggled",
+                       G_CALLBACK(system_default_set_cb), NULL);
+
        /* group type checkbuttons */
 
        g_signal_connect (bytes_chkbtn, "toggled",
@@ -354,16 +410,34 @@ setup_signals (void)
                        G_CALLBACK(help_clicked_cb), NULL);
 }
 
-/* put all of your GET_WIDGET calls other than the main prefs_dialog widget
- * and CSS-only stuff in here, please.
- */
 static void
 grab_widget_values_from_settings (void)
 {
+       GtkSettings *gtk_settings;
+
        /* font_button */
        gtk_font_chooser_set_font (GTK_FONT_CHOOSER(font_button),
                        def_font_name);
 
+       /* dark mode stuff */
+
+       /* Set switch to appropriate position and grey out if system default */
+       if (def_dark_mode == DARK_MODE_SYSTEM)
+       {
+               gtk_check_button_set_active (GTK_CHECK_BUTTON(system_default_chkbtn),
+                               TRUE);
+               gtk_widget_set_sensitive (dark_mode_switch, FALSE);
+               gtk_switch_set_state (GTK_SWITCH(dark_mode_switch),
+                               sys_default_is_dark);
+       } else
+       {
+               gtk_check_button_set_active (GTK_CHECK_BUTTON(system_default_chkbtn),
+                               FALSE);
+               gtk_widget_set_sensitive (dark_mode_switch, TRUE);
+               gtk_switch_set_state (GTK_SWITCH(dark_mode_switch),
+                               def_dark_mode == DARK_MODE_ON ? TRUE : FALSE);
+       }
+
        /* data_font_button */
        gtk_font_chooser_set_font (GTK_FONT_CHOOSER(data_font_button),
                        data_font_name);
@@ -426,6 +500,8 @@ init_widgets (void)
        GET_WIDGET (shaded_box_chkbtn);
        GET_WIDGET (shaded_box_spinbtn);
        GET_WIDGET (shaded_box_box);
+       GET_WIDGET (dark_mode_switch);
+       GET_WIDGET (system_default_chkbtn);
        GET_WIDGET (close_button);
        GET_WIDGET (help_button);
 
diff --git a/src/preferences.ui b/src/preferences.ui
index 8d25cdff..99f64331 100644
--- a/src/preferences.ui
+++ b/src/preferences.ui
@@ -37,19 +37,46 @@
 
                                                                                <child> <!-- font_frame -->
                                                                                        <object 
class="GtkFrame" id="font_frame">
+
                                                                                                <child 
type="label">
                                                                                                        
<object class="GtkLabel" id="font_frame_label">
                                                                                                              
  <property name="label" translatable="yes">Font</property>
                                                                                                        
</object>
                                                                                                </child>
+
                                                                                                <child>
                                                                                                        
<object class="GtkFontButton" id="font_button">
                                                                                                              
  <property name="halign">start</property>
                                                                                                        
</object>
                                                                                                </child>
+
                                                                                        </object>
                                                                                </child> <!-- /font_frame -->
 
+                                                                               <child> <!-- dark mode hbox 
-->
+                                                                                       <object 
class="GtkBox">
+                                                                                               <property 
name="orientation">horizontal</property>
+                                                                                               <property 
name="spacing">18</property>
+
+                                                                                               <child>
+                                                                                                       
<object class="GtkLabel" id="dark_mode_label">
+                                                                                                             
  <property name="label" translatable="yes">Dark Mode</property>
+                                                                                                       
</object>
+                                                                                               </child>
+                                                                                               <child>
+                                                                                                       
<object class="GtkSwitch" id="dark_mode_switch">
+                                                                                                             
  <property name="active">false</property>
+                                                                                                       
</object>
+                                                                                               </child>
+                                                                                               <child>
+                                                                                                       
<object class="GtkCheckButton" id="system_default_chkbtn">
+                                                                                                             
  <property name="label" translatable="yes">Use system default</property>
+                                                                                                       
</object>
+                                                                                               </child>
+
+                                                                                       </object>
+                                                                               </child> <!-- /dark mode hbox 
-->
+
                                                                                <child> <!-- group_type_frame 
-->
                                                                                        <object 
class="GtkFrame" id="group_type_frame">
                                                                                                <property 
name="label" translatable="yes">Hex Group Type</property>
diff --git a/src/print.c b/src/print.c
index 451b209b..9f057faa 100644
--- a/src/print.c
+++ b/src/print.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 -*- */
 /* print.c - print a HexDocument
 
@@ -26,23 +28,19 @@
 #  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
-#include <glib/gi18n.h>
-
 #include "print.h"
-#include "gtkhex.h"
-#include "ui.h"
 
 #define is_printable(c) (((((guchar)c)>=0x20) && (((guchar)c)<0x7F))?1:0)
 
-gchar *data_font_name, *header_font_name;
+char *data_font_name, *header_font_name;
 guint shaded_box_size;
 
 static void print_header(GHexPrintJobInfo *pji, unsigned int page);
 static void print_row(GHexPrintJobInfo *pji, unsigned int offset,
                                          unsigned int bytes, int row);
-static void format_hex(HexDocument *doc, guint gt, gchar *out,
+static void format_hex(HexDocument *doc, guint gt, char *out,
                                           guint start, guint end);
-static void format_ascii(HexDocument *doc, gchar *out,
+static void format_ascii(HexDocument *doc, char *out,
                                                 guint start, guint end);
 static void print_shaded_boxes( GHexPrintJobInfo *pji, guint page,
                                                                guint max_row);
@@ -52,12 +50,12 @@ static void print_header(GHexPrintJobInfo *pji, unsigned int page)
 {
        PangoLayout *layout;
        cairo_t *cr = gtk_print_context_get_cairo_context (pji->pc);
-       gchar *text1 = g_filename_to_utf8 (pji->doc->file_name, -1, NULL,
+       char *text1 = g_filename_to_utf8 (pji->doc->file_name, -1, NULL,
                                                                           NULL, NULL);
-       gchar *text2 = g_strdup_printf (_("Page: %i/%i"), page, pji->pages);
-       gchar *pagetext = g_strdup_printf ("%d", page);
-       gdouble x, y;
-       gint width, height;
+       char *text2 = g_strdup_printf (_("Page: %i/%i"), page, pji->pages);
+       char *pagetext = g_strdup_printf ("%d", page);
+       double x, y;
+       int width, height;
 
        layout = gtk_print_context_create_pango_layout (pji->pc);
        pango_layout_set_text (layout, pagetext, -1);
@@ -99,13 +97,14 @@ static void print_row(GHexPrintJobInfo *pji, unsigned int offset,
                                          unsigned int bytes, int row)
 {
        PangoLayout *layout;
-       gdouble x, y;
+       double x, y;
        const int TEMP_LEN = 256;
-       gchar *temp = g_malloc(TEMP_LEN + 1);
+       char *temp = g_malloc(TEMP_LEN + 1);
        cairo_t *cr = gtk_print_context_get_cairo_context (pji->pc);
 
        y = pji->header_height +
                (pji->font_char_height*(row + 1));
+
        /* Print Offset */ 
        cairo_move_to (cr, 0, y);
        layout = gtk_print_context_create_pango_layout (pji->pc);
@@ -115,6 +114,7 @@ static void print_row(GHexPrintJobInfo *pji, unsigned int offset,
        pango_layout_set_indent (layout, 0);
        pango_cairo_show_layout (cr, layout);
        g_object_unref (layout);
+
        /* Print Hex */
        x = pji->font_char_width*pji->offset_chars +
                pji->pad_size ;
@@ -142,13 +142,13 @@ static void print_row(GHexPrintJobInfo *pji, unsigned int offset,
        g_free(temp);
 }
 
-static void format_hex(HexDocument *doc, guint gt, gchar *out,
-                                          guint start, guint end)
+static void format_hex (HexDocument *doc, guint gt, char *out,
+               guint start, guint end)
 {
-       gint i, j, low, high;
+       int i, j, low, high;
        guchar c;
 
-       for(i = start + 1, j = 0; i <= end; i++) {
+       for (i = start + 1, j = 0; i <= end; i++) {
                c = hex_document_get_byte(doc, i - 1);
                low = c & 0x0F;
                high = (c & 0xF0) >> 4;
@@ -156,18 +156,19 @@ static void format_hex(HexDocument *doc, guint gt, gchar *out,
                out[j++] = ((high < 10)?(high + '0'):(high - 10 + 'A'));
                out[j++] = ((low < 10)?(low + '0'):(low - 10 + 'A'));
 
-               if(i % gt == 0)
+               if (i % gt == 0)
           out[j++] = ' ';
        }
        out[j++] = 0;
 }
 
-static void format_ascii(HexDocument *doc, gchar *out, guint start, guint end)
+static void format_ascii (HexDocument *doc,
+               char *out, guint start, guint end)
 {
-       gint i, j;
+       int i, j;
        guchar c;
 
-       for(i = start, j = 0; i < end; i++, j++) {
+       for (i = start, j = 0; i < end; i++, j++) {
                c = hex_document_get_byte(doc, i);
                if (is_printable(c))
                        out[j] = c;
@@ -178,24 +179,26 @@ static void format_ascii(HexDocument *doc, gchar *out, guint start, guint end)
 }
 
 static void print_shaded_boxes(GHexPrintJobInfo *pji, guint page,
-                                                          guint max_row)
+               guint max_row)
 {
        guint i;
        guint box_size = shaded_box_size;
 
-       if(box_size == 0)
+       if (box_size == 0)
                return;
 
-       for(i = box_size + 1;
+       for (i = box_size + 1;
                i <= pji->rows_per_page && i <= max_row;
                i += box_size*2)
+       {
                print_shaded_box (pji, i+1, ((i + box_size - 1) > max_row ?
                                                                  max_row - i + 1 : box_size));
+       }
 }
 
-static void print_shaded_box(GHexPrintJobInfo *pji, guint row, guint rows)
+static void print_shaded_box (GHexPrintJobInfo *pji, guint row, guint rows)
 {
-       gdouble box_top;
+       double box_top;
        cairo_t *cr = gtk_print_context_get_cairo_context (pji->pc);
 
        box_top = pji->header_height + row * pji->font_char_height;
@@ -221,7 +224,7 @@ static void print_shaded_box(GHexPrintJobInfo *pji, guint row, guint rows)
  * Creates a new GHexPrintJobInfo object.
  **/
 GHexPrintJobInfo *
-ghex_print_job_info_new(HexDocument *doc, guint group_type)
+ghex_print_job_info_new (HexDocument *doc, guint group_type)
 {
        GHexPrintJobInfo *pji;
        PangoFontDescription *d_font;
@@ -266,7 +269,7 @@ ghex_print_job_info_new(HexDocument *doc, guint group_type)
  * Destroys the GHexPrintJobInfo object pointed to by pji.
  **/
 void
-ghex_print_job_info_destroy(GHexPrintJobInfo *pji)
+ghex_print_job_info_destroy (GHexPrintJobInfo *pji)
 {
        pango_font_description_free (pji->h_font);
        pango_font_description_free (pji->d_font);
@@ -288,8 +291,8 @@ begin_print (GtkPrintOperation *operation,
     PangoLayout *layout;
     GHexPrintJobInfo *pji = (GHexPrintJobInfo *)data;
     pji->pc = context;
-    gint font_width, font_height;
-    gint printable_width, printable_height;
+    int font_width, font_height;
+    int printable_width, printable_height;
 
     layout = gtk_print_context_create_pango_layout (context);
     pango_layout_set_text (layout, " ", -1);
@@ -326,10 +329,10 @@ begin_print (GtkPrintOperation *operation,
 void
 print_page (GtkPrintOperation *operation,
             GtkPrintContext   *context,
-            gint               page_nr,
+            int               page_nr,
             gpointer           data)
 {
-       gint j, max_row;
+       int j, max_row;
 
        GHexPrintJobInfo *pji = (GHexPrintJobInfo *)data;
        g_return_if_fail(pji != NULL);
@@ -358,4 +361,3 @@ print_page (GtkPrintOperation *operation,
                print_row (pji, file_offset, length, j);
        }
 }
-
diff --git a/src/print.h b/src/print.h
index 0527858c..46290f3d 100644
--- a/src/print.h
+++ b/src/print.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 -*- */
 /* print.h - printing related stuff for ghex
 
@@ -21,12 +23,13 @@
    Author: Jaka Mocnik <jaka gnu org>
 */
 
-#ifndef __GHEX_PRINT_H__
-#define __GHEX_PRINT_H__
+#ifndef GHEX_PRINT_H
+#define GHEX_PRINT_H
 
 #include <gtk/gtk.h>
-
-#include "hex-document.h"
+#include <glib/gi18n.h>
+#include <gtkhex.h>
+#include <hex-document.h>
 
 G_BEGIN_DECLS
 
@@ -40,34 +43,36 @@ struct _GHexPrintJobInfo {
        PangoFontDescription *d_font, *h_font;
        HexDocument *doc;
 
-       int   pages;
-       gint range;
-       gint page_first;
-       gint page_last;
+       int pages;
+       int range;
+       int page_first;
+       int page_last;
 
-       gdouble header_height;
+       double header_height;
        
-       gint font_char_width;
-       gint font_char_height;
+       int font_char_width;
+       int font_char_height;
 
-       int   bytes_per_row, rows_per_page;
-       gdouble pad_size;
-       int   offset_chars ; /* How many chars are used in the offset window */
-       int   gt;            /* group_type */
+       int  bytes_per_row, rows_per_page;
+       double pad_size;
+       int offset_chars ; /* How many chars are used in the offset window */
+       int gt;            /* group_type */
        gboolean preview;
 };
 
-/* printing */
+/* FUNCTION DECLARATIONS */
+
 void begin_print (GtkPrintOperation *operation,
                   GtkPrintContext   *context,
                   gpointer           data);
 void print_page (GtkPrintOperation *operation,
                  GtkPrintContext   *context,
-                 gint               page_nr,
+                 int               page_nr,
                  gpointer           data);
-GHexPrintJobInfo *ghex_print_job_info_new(HexDocument *doc, guint group_type);
+GHexPrintJobInfo *ghex_print_job_info_new (HexDocument *doc,
+               guint group_type);
 void ghex_print_job_info_destroy(GHexPrintJobInfo *pji);
 
 G_END_DECLS
 
-#endif /* !__GHEX_PRINT_H__ */
+#endif /* GHEX_PRINT_H */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]