[ghex/gtk4-port: 72/91] Basic copy/paste-special up and running.
- From: Logan Rathbone <larathbone src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [ghex/gtk4-port: 72/91] Basic copy/paste-special up and running.
- Date: Thu, 12 Aug 2021 23:35:11 +0000 (UTC)
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]