[ghex/gtk4-port: 70/91] Separate HexPasteData and initial impl of paste-special
- From: Logan Rathbone <larathbone src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [ghex/gtk4-port: 70/91] Separate HexPasteData and initial impl of paste-special
- Date: Thu, 12 Aug 2021 23:35:11 +0000 (UTC)
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]