[ghex/gtk4-port: 70/91] Separate HexPasteData and initial impl of paste-special




commit 51cffabdfe2ea097e7c58267f6c793327f4bb6f3
Author: Logan Rathbone <poprocks gmail com>
Date:   Mon Feb 1 22:40:03 2021 -0500

    Separate HexPasteData and initial impl of paste-special

 src/ghex-application-window.c |  39 +++
 src/ghex-application-window.h |   3 +-
 src/ghex.gresource.xml        |   1 +
 src/gtkhex-layout-manager.h   |   8 +-
 src/gtkhex-paste-data.c       | 133 +++++++++++
 src/gtkhex-paste-data.h       |  47 ++++
 src/gtkhex.c                  | 117 +--------
 src/gtkhex.h                  |  12 +-
 src/meson.build               |  10 +-
 src/paste-special.c           | 544 ++++++++++++++++++++++++++++++++++++++++++
 src/paste-special.h           |  43 ++++
 src/paste-special.ui          |  65 +++++
 src/preferences.h             |   1 +
 13 files changed, 905 insertions(+), 118 deletions(-)
---
diff --git a/src/ghex-application-window.c b/src/ghex-application-window.c
index 35f127d3..7f7bbf51 100644
--- a/src/ghex-application-window.c
+++ b/src/ghex-application-window.c
@@ -23,6 +23,9 @@
 */
 
 #include "ghex-application-window.h"
+// FIXME - not sure I want this to be a dep. Needs to be in the .C file
+// due to both headers requiring each other
+#include "paste-special.h"
 
 /* DEFINES */
 
@@ -565,6 +568,27 @@ close_tab_shortcut_cb (GtkWidget *widget,
        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);
+
+       return TRUE;
+}
+
 static gboolean
 assess_can_save (HexDocument *doc)
 {
@@ -1763,6 +1787,13 @@ ghex_application_window_class_init(GHexApplicationWindowClass *klass)
                        close_tab_shortcut_cb,
                        NULL);
 
+       /* Ctrl+Shift+V - paste special */
+       gtk_widget_class_add_binding (widget_class,
+                       GDK_KEY_v,
+                       GDK_CONTROL_MASK | GDK_SHIFT_MASK,
+                       paste_special_shortcut_cb,
+                       NULL);
+
        /* WIDGET TEMPLATE .UI */
 
        gtk_widget_class_set_template_from_resource (widget_class,
@@ -1923,3 +1954,11 @@ ghex_application_window_open_file (GHexApplicationWindow *self, GFile *file)
 
        g_object_unref (my_file);
 }
+
+GtkHex *
+ghex_application_window_get_hex (GHexApplicationWindow *self)
+{
+       g_return_val_if_fail (GHEX_IS_APPLICATION_WINDOW (self), NULL);
+
+       return self->gh;
+}
diff --git a/src/ghex-application-window.h b/src/ghex-application-window.h
index de3d75ca..949ed382 100644
--- a/src/ghex-application-window.h
+++ b/src/ghex-application-window.h
@@ -1,6 +1,6 @@
 /* vim: ts=4 sw=4 colorcolumn=80
  * -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/* ghex-application-window.c - GHex main application window
+/* ghex-application-window.h - GHex main application window declarations
 
    Copyright © 2021 Logan Rathbone <poprocks gmail com>
 
@@ -55,6 +55,7 @@ void          ghex_application_window_activate_tab (GHexApplicationWindow *self,
 GList *                ghex_application_window_get_list (GHexApplicationWindow *self);
 void           ghex_application_window_open_file (GHexApplicationWindow *self,
                                GFile *file);
+GtkHex *       ghex_application_window_get_hex (GHexApplicationWindow *self);
 
 G_END_DECLS
 
diff --git a/src/ghex.gresource.xml b/src/ghex.gresource.xml
index 9db15156..9c4d21e5 100644
--- a/src/ghex.gresource.xml
+++ b/src/ghex.gresource.xml
@@ -28,5 +28,6 @@
                <file preprocess="xml-stripblanks" compressed="true">ghex-application-window.ui</file>
                <file preprocess="xml-stripblanks" compressed="true">context-menu.ui</file>
                <file preprocess="xml-stripblanks" compressed="true">preferences.ui</file>
+               <file preprocess="xml-stripblanks" compressed="true">paste-special.ui</file>
        </gresource>
 </gresources>
diff --git a/src/gtkhex-layout-manager.h b/src/gtkhex-layout-manager.h
index 1855a193..48f44b8a 100644
--- a/src/gtkhex-layout-manager.h
+++ b/src/gtkhex-layout-manager.h
@@ -27,12 +27,10 @@
 
 #include <gtk/gtk.h>
 
-G_BEGIN_DECLS
+/* Not a circular dep; this is just for the GROUP_* enums defined there. */
+#include "gtkhex.h"
 
