[ghex/gtk4-port: 72/91] Basic copy/paste-special up and running.




commit 134381d63a2e2f059c7eab81b2b0949e5bd21d43
Author: Logan Rathbone <poprocks gmail com>
Date:   Tue Feb 2 21:50:26 2021 -0500

    Basic copy/paste-special up and running.
    
    Let's let this cook for a bit before moving out of staging.
    
    Quite frankly as the code grew and grew, I came to the conclusion it was
    probably a mistake not to use a GObject structure for this dialog. It
    works now, but reworking it wouldn't hurt.

 src/ghex-application-window.c |  40 +++++-
 src/paste-special.c           | 280 ++++++++++++++++++++++++++++++++++++------
 src/paste-special.h           |   3 +
 3 files changed, 279 insertions(+), 44 deletions(-)
---
diff --git a/src/ghex-application-window.c b/src/ghex-application-window.c
index 7f7bbf51..84fb83ed 100644
--- a/src/ghex-application-window.c
+++ b/src/ghex-application-window.c
@@ -54,6 +54,8 @@ struct _GHexApplicationWindow
        GtkWidget *jump_dialog;
        GtkWidget *chartable;
        GtkWidget *converter;
+       GtkWidget *paste_special_dialog;
+       GtkWidget *copy_special_dialog;
 
        /* From GtkBuilder: */
        GtkWidget *no_doc_label;
@@ -568,23 +570,44 @@ close_tab_shortcut_cb (GtkWidget *widget,
        return TRUE;
 }
 
+static gboolean
+copy_special_shortcut_cb (GtkWidget *widget,
+               GVariant *args,
+               gpointer user_data)
+{
+       GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW(widget);
+       GdkClipboard *clipboard;
+
+       g_return_val_if_fail (GTK_IS_HEX (self->gh), FALSE);
+
+       clipboard = gtk_widget_get_clipboard (GTK_WIDGET(self->gh));
+
+       if (! GTK_IS_WIDGET (self->copy_special_dialog)) {
+               self->copy_special_dialog = create_copy_special_dialog (self,
+                               clipboard);
+       }
+       gtk_widget_show (self->copy_special_dialog);
+
+       return TRUE;
+}
 
 static gboolean
 paste_special_shortcut_cb (GtkWidget *widget,
                GVariant *args,
                gpointer user_data)
 {
-       // TEST
        GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW(widget);
        GdkClipboard *clipboard;
-       GtkWidget *paste_special_dialog;
 
        g_return_val_if_fail (GTK_IS_HEX (self->gh), FALSE);
 
        clipboard = gtk_widget_get_clipboard (GTK_WIDGET(self->gh));
-       paste_special_dialog = create_paste_special_dialog (self,
-                       clipboard);
-       gtk_widget_show (paste_special_dialog);
+
+       if (! GTK_IS_WIDGET (self->paste_special_dialog)) {
+               self->paste_special_dialog = create_paste_special_dialog (self,
+                               clipboard);
+       }
+       gtk_widget_show (self->paste_special_dialog);
 
        return TRUE;
 }
@@ -1794,6 +1817,13 @@ ghex_application_window_class_init(GHexApplicationWindowClass *klass)
                        paste_special_shortcut_cb,
                        NULL);
 