-/* how to group bytes? */
-#define GROUP_BYTE 1
-#define GROUP_WORD 2
-#define GROUP_LONG 4
+G_BEGIN_DECLS
 
 #define GTK_TYPE_HEX_LAYOUT (gtk_hex_layout_get_type ())
 G_DECLARE_FINAL_TYPE (GtkHexLayout, gtk_hex_layout, GTK, HEX_LAYOUT,
diff --git a/src/gtkhex-paste-data.c b/src/gtkhex-paste-data.c
new file mode 100644
index 00000000..b2849ed0
--- /dev/null
+++ b/src/gtkhex-paste-data.c
@@ -0,0 +1,133 @@
+/* vim: ts=4 sw=4 colorcolumn=80
+ * -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* gtkhex-paste-data.c - Paste data for GtkHex
+
+   Copyright © 2021 Logan Rathbone <poprocks gmail com>
+
+   GHex is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   GHex is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GHex; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+   Original GHex Author: Jaka Mocnik <jaka gnu org>
+*/
+
+#include "gtkhex-paste-data.h"
+
+
+/* GObject Definition */
+
+struct _GtkHexPasteData
+{
+       GObject parent_instance;
+
+       guchar *doc_data;
+       guint elems;
+};
+
+G_DEFINE_TYPE (GtkHexPasteData, gtk_hex_paste_data, G_TYPE_OBJECT)
+
+
+/* Helper Functions */
+
+/* Helper function for the copy and paste stuff, since the data returned by
+ * hex_document_get_data is NOT null-temrinated.
+ *
+ * String returned should be freed with g_free.
+ */
+static char *
+doc_data_to_string (const guchar *data, guint len)
+{
+       char *str;
+
+       str = g_malloc (len + 1);
+       memcpy (str, data, len);
+       str[len] = '\0';
+
+       return str;
+}
+
+
+/* FIXME/TODO - this transforms certain problematic characters for copy/paste
+ * to a '?'. Maybe find a home for this guy at some point.
+ */
+#if 0
+{
+       char *cp;
+               for (cp = text; *cp != '\0'; ++cp)
+               {
+                       if (! is_copyable(*cp))
+                               *cp = '?';
+               }
+}
+#endif
+               
+
+/* Constructors and Destructors */
+
+static void
+gtk_hex_paste_data_init (GtkHexPasteData *self)
+{
+}
+
+static void
+gtk_hex_paste_data_class_init (GtkHexPasteDataClass *klass)
+{
+}
+
+
+/* Public Method Definitions */
+
+GtkHexPasteData *
+gtk_hex_paste_data_new (guchar *doc_data, guint elems)
+{
+       GtkHexPasteData *self;
+
+       g_return_val_if_fail (doc_data, NULL);
+       g_return_val_if_fail (elems, NULL);
+
+       self = g_object_new (GTK_TYPE_HEX_PASTE_DATA, NULL);
+
+       self->doc_data = doc_data;
+       self->elems = elems;
+
+       return self;
+}
+
+/* String returned should be freed with g_free. */
+char *
+gtk_hex_paste_data_get_string (GtkHexPasteData *self)
+{
+       char *string;
+
+       g_return_val_if_fail (self->doc_data, NULL);
+       g_return_val_if_fail (self->elems, NULL);
+
+       string = doc_data_to_string (self->doc_data, self->elems);
+
+       return string;
+}
+
+guchar *
+gtk_hex_paste_data_get_doc_data (GtkHexPasteData *self)
+{
+       g_return_val_if_fail (self->doc_data, NULL);
+
+       return self->doc_data;
+}
+
+guint
+gtk_hex_paste_data_get_elems (GtkHexPasteData *self)
+{
+       return self->elems;
+}
diff --git a/src/gtkhex-paste-data.h b/src/gtkhex-paste-data.h
new file mode 100644
index 00000000..3afd9dd3
--- /dev/null
+++ b/src/gtkhex-paste-data.h
@@ -0,0 +1,47 @@
+/* vim: ts=4 sw=4 colorcolumn=80
+ * -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* gtkhex-paste-data.h - declaration of paste data for GtkHex
+
+   Copyright © 2021 Logan Rathbone <poprocks gmail com>
+
+   GHex is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   GHex is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GHex; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+   Original GHex Author: Jaka Mocnik <jaka gnu org>
+*/
+
+#ifndef GTKHEX_PASTE_DATA_H
+#define GTKHEX_PASTE_DATA_H
+
+#include <glib-object.h>
+
+#include <hex-document.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_HEX_PASTE_DATA (gtk_hex_paste_data_get_type ())
+G_DECLARE_FINAL_TYPE (GtkHexPasteData, gtk_hex_paste_data, GTK, HEX_PASTE_DATA,
+               GObject)
+
+/* Method Declarations */
+
+GtkHexPasteData *      gtk_hex_paste_data_new (guchar *doc_data, guint elems);
+char *                         gtk_hex_paste_data_get_string (GtkHexPasteData *self);
+guchar *                       gtk_hex_paste_data_get_doc_data (GtkHexPasteData *self);
+guint                          gtk_hex_paste_data_get_elems (GtkHexPasteData *self);
+
+G_END_DECLS
+
+#endif         /* GTKHEX_PASTE_DATA_H */
diff --git a/src/gtkhex.c b/src/gtkhex.c
index 7cb0b4af..7754841d 100644
--- a/src/gtkhex.c
+++ b/src/gtkhex.c
@@ -31,6 +31,7 @@
 */
 
 #include "gtkhex.h"
+#include "gtkhex-layout-manager.h"
 
 #include <string.h>
 
@@ -114,31 +115,6 @@ struct _GtkHex_AutoHighlight
        GtkHex_AutoHighlight *next, *prev;
 };
 
-/* GtkHexPasteData - allow us to implement copy/pasting which is more
- * flexible than standard C-strings */
-#define GTK_TYPE_HEX_PASTE_DATA (gtk_hex_paste_data_get_type ())
-G_DECLARE_FINAL_TYPE (GtkHexPasteData, gtk_hex_paste_data, GTK, HEX_PASTE_DATA,
-               GObject)
-
-/* GtkHexPasteData - Method Declarations */
-
-static GtkHexPasteData * gtk_hex_paste_data_new (guchar *doc_data,
-               guint elems);
-
-/* GtkHexPasteData - GObject Definition */
-
-struct _GtkHexPasteData
-{
-       GObject parent_instance;
-
-       guchar *doc_data;
-       guint elems;
-};
-
-G_DEFINE_TYPE (GtkHexPasteData, gtk_hex_paste_data, G_TYPE_OBJECT)
-
-/* </GtkHexPasteData Decls> */
-
 
 /* ------------------------------
  * Main GtkHex GObject definition
@@ -225,86 +201,6 @@ static void gtk_hex_update_auto_highlight(GtkHex *gh, GtkHex_AutoHighlight *ahl,
                gboolean delete, gboolean add);
 
 static void recalc_displays(GtkHex *gh);
-static char *doc_data_to_string (const guchar *data, guint len);
-
-/* GtkHexPasteData - Helper Functions */
-
-/* Helper function for the copy and paste stuff, since the data returned by
- * hex_document_get_data is NOT null-temrinated.
- *
- * String returned should be freed with g_free.
- */
-static char *
-doc_data_to_string (const guchar *data, guint len)
-{
-       char *str;
-
-       str = g_malloc (len + 1);
-       memcpy (str, data, len);
-       str[len] = '\0';
-
-       return str;
-}
-
-
-/* FIXME/TODO - this transforms certain problematic characters for copy/paste
- * to a '?'. Maybe find a home for this guy at some point.
- */
-#if 0
-{
-       char *cp;
-               for (cp = text; *cp != '\0'; ++cp)
-               {
-                       if (! is_copyable(*cp))
-                               *cp = '?';
-               }
-}
-#endif
-               
-/* GtkHexPasteData - Constructors and Destructors */
-
-static void
-gtk_hex_paste_data_init (GtkHexPasteData *self)
-{
-}
-
-static void
-gtk_hex_paste_data_class_init (GtkHexPasteDataClass *klass)
-{
-}
-
-
-/* GtkHexPasteData - Method Definitions (all private) */
-
-static GtkHexPasteData *
-gtk_hex_paste_data_new (guchar *doc_data, guint elems)
-{
-       GtkHexPasteData *self;
-
-       g_return_val_if_fail (doc_data, NULL);
-       g_return_val_if_fail (elems, NULL);
-
-       self = g_object_new (GTK_TYPE_HEX_PASTE_DATA, NULL);
-
-       self->doc_data = doc_data;
-       self->elems = elems;
-
-       return self;
-}
-
-/* String returned should be freed with g_free. */
-static char *
-gtk_hex_paste_data_get_string (GtkHexPasteData *self)
-{
-       char *string;
-
-       g_return_val_if_fail (self->doc_data, NULL);
-       g_return_val_if_fail (self->elems, NULL);
-
-       string = doc_data_to_string (self->doc_data, self->elems);
-
-       return string;
-}
 
 /* GtkHex - Method Definitions */
 