+       /* Ctrl+Shift+C - copy special */
+       gtk_widget_class_add_binding (widget_class,
+                       GDK_KEY_c,
+                       GDK_CONTROL_MASK | GDK_SHIFT_MASK,
+                       copy_special_shortcut_cb,
+                       NULL);
+
        /* WIDGET TEMPLATE .UI */
 
        gtk_widget_class_set_template_from_resource (widget_class,
diff --git a/src/paste-special.c b/src/paste-special.c
index 3fa7aa5d..b2cbeac0 100644
--- a/src/paste-special.c
+++ b/src/paste-special.c
@@ -40,6 +40,12 @@
 #define HEX_PASTE_ERROR hex_paste_error_quark ()
 G_DEFINE_QUARK (hex-paste-error-quark, hex_paste_error)
 
+typedef enum operations {
+       COPY_OPERATION,
+       PASTE_OPERATION
+
+} PasteSpecialOperation;
+
 typedef enum {
        HEX_PASTE_ERROR_INVALID
 
@@ -76,11 +82,17 @@ struct _MimeSubTypeLabel {
 
 G_DEFINE_TYPE (MimeSubTypeLabel, mime_sub_type_label, GTK_TYPE_WIDGET)
 
+/* PRIVATE FORWARD DECLARATIONS */
+static void dialog_response_cb (GtkDialog *dialog, int response_id,
+               gpointer user_data);
+static gboolean destroy_paste_special_dialog (void);
+
 /* STATIC GLOBALS */
 
 static GtkBuilder *builder;
 static GHexApplicationWindow *app_window;
 static GdkClipboard *clipboard;
+static GtkHexPasteData *hex_paste_data;
 static GHashTable *mime_hash;
 static GSList *known_mime[LAST_MIME];
 static GtkWidget *hex_paste_data_label;
@@ -143,6 +155,36 @@ mime_sub_type_label_new (KnownMimeType *known_type)
 
 /* PRIVATE FUNCTIONS */
 
+/* Note - I looked into reusing the hex block formatting code from GtkHex,
+ * but honestly, it looks like it'd be more trouble than it's worth.
+ */
+static char *
+hex_paste_data_to_delimited_hex (void)
+{
+       GString *buf;
+       guchar *doc_data = NULL;
+       guint elems;
+       char *ret_str;
+
+       g_return_val_if_fail (GTK_IS_HEX_PASTE_DATA (hex_paste_data), NULL);
+
+       buf = g_string_new (NULL);
+       doc_data = gtk_hex_paste_data_get_doc_data (hex_paste_data);
+       elems = gtk_hex_paste_data_get_elems (hex_paste_data);
+
+       for (guint i = 0; i < elems; ++i)
+       {
+               g_string_append_printf (buf, "%.2X", doc_data[i]);
+
+               if (i < elems - 1)
+                       g_string_append_c (buf, ' ');
+       }
+       ret_str = g_string_free (buf,
+                       FALSE);         /* Free the GString container but grab the C-string. */
+
+       return ret_str;
+}
+
 static GString *
 delimited_hex_to_gstring (const char *hex_str, GError **err)
 {
@@ -240,6 +282,34 @@ delimited_hex_paste (void)
                        NULL);  /* user_data */
 }
 
+static void
+delimited_hex_copy (void)
+{
+       GdkContentProvider *provider;
+       GValue value = G_VALUE_INIT;
+       GError *error = NULL;
+       gboolean have_hex_paste_data = FALSE;
+       char *hex_str = NULL;
+
+       /* Save selection to clipboard as HexPasteData */
+       gtk_hex_copy_to_clipboard (ghex_application_window_get_hex(app_window));
+
+       provider = gdk_clipboard_get_content (clipboard);
+       g_return_if_fail (GDK_IS_CONTENT_PROVIDER(provider));
+
+       g_value_init (&value, GTK_TYPE_HEX_PASTE_DATA);
+       gdk_content_provider_get_value (provider, &value, &error);
+       hex_paste_data = GTK_HEX_PASTE_DATA(g_value_get_object (&value));
+
+       hex_str = hex_paste_data_to_delimited_hex ();
+
+       // FIXME - Maybe this should be set up as a union? Not sure what would
+       // best satisfy the principle of least astonishment (POLA).
+       
+       if (hex_str)
+               gdk_clipboard_set_text (clipboard, hex_str);
+}
+
 static void
 create_hex_paste_data_label (void)
 {
@@ -253,7 +323,17 @@ row_activated_cb (GtkListBox *box,
                GtkListBoxRow *row,
                gpointer user_data)
 {
+#define STANDARD_PASTE \
+       gtk_hex_paste_from_clipboard \
+               (ghex_application_window_get_hex(app_window));
+
+#define STANDARD_COPY \
+       gtk_hex_copy_to_clipboard \
+               (ghex_application_window_get_hex(app_window));
+
        GtkWidget *child = gtk_list_box_row_get_child (row);
+       PasteSpecialOperation operation = GPOINTER_TO_INT (user_data);
+
        g_return_if_fail (GTK_IS_WIDGET(child));
 
        if (MIME_SUB_TYPE_IS_LABEL(child))
@@ -263,17 +343,27 @@ row_activated_cb (GtkListBox *box,
                switch (label->known_type->sub_type)
                {
                        case NO_SUBTYPE:
-                               gtk_hex_paste_from_clipboard
-                                       (ghex_application_window_get_hex(app_window));
+                               if (operation == COPY_OPERATION) {
+                                       STANDARD_COPY
+                               } else {
+                                       STANDARD_PASTE
+                               }
                                break;
 
                        case ASCII_PLAINTEXT:
-                               gtk_hex_paste_from_clipboard
-                                       (ghex_application_window_get_hex(app_window));
+                               if (operation == COPY_OPERATION) {
+                                       STANDARD_COPY
+                               } else {
+                                       STANDARD_PASTE
+                               }
                                break;
 
                        case HEX_PLAINTEXT:
-                               delimited_hex_paste ();
+                               if (operation == COPY_OPERATION) {
+                                       delimited_hex_copy ();
+                               } else {
+                                       delimited_hex_paste ();
+                               }
                                break;
 
                        default:
@@ -283,42 +373,43 @@ row_activated_cb (GtkListBox *box,
                                break;
                }
        }
-       else
+       else    /* HexPasteData */
        {
-               g_debug ("%s: HexPasteData - NOT IMPLEMENTED", __func__);
+               if (operation == COPY_OPERATION) {
+                       STANDARD_COPY
+               } else {
+                       STANDARD_PASTE
+               }
        }
 }
+#undef STANDARD_PASTE
+#undef STANDARD_COPY
 
 static void
-close_clicked_cb (GtkButton *button,
-               gpointer user_data)
+common_init_widgets (void)
 {
-       (void)user_data;        /* unused */
-       g_return_if_fail (GTK_IS_WINDOW (paste_special_dialog));
+       GET_WIDGET (paste_special_dialog);
+       GET_WIDGET (paste_button);
+       GET_WIDGET (close_button);
+       GET_WIDGET (paste_special_listbox);
 
-       gtk_window_destroy (GTK_WINDOW(paste_special_dialog));
+       gtk_list_box_set_activate_on_single_click (GTK_LIST_BOX(paste_special_listbox),
+                       FALSE);
 }
 
 static void
-paste_clicked_cb (GtkButton *button,
-               gpointer user_data)
+paste_special_init_widgets (void)
 {
-       GtkListBoxRow *row;
-
-       row = gtk_list_box_get_selected_row (GTK_LIST_BOX(paste_special_listbox));
-       g_signal_emit_by_name (row, "activate");
+       common_init_widgets ();
 }
 
 static void
-init_widgets (void)
+copy_special_init_widgets (void)
 {
-       GET_WIDGET (paste_special_dialog);
-       GET_WIDGET (paste_button);
-       GET_WIDGET (close_button);
-       GET_WIDGET (paste_special_listbox);
-
-       gtk_list_box_set_activate_on_single_click (GTK_LIST_BOX(paste_special_listbox),
-                       FALSE);
+       common_init_widgets ();
+       gtk_button_set_label (GTK_BUTTON(paste_button), _("_Copy"));
+       gtk_window_set_title (GTK_WINDOW(paste_special_dialog),
+                       _("Copy Special"));
 }
 
 static KnownMimeType *
@@ -467,26 +558,39 @@ init_mime_hash (void)
 }
 
 static void
-setup_signals (void)
+common_setup_signals (void)
 {
+       g_return_if_fail (GTK_IS_DIALOG (paste_special_dialog));
+
+       g_signal_connect (paste_special_dialog, "response",
+                       G_CALLBACK(dialog_response_cb), NULL);
+}
+
+static void
+paste_special_setup_signals (void)
+{
+       common_setup_signals ();
+
        g_signal_connect (paste_special_listbox, "row-activated",
-                       G_CALLBACK(row_activated_cb), NULL);
+                       G_CALLBACK(row_activated_cb), GINT_TO_POINTER (PASTE_OPERATION));
+}
 
-       g_signal_connect (paste_button, "clicked",
-                       G_CALLBACK(paste_clicked_cb), NULL);
+static void
+copy_special_setup_signals (void)
+{
+       common_setup_signals ();
 
-       g_signal_connect (close_button, "clicked",
-                       G_CALLBACK(close_clicked_cb), NULL);
+       g_signal_connect (paste_special_listbox, "row-activated",
+                       G_CALLBACK(row_activated_cb), GINT_TO_POINTER (COPY_OPERATION));
 }
 
 static void
-populate_listbox (void)
+paste_special_populate_listbox (void)
 {
        GdkContentProvider *provider;
        GdkContentFormats *formats;
        GValue value = G_VALUE_INIT;
        GError *error = NULL;
-       GtkHexPasteData *paste;
        gboolean have_hex_paste_data = FALSE;
        const char * const * mime_types;
 
@@ -510,8 +614,9 @@ populate_listbox (void)
 
        if (have_hex_paste_data)
        {
-               if (! hex_paste_data_label)
-                       create_hex_paste_data_label ();
+               hex_paste_data = GTK_HEX_PASTE_DATA(g_value_get_object (&value));
+
+               create_hex_paste_data_label ();
 
                gtk_list_box_append (GTK_LIST_BOX(paste_special_listbox),
                                hex_paste_data_label);
@@ -540,6 +645,81 @@ populate_listbox (void)
        }
 }
 
+static void
+copy_special_populate_listbox (void)
+{
+       /* We always give the option to copy to HexPasteData. */
+       create_hex_paste_data_label ();
+
+       gtk_list_box_append (GTK_LIST_BOX(paste_special_listbox),
+                       hex_paste_data_label);
+
+       /* Add a listbox entry for each known mime type. */
+       for (int i = 0; i < LAST_MIME; ++i)
+       {
+               GSList *tracer;
+               
+               for (tracer = known_mime[i];
+                               tracer != NULL;
+                               tracer = tracer->next)
+               {
+                       GtkWidget *label;
+                       KnownMimeType *type = tracer->data;
+
+                       label = mime_sub_type_label_new (type);
+                       gtk_list_box_append (GTK_LIST_BOX(paste_special_listbox), label);
+               }
+       }
+}
+
+static gboolean
+destroy_paste_special_dialog (void)
+{
+       g_debug ("%s: START", __func__);
+
+       g_return_val_if_fail (GTK_IS_WINDOW (paste_special_dialog),
+                       GDK_EVENT_PROPAGATE);
+
+       if (builder)
+               g_clear_object (&builder);
+
+       if (mime_hash)
+               g_clear_pointer (&mime_hash, g_hash_table_destroy);
+
+       /* This is not refcounted and is just a pure pointer */
+       if (hex_paste_data)
+               hex_paste_data = NULL;
+
+       gtk_window_destroy (GTK_WINDOW(paste_special_dialog));
+
+       return GDK_EVENT_STOP;
+}
+
+static void
+dialog_response_cb (GtkDialog *dialog,
+               int        response_id,
+               gpointer   user_data)
+{
+       GtkListBoxRow *row;
+
+       g_debug ("%s: START -- response_id: %d",
+                       __func__, response_id);
+
+       switch (response_id)
+       {
+               case GTK_RESPONSE_ACCEPT:
+                       row = gtk_list_box_get_selected_row
+                               (GTK_LIST_BOX(paste_special_listbox));
+                       g_signal_emit_by_name (row, "activate");
+                       break;
+
+               /* This should cover both delete events and close. */
+               default:
+                       destroy_paste_special_dialog ();
+                       break;
+       }
+}
+
 /* PUBLIC FUNCTIONS */
 
 GtkWidget *
@@ -553,10 +733,32 @@ create_paste_special_dialog (GHexApplicationWindow *parent,
        app_window = parent;
        builder = gtk_builder_new_from_resource (PASTE_SPECIAL_RESOURCE);
 
-       init_widgets ();
        init_mime_hash ();
-       populate_listbox ();
-       setup_signals ();
+       paste_special_init_widgets ();
+       paste_special_populate_listbox ();
+       paste_special_setup_signals ();
+
+       gtk_window_set_transient_for (GTK_WINDOW(paste_special_dialog),
+                       GTK_WINDOW(parent));
+
+       return paste_special_dialog;
+}
+
+GtkWidget *
+create_copy_special_dialog (GHexApplicationWindow *parent,
+               GdkClipboard *clip)
+{
+       g_return_val_if_fail (GDK_IS_CLIPBOARD (clip), NULL);
+       g_return_val_if_fail (GHEX_IS_APPLICATION_WINDOW (parent), NULL);
+
+       clipboard = clip;
+       app_window = parent;
+       builder = gtk_builder_new_from_resource (PASTE_SPECIAL_RESOURCE);
+
+       init_mime_hash ();
+       copy_special_init_widgets ();
+       copy_special_populate_listbox ();
+       copy_special_setup_signals ();
 
        gtk_window_set_transient_for (GTK_WINDOW(paste_special_dialog),
                        GTK_WINDOW(parent));
diff --git a/src/paste-special.h b/src/paste-special.h
index d27811c4..30bc57b9 100644
--- a/src/paste-special.h
+++ b/src/paste-special.h
@@ -39,6 +39,9 @@ G_BEGIN_DECLS
 GtkWidget *    create_paste_special_dialog (GHexApplicationWindow *parent,
                GdkClipboard *clip);
 
+GtkWidget *    create_copy_special_dialog (GHexApplicationWindow *parent,
+               GdkClipboard *clip);
+
 G_END_DECLS
 
 #endif /* PASTE_SPECIAL_H */


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