@@ -2326,19 +2222,24 @@ gtk_hex_real_paste_from_clipboard (GtkHex *gh,
 
        if (have_hex_paste_data)
        {
+               guchar *doc_data;
+               guint elems;
+
                g_debug("%s: We HAVE our special HexPasteData.",
                                __func__);
 
                paste = GTK_HEX_PASTE_DATA(g_value_get_object (&value));
+               doc_data = gtk_hex_paste_data_get_doc_data (paste);
+               elems = gtk_hex_paste_data_get_elems (paste);
 
                hex_document_set_data (gh->document,
                                gh->cursor_pos,
-                               paste->elems,
+                               elems,
                                0,      /* rep_len (0 to insert w/o replacing; what we want) */
-                               paste->doc_data,
+                               doc_data,
                                TRUE);
 
-               gtk_hex_set_cursor(gh, gh->cursor_pos + paste->elems);
+               gtk_hex_set_cursor (gh, gh->cursor_pos + elems);
        }
        else {
                gdk_clipboard_read_text_async (clipboard,
diff --git a/src/gtkhex.h b/src/gtkhex.h
index 6194c457..48bb1ed1 100644
--- a/src/gtkhex.h
+++ b/src/gtkhex.h
@@ -33,13 +33,21 @@
 #define GTKHEX_H
 
 #include <gtk/gtk.h>
-#include <gdk/gdk.h>
 
 #include <hex-document.h>
-#include <gtkhex-layout-manager.h>
+#include <gtkhex-paste-data.h>
 
 G_BEGIN_DECLS
 
+/* CONSTANTS */
+
+/* how to group bytes? */
+#define GROUP_BYTE 1
+#define GROUP_WORD 2
+#define GROUP_LONG 4
+
+/* GOBJECT DECLARATION */
+
 #define GTK_TYPE_HEX (gtk_hex_get_type ())
 G_DECLARE_FINAL_TYPE(GtkHex, gtk_hex, GTK, HEX, GtkWidget)
 
diff --git a/src/meson.build b/src/meson.build
index b54f165b..56dfee3f 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,12 +1,14 @@
 libghex_sources = [
   'gtkhex.c',
   'hex-document.c',
-  'gtkhex-layout-manager.c'
+  'gtkhex-layout-manager.c',
+  'gtkhex-paste-data.c'
 ]
 
 libghex_headers = [
   'hex-document.h',
-  'gtkhex.h'
+  'gtkhex.h',
+  'gtkhex-paste-data.h'
 ]
 
 libghex_deps = [
@@ -61,9 +63,13 @@ ghex_sources = [
   'ghex-application-window.h',
   'ghex-notebook-tab.c',
   'ghex-notebook-tab.h',
+  'gtkhex-paste-data.c',
+  'gtkhex-paste-data.h',
   'hex-dialog.c',
   'hex-dialog.h',
   'main.c',
+  'paste-special.c',
+  'paste-special.h',
   'preferences.c',
   'preferences.h',
   'print.c',
diff --git a/src/paste-special.c b/src/paste-special.c
new file mode 100644
index 00000000..dd754f42
--- /dev/null
+++ b/src/paste-special.c
@@ -0,0 +1,544 @@
+/* vim: colorcolumn=80 ts=4 sw=4
+ */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* paste-special.c - `Paste special' dialog for GHex
+
+   Copyright © 2021 Logan Rathbone <poprocks gmail com>
+
+   GHex is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   GHex is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GHex; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+   Original GHex Author: Jaka Mocnik <jaka gnu org>
+*/
+
+#include "paste-special.h"
+
+/* DEFINES */
+
+#define PASTE_SPECIAL_RESOURCE "/org/gnome/ghex/paste-special.ui"
+
+/* MACROS */
+
+#define GET_WIDGET(X) \
+       X = GTK_WIDGET(gtk_builder_get_object (builder, #X)); \
+       g_assert (GTK_IS_WIDGET (X))
+
+/* ENUMS AND DATATYPES */
+enum mime_types {
+       NO_MIME,
+       PLAINTEXT_MIME,
+       UTF_PLAINTEXT_MIME,
+       LAST_MIME
+} MimeType;
+
+typedef enum {
+       NO_SUBTYPE,
+       ASCII_PLAINTEXT,
+       HEX_PLAINTEXT
+} MimeSubType;
+
+typedef struct {
+       char *mime_type;
+       char *pretty_name;
+       MimeSubType sub_type;
+} KnownMimeType;
+
+G_DECLARE_FINAL_TYPE (MimeSubTypeLabel, mime_sub_type_label, MIME_SUB_TYPE, LABEL,
+               GtkWidget)
+
+struct _MimeSubTypeLabel {
+       GtkWidget parent_instance;
+
+       GtkWidget *label;
+       KnownMimeType *known_type;
+};
+
+G_DEFINE_TYPE (MimeSubTypeLabel, mime_sub_type_label, GTK_TYPE_WIDGET)
+
+/* STATIC GLOBALS */
+
+static GtkBuilder *builder;
+static GHexApplicationWindow *app_window;
+static GdkClipboard *clipboard;
+static GHashTable *mime_hash;
+static GSList *known_mime[LAST_MIME];
+static GtkWidget *hex_paste_data_label;
+
+/* use GET_WIDGET(X) macro to set these from builder, where
+ * X == var name == builder id name. Don't include quotation marks. */
+
+static GtkWidget *paste_special_dialog;
+
+static GtkWidget *paste_button;
+static GtkWidget *close_button;
+static GtkWidget *paste_special_listbox;
+
+/* MimeSubTypeLabel - Constructors and Destructors */
+
+static void
+mime_sub_type_label_init (MimeSubTypeLabel *self)
+{
+       self->label = gtk_label_new (NULL);
+       gtk_widget_set_hexpand (self->label, TRUE);
+       gtk_widget_set_halign (self->label, GTK_ALIGN_START);
+       gtk_widget_set_parent (self->label, GTK_WIDGET(self));
+}
+
+static void
+mime_sub_type_label_dispose (GObject *object)
+{
+       MimeSubTypeLabel *self = MIME_SUB_TYPE_LABEL (object);
+
+       g_clear_pointer (&self->label, gtk_widget_unparent);
+
+       G_OBJECT_CLASS (mime_sub_type_label_parent_class)->dispose (object);
+}
+
+static void
+mime_sub_type_label_class_init (MimeSubTypeLabelClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       object_class->dispose = mime_sub_type_label_dispose;
+
+       gtk_widget_class_set_layout_manager_type (widget_class,
+                       GTK_TYPE_BOX_LAYOUT);
+}
+
+GtkWidget *
+mime_sub_type_label_new (KnownMimeType *known_type)
+{
+       MimeSubTypeLabel *self = g_object_new (mime_sub_type_label_get_type (),
+                       NULL);
+
+       g_return_val_if_fail (known_type->pretty_name, NULL);
+
+       self->known_type = known_type;
+       gtk_label_set_text (GTK_LABEL(self->label), known_type->pretty_name);
+
+       return GTK_WIDGET(self);
+}
+
+/* PRIVATE FUNCTIONS */
+
+static GString *
+delimited_hex_to_gstring (const char *hex_str)
+{
+       char *copy;
+       char *cp;
+       GString *buf;
+
+       g_return_val_if_fail (hex_str, NULL);
+
+       buf = g_string_new (NULL);
+       copy = g_strdup (hex_str);
+
+       cp = strtok(copy, " ");
+       do {
+               guint hex_char;
+               int scan_ct;
+
+               if (strlen(cp) != 2) goto fail;
+
+               scan_ct = sscanf(cp, "%x", &hex_char);
+
+               if (scan_ct != 1) goto fail;
+               if (hex_char > 255) goto fail;
+
+               g_string_append_printf (buf, "%c", hex_char);
+
+       } while ((cp = strtok(NULL, " ")) != NULL);
+
+       g_free (copy);
+
+       g_debug ("%s: buf->str: %s", __func__, buf->str);
+       return buf;
+
+fail:
+       g_debug ("%s: Invalid hex format detected", __func__);
+       g_free (copy);
+       return NULL;
+}
+
+static void
+delimited_paste_received_cb (GObject *source_object,
+               GAsyncResult *result,
+               gpointer user_data)
+{
+       GtkHex *gh;
+       HexDocument *doc;
+       char *text;
+       GString *buf = NULL;
+
+       /* Get the resulting text of the read operation */
+       text = gdk_clipboard_read_text_finish (GDK_CLIPBOARD(source_object),
+                       result,
+                       NULL);  /* GError */
+
+       buf = delimited_hex_to_gstring (text);
+       if (! buf)
+       {
+               g_debug ("%s: Received invalid delimeter string. Returning.",
+                               __func__);
+               return;
+       }
+
+       gh = ghex_application_window_get_hex (app_window);
+       g_return_if_fail (GTK_IS_HEX (gh));
+
+       doc = gtk_hex_get_document (gh);
+       g_return_if_fail (HEX_IS_DOCUMENT (doc));
+
+       hex_document_set_data (doc,
+                       gtk_hex_get_cursor (gh),
+                       buf->len,
+                       0,      /* rep_len (0 to insert w/o replacing; what we want) */
+                       (guchar *)buf->str,
+                       TRUE);
+}
+
+static void
+delimited_hex_paste (void)
+{
+       gdk_clipboard_read_text_async (clipboard,
+                       NULL,   /* GCancellable *cancellable */
+                       delimited_paste_received_cb,
+                       NULL);  /* user_data */
+}
+
+static void
+create_hex_paste_data_label (void)
+{
+       hex_paste_data_label = gtk_label_new (_("GHex Paste Data"));
+       gtk_widget_set_halign (hex_paste_data_label, GTK_ALIGN_START);
+       gtk_widget_set_hexpand (hex_paste_data_label, TRUE);
+}
+
+static void
+row_activated_cb (GtkListBox *box,
+               GtkListBoxRow *row,
+               gpointer user_data)
+{
+       GtkWidget *child = gtk_list_box_row_get_child (row);
+       g_return_if_fail (GTK_IS_WIDGET(child));
+
+       if (MIME_SUB_TYPE_IS_LABEL(child))
+       {
+               MimeSubTypeLabel *label = MIME_SUB_TYPE_LABEL(child);
+
+               switch (label->known_type->sub_type)
+               {
+                       case NO_SUBTYPE:
+                               gtk_hex_paste_from_clipboard
+                                       (ghex_application_window_get_hex(app_window));
+                               break;
+
+                       case ASCII_PLAINTEXT:
+                               gtk_hex_paste_from_clipboard
+                                       (ghex_application_window_get_hex(app_window));
+                               break;
+
+                       case HEX_PLAINTEXT:
+                               delimited_hex_paste ();
+                               break;
+
+                       default:
+                               g_error ("%s: ERROR - INVALID MIME TYPE. "
+                                               "THIS SHOULDN'T HAPPEN!",
+                                               __func__);
+                               break;
+               }
+       }
+       else
+       {
+               g_debug ("%s: HexPasteData - NOT IMPLEMENTED", __func__);
+       }
+}
+
+static void
+close_clicked_cb (GtkButton *button,
+               gpointer user_data)
+{
+       (void)user_data;        /* unused */
+       g_return_if_fail (GTK_IS_WINDOW (paste_special_dialog));
+
+       gtk_window_destroy (GTK_WINDOW(paste_special_dialog));
+}
+
+static void
+paste_clicked_cb (GtkButton *button,
+               gpointer user_data)
+{
+       GtkListBoxRow *row;
+
+       row = gtk_list_box_get_selected_row (GTK_LIST_BOX(paste_special_listbox));
+       g_signal_emit_by_name (row, "activate");
+}
+
+static void
+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);
+}
+
+static KnownMimeType *
+create_known_mime_sub_type (const char *mime_type, const char *pretty_name,
+               MimeSubType sub_type)
+{
+       KnownMimeType *new_subtype = g_new0 (KnownMimeType, 1);
+
+       new_subtype->mime_type = g_strdup (mime_type);
+       new_subtype->pretty_name = g_strdup (pretty_name);
+       new_subtype->sub_type = sub_type;
+
+       return new_subtype;
+}
+
+static void
+known_mime_sub_type_destroy (KnownMimeType *known_type)
+{
+       if (known_type->mime_type)
+               g_free (known_type->mime_type);
+
+       if (known_type->pretty_name)
+               g_free (known_type->pretty_name);
+
+       g_free (known_type);
+}
+
+static void
+known_mime_list_destroy (gpointer data)
+{
+       GSList *list = data;
+
+       g_slist_free_full (list,
+                       (GDestroyNotify)known_mime_sub_type_destroy);
+}
+
+static guint
+mime_hash_func (gconstpointer key)
+{
+       char *str = g_strdup ((const char *)key);
+       guint hash = NO_MIME;
+       char *cp;
+
+       g_debug ("%s: str: %s", __func__, str);
+
+       /* strip off parameters. */
+       cp = strtok(str, ";");
+
+       g_debug ("%s: cp: %s", __func__, cp);
+
+       if (g_ascii_strcasecmp (str, "text/plain") == 0)
+       {
+               char *utf_str = "charset=utf";
+
+               hash = PLAINTEXT_MIME;
+
+               cp = strtok (NULL, ";");
+
+               g_debug ("%s: cp after 2nd strtok: %s", __func__, cp);
+
+               if (cp && g_ascii_strncasecmp (cp, utf_str, strlen(utf_str)) == 0)
+                       hash = UTF_PLAINTEXT_MIME;
+       }
+       g_free (str);
+       g_debug ("%s: returning: %u", __func__, hash);
+       return hash;
+}
+
+static gboolean
+mime_hash_equal (gconstpointer a,
+               gconstpointer b)
+{
+       char *str1 = g_strdup ((const char *)a);
+       char *str2 = g_strdup ((const char *)b);
+       char *cp1, *cp2;
+       gboolean retval = FALSE;
+
+       g_debug ("%s: str1: %s - str2: %s", __func__, str1, str2);
+
+       if (g_ascii_strcasecmp(str1, str2) == 0)
+               retval = TRUE;
+
+       g_free (str1);
+       g_free (str2);
+
+       g_debug ("%s: returning: %d", __func__, retval);
+       return retval;
+}
+
+static void
+init_mime_hash (void)
+{
+       /* Create mime_hash, clearing any existing */
+
+       if (mime_hash) {
+               g_clear_pointer (&mime_hash, g_hash_table_destroy);
+       }
+       g_assert (mime_hash == NULL);
+
+       mime_hash = g_hash_table_new_full (mime_hash_func,
+                       mime_hash_equal,
+                       g_free,
+                       known_mime_list_destroy);
+       g_assert (mime_hash);
+
+       /* PLAINTEXT_MIME */
+#define LIST known_mime[PLAINTEXT_MIME]
+#define MIME "text/plain"
+
+       /* _destroy will already handle the freeing, but we need to set it to NULL
+        * due to the way GSList works. */
+       if (LIST)
+               LIST = NULL;
+
+       LIST = g_slist_append (LIST, create_known_mime_sub_type (MIME,
+                               _("Plain text (as ASCII)"),
+                               ASCII_PLAINTEXT));
+
+       LIST = g_slist_append (LIST, create_known_mime_sub_type (MIME,
+                               _("Plain text (as delimited hex pairs)"),
+                               HEX_PLAINTEXT));
+
+       g_hash_table_insert (mime_hash,
+                       g_strdup (MIME),
+                       LIST);
+#undef LIST
+#undef MIME
+
+       /* UTF_PLAINTEXT_MIME */
+#define LIST known_mime[UTF_PLAINTEXT_MIME]
+#define MIME "text/plain;charset=utf-8"
+
+       if (LIST)
+               LIST = NULL;
+
+       LIST = g_slist_append (LIST, create_known_mime_sub_type (MIME,
+                               _("Plain text (Unicode)"),
+                               NO_SUBTYPE));
+
+       g_hash_table_insert (mime_hash,
+                       g_strdup (MIME),
+                       LIST);
+
+#undef LIST
+#undef MIME
+}
+
+static void
+setup_signals (void)
+{
+       g_signal_connect (paste_special_listbox, "row-activated",
+                       G_CALLBACK(row_activated_cb), NULL);
+
+       g_signal_connect (paste_button, "clicked",
+                       G_CALLBACK(paste_clicked_cb), NULL);
+
+       g_signal_connect (close_button, "clicked",
+                       G_CALLBACK(close_clicked_cb), NULL);
+}
+
+static void
+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;
+
+       /* Note: this will return NULL if the contents are NOT owned by
+        * the local process, as opposed to _get_formats */
+       provider = gdk_clipboard_get_content (clipboard);
+
+       /* Get all available formats (this will include both known GTypes (such
+        * as our HexPasteData) _and_ other MIME types. */
+       formats = gdk_clipboard_get_formats (clipboard);
+
+       g_debug("%s: formats: %s",
+                       __func__,
+                       gdk_content_formats_to_string (formats));
+
+       mime_types = gdk_content_formats_get_mime_types (formats, NULL);
+
+       g_value_init (&value, GTK_TYPE_HEX_PASTE_DATA);
+       have_hex_paste_data = GDK_IS_CONTENT_PROVIDER (provider) &&
+               gdk_content_provider_get_value (provider, &value, &error);
+
+       if (have_hex_paste_data)
+       {
+               if (! hex_paste_data_label)
+                       create_hex_paste_data_label ();
+
+               gtk_list_box_append (GTK_LIST_BOX(paste_special_listbox),
+                               hex_paste_data_label);
+       }
+
+       for (int i = 0; mime_types[i] != NULL; ++i)
+       {
+               GSList *tracer = NULL;
+
+               g_debug ("%s: checking mime_types[%d]: %s",
+                               __func__, i, mime_types[i]);
+
+               for (tracer = g_hash_table_lookup (mime_hash, mime_types[i]);
+                               tracer != NULL;
+                               tracer = tracer->next)
+               {
+                       GtkWidget *label;
+                       KnownMimeType *type = tracer->data;
+
+                       g_debug ("%s: MATCH - type->pretty_name: %s",
+                                       __func__, type->pretty_name);
+
+                       label = mime_sub_type_label_new (type);
+                       gtk_list_box_append (GTK_LIST_BOX(paste_special_listbox), label);
+               }
+       }
+}
+
+/* PUBLIC FUNCTIONS */
+
+GtkWidget *
+create_paste_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_widgets ();
+       init_mime_hash ();
+       populate_listbox ();
+       setup_signals ();
+
+       gtk_window_set_transient_for (GTK_WINDOW(paste_special_dialog),
+                       GTK_WINDOW(parent));
+
+       return paste_special_dialog;
+}
diff --git a/src/paste-special.h b/src/paste-special.h
new file mode 100644
index 00000000..4c2d6573
--- /dev/null
+++ b/src/paste-special.h
@@ -0,0 +1,43 @@
+/* vim: colorcolumn=80 ts=4 sw=4
+ */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* paste-special.h - Declarations for paste special dialog
+
+   Copyright © 2021 Logan Rathbone <poprocks gmail com>
+
+   GHex is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   GHex is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GHex; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+   Original GHex Author: Jaka Mocnik <jaka gnu org>
+*/
+
+#ifndef PASTE_SPECIAL_H
+#define PASTE_SPECIAL_H
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "gtkhex.h"
+#include "gtkhex-paste-data.h"
+#include "ghex-application-window.h"
+
+G_BEGIN_DECLS
+
+GtkWidget *    create_paste_special_dialog (GHexApplicationWindow *parent,
+               GdkClipboard *clip);
+
+G_END_DECLS
+
+#endif /* PASTE_SPECIAL_H */
diff --git a/src/paste-special.ui b/src/paste-special.ui
new file mode 100644
index 00000000..edaa6455
--- /dev/null
+++ b/src/paste-special.ui
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- vim: ts=2 sw=2
+-->
+<!--
+   Copyright © 2021 Logan Rathbone <poprocks gmail com>
+
+   GHex is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   GHex is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GHex; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+   Original GHex Author: Jaka Mocnik <jaka gnu org>
+-->
+
+<interface>
+       <object class="GtkDialog" id="paste_special_dialog"> <!-- paste_special_dialog -->
+               <property name="title" translatable="yes">Paste Special</property>
+               <property name="default-width">400</property>
+               <property name="default-height">200</property>
+
+               <child internal-child="content_area"> <!-- box -->
+                       <object class="GtkBox" id="content_area_box">
+                               <property name="orientation">vertical</property>
+
+                               <child> <!-- paste_special_listbox -->
+                                       <object class="GtkListBox" id="paste_special_listbox">
+                                               <property name="hexpand">true</property>
+                                               <property name="vexpand">true</property>
+                                       </object>
+                               </child> <!-- /paste_special_listbox-->
+
+                       </object>
+               </child> <!-- /box -->
+
+               <child type="action">
+                       <object class="GtkButton" id="paste_button">
+                               <property name="use-underline">true</property>
+                               <property name="label" translatable="yes">_Paste</property>
+                       </object>
+               </child>
+               <child type="action">
+                       <object class="GtkButton" id="close_button">
+                               <property name="use-underline">true</property>
+                               <property name="label" translatable="yes">_Close</property>
+                       </object>
+               </child>
+
+               <action-widgets>
+                       <action-widget response="accept" default="true">paste_button</action-widget>
+                       <action-widget response="close">close_button</action-widget>
+               </action-widgets>
+       </object> <!-- /paste_special_dialog -->
+
+</interface>
diff --git a/src/preferences.h b/src/preferences.h
index fd6d43f7..de598ad2 100644
--- a/src/preferences.h
+++ b/src/preferences.h
@@ -36,6 +36,7 @@
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
 #include <string.h>
+
 #include <gtkhex.h>    /* for GROUP_* enums */
 
 #include "configuration.h"


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