From f4d50ffa3b4237447093c1cef9cbe945d22ceb84 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 27 Sep 2011 16:23:59 -0700 Subject: [PATCH 1/4] Clean up the drag and drop code and allow ripping off the Dive Profile Linus had used some deprecated interfcase and didn't correctly untangle the new window that he created (hiding it the window... very nifty). I think I'm closer to the real solution with a data structure that keeps track of the components of the new top level window that I need to be able to untangle (and eventually, destroy) at the end. The one error I also can't seem to get rid of is the Clean up the drag and drop code and allow ripping of the Dive Profile Gtk-CRITICAL **: IA__gtk_selection_data_set: assertion `length <= 0' failed Signed-off-by: Dirk Hohndel --- gtk-gui.c | 83 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 30 deletions(-) diff --git a/gtk-gui.c b/gtk-gui.c index e1140cbe0..985a70e04 100644 --- a/gtk-gui.c +++ b/gtk-gui.c @@ -17,7 +17,7 @@ #include "libdivecomputer.h" -GtkWidget *main_window, *divelist_window; +GtkWidget *main_window; GtkWidget *main_vbox; GtkWidget *error_info_bar; GtkWidget *error_label; @@ -460,34 +460,50 @@ static void switch_page(GtkNotebook *notebook, gint arg1, gpointer user_data) repaint_dive(); } -static const char notebook_group[] = "123"; -#define GRP_ID ((void *)notebook_group) +static const char notebook_name[] = "123"; + +typedef struct { + char *name; + GtkWidget *widget; + GtkWidget *box; + gulong delete_handler; + gulong destroy_handler; +} notebook_data_t; + +static notebook_data_t nbd[2]; /* we rip at most two notebook pages off */ static GtkNotebook *create_new_notebook_window(GtkNotebook *source, GtkWidget *page, gint x, gint y, gpointer data) { GtkWidget *win, *notebook, *vbox; + notebook_data_t *nbdp; - /* We don't detatch twice */ - if (divelist_window) + /* pick the right notebook page data and return if both are detached */ + if (nbd[0].widget == NULL) + nbdp = nbd; + else if (nbd[1].widget == NULL) + nbdp = nbd + 1; + else return NULL; - divelist_window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_title(GTK_WINDOW(win), "Dive List"); + nbdp->name = strdup(gtk_widget_get_name(page)); + nbdp->widget = win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(win), nbdp->name); gtk_window_move(GTK_WINDOW(win), x, y); /* Destroying the dive list will kill the application */ - g_signal_connect(G_OBJECT(win), "delete-event", G_CALLBACK(on_delete), NULL); - g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(on_destroy), NULL); + nbdp->delete_handler = g_signal_connect(G_OBJECT(win), "delete-event", G_CALLBACK(on_delete), NULL); + nbdp->destroy_handler = g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(on_destroy), NULL); - vbox = gtk_vbox_new(FALSE, 0); + nbdp->box = vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(win), vbox); notebook = gtk_notebook_new(); - gtk_notebook_set_group(GTK_NOTEBOOK(notebook), GRP_ID); + gtk_widget_set_name(notebook, nbdp->name); + gtk_notebook_set_group_name(GTK_NOTEBOOK(notebook), notebook_name); gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 6); - gtk_widget_set_size_request(notebook, 350, 250); + gtk_widget_set_size_request(notebook, 450, 350); gtk_widget_show_all(win); return GTK_NOTEBOOK(notebook); @@ -499,8 +515,8 @@ static void drag_cb(GtkWidget *widget, GdkDragContext *context, guint info, guint time, gpointer user_data) { - GtkWidget *source, *target; - GtkWidget *tab; + GtkWidget *source; + notebook_data_t *nbdp; /* * We don't actually really *use* this yet, but Dirk wants to @@ -508,23 +524,26 @@ static void drag_cb(GtkWidget *widget, GdkDragContext *context, * this all to figure out which window we're talking about. */ source = gtk_drag_get_source_widget(context); - target = (GtkWidget *) user_data; - tab = *(GtkWidget **)selection_data->data; + if (! strcmp(nbd[0].name,gtk_widget_get_name(source))) + nbdp = nbd; + else if (! strcmp(nbd[1].name,gtk_widget_get_name(source))) + nbdp = nbd + 1; + else + /* HU? */ + return; gtk_drag_finish(context, TRUE, TRUE, time); - /* - * Horrible, horrible hack. We hide the old divelist window, and - * set it to NULL. So if we drag the thing back out, we'll create - * a new window. Ugh. - * - * Actually destroying the divelist window triggers the whole - * destroy callback, which we don't want. - */ - if (source != target) { - gtk_widget_hide(divelist_window); - divelist_window = NULL; - } + /* we no longer need the widget - but getting rid of this is hard; + * remove the signal handler, remove the notebook from the box + * then destroy the widget (and clear out our data structure) */ + g_signal_handler_disconnect(nbdp->widget,nbdp->delete_handler); + g_signal_handler_disconnect(nbdp->widget,nbdp->destroy_handler); + gtk_container_remove(GTK_CONTAINER(nbdp->box), source); + gtk_widget_destroy(nbdp->widget); + nbdp->widget = NULL; + free(nbdp->name); + nbdp->name = NULL; } void init_ui(int argc, char **argv) @@ -579,21 +598,25 @@ void init_ui(int argc, char **argv) /* Notebook for dive info vs profile vs .. */ notebook = gtk_notebook_new(); gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 6); - gtk_notebook_set_group(GTK_NOTEBOOK(notebook), GRP_ID); - gtk_notebook_set_window_creation_hook(create_new_notebook_window, NULL, NULL); + gtk_notebook_set_group_name(GTK_NOTEBOOK(notebook), notebook_name); + g_signal_connect(notebook, "create-window", G_CALLBACK(create_new_notebook_window), NULL); gtk_drag_dest_set(notebook, GTK_DEST_DEFAULT_ALL, ¬ebook_target, 1, GDK_ACTION_MOVE); g_signal_connect(notebook, "drag-data-received", G_CALLBACK(drag_cb), notebook); g_signal_connect(notebook, "switch-page", G_CALLBACK(switch_page), NULL); /* Create the actual divelist */ dive_list = dive_list_create(); + gtk_widget_set_name(dive_list, "Dive List"); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dive_list, gtk_label_new("Dive List")); gtk_notebook_set_tab_detachable(GTK_NOTEBOOK(notebook), dive_list, 1); gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(notebook), dive_list, 1); /* Frame for dive profile */ dive_profile = dive_profile_widget(); + gtk_widget_set_name(dive_profile, "Dive Profile"); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dive_profile, gtk_label_new("Dive Profile")); + gtk_notebook_set_tab_detachable(GTK_NOTEBOOK(notebook), dive_profile, 1); + gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(notebook), dive_profile, 1); /* Frame for extended dive info */ dive_info = extended_dive_info_widget(); From fda230235ce8879736803dc9e0ae918ce471201f Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 27 Sep 2011 17:03:15 -0700 Subject: [PATCH 2/4] Use the correct signal to avoid Gtk-CRITICAL error message We used the wrong signal - "data-drag-received" is intended to check whether the target will accept the drop. What we want is the "drag-drop" signal which tells the widget that something was dropped on it. Also fix an embarrassing lack of NULL pointer checks in my string comparisons... Signed-off-by: Dirk Hohndel --- gtk-gui.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gtk-gui.c b/gtk-gui.c index 985a70e04..701e8fe46 100644 --- a/gtk-gui.c +++ b/gtk-gui.c @@ -524,9 +524,9 @@ static void drag_cb(GtkWidget *widget, GdkDragContext *context, * this all to figure out which window we're talking about. */ source = gtk_drag_get_source_widget(context); - if (! strcmp(nbd[0].name,gtk_widget_get_name(source))) + if (nbd[0].name && ! strcmp(nbd[0].name,gtk_widget_get_name(source))) nbdp = nbd; - else if (! strcmp(nbd[1].name,gtk_widget_get_name(source))) + else if (nbd[1].name && ! strcmp(nbd[1].name,gtk_widget_get_name(source))) nbdp = nbd + 1; else /* HU? */ @@ -601,7 +601,7 @@ void init_ui(int argc, char **argv) gtk_notebook_set_group_name(GTK_NOTEBOOK(notebook), notebook_name); g_signal_connect(notebook, "create-window", G_CALLBACK(create_new_notebook_window), NULL); gtk_drag_dest_set(notebook, GTK_DEST_DEFAULT_ALL, ¬ebook_target, 1, GDK_ACTION_MOVE); - g_signal_connect(notebook, "drag-data-received", G_CALLBACK(drag_cb), notebook); + g_signal_connect(notebook, "drag-drop", G_CALLBACK(drag_cb), notebook); g_signal_connect(notebook, "switch-page", G_CALLBACK(switch_page), NULL); /* Create the actual divelist */ From 79a866f5b4d51efc15286059ac7e4322b6008eb3 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 27 Sep 2011 17:11:08 -0700 Subject: [PATCH 3/4] Linus would like to be less on the bleeding edge of Gtk+ So we go back to the old interfaces to identify the notebook as part of one group - the one that was just recently deprecated Signed-off-by: Dirk Hohndel --- gtk-gui.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gtk-gui.c b/gtk-gui.c index 701e8fe46..140129f60 100644 --- a/gtk-gui.c +++ b/gtk-gui.c @@ -460,8 +460,8 @@ static void switch_page(GtkNotebook *notebook, gint arg1, gpointer user_data) repaint_dive(); } -static const char notebook_name[] = "123"; - +static const char notebook_group[] = "123"; +#define GRP_ID ((void *)notebook_group) typedef struct { char *name; GtkWidget *widget; @@ -500,8 +500,8 @@ static GtkNotebook *create_new_notebook_window(GtkNotebook *source, gtk_container_add(GTK_CONTAINER(win), vbox); notebook = gtk_notebook_new(); + gtk_notebook_set_group(GTK_NOTEBOOK(notebook), GRP_ID); gtk_widget_set_name(notebook, nbdp->name); - gtk_notebook_set_group_name(GTK_NOTEBOOK(notebook), notebook_name); gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 6); gtk_widget_set_size_request(notebook, 450, 350); @@ -598,7 +598,7 @@ void init_ui(int argc, char **argv) /* Notebook for dive info vs profile vs .. */ notebook = gtk_notebook_new(); gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 6); - gtk_notebook_set_group_name(GTK_NOTEBOOK(notebook), notebook_name); + gtk_notebook_set_group(GTK_NOTEBOOK(notebook), GRP_ID); g_signal_connect(notebook, "create-window", G_CALLBACK(create_new_notebook_window), NULL); gtk_drag_dest_set(notebook, GTK_DEST_DEFAULT_ALL, ¬ebook_target, 1, GDK_ACTION_MOVE); g_signal_connect(notebook, "drag-drop", G_CALLBACK(drag_cb), notebook); From d37d7211ca7ab810987e7eae785ec25484c1e34c Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Tue, 27 Sep 2011 19:47:19 -0700 Subject: [PATCH 4/4] The notebook pages can only be dropped back into the main notebook Disable the secondary notebooks that are created when ripping off a page (dive_list or dive_profile) as drop targets for other pages. Also fix the incorrect arguments for the drag callback function. Signed-off-by: Dirk Hohndel --- gtk-gui.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/gtk-gui.c b/gtk-gui.c index 140129f60..87cd8bf87 100644 --- a/gtk-gui.c +++ b/gtk-gui.c @@ -502,6 +502,8 @@ static GtkNotebook *create_new_notebook_window(GtkNotebook *source, notebook = gtk_notebook_new(); gtk_notebook_set_group(GTK_NOTEBOOK(notebook), GRP_ID); gtk_widget_set_name(notebook, nbdp->name); + /* disallow drop events */ + gtk_drag_dest_set(notebook, 0, NULL, 0, 0); gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 6); gtk_widget_set_size_request(notebook, 450, 350); @@ -510,19 +512,12 @@ static GtkNotebook *create_new_notebook_window(GtkNotebook *source, } static void drag_cb(GtkWidget *widget, GdkDragContext *context, - gint x, gint y, - GtkSelectionData *selection_data, - guint info, guint time, + gint x, gint y, guint time, gpointer user_data) { GtkWidget *source; notebook_data_t *nbdp; - /* - * We don't actually really *use* this yet, but Dirk wants to - * do all the tabs as detatched tabs, and we'd need to use - * this all to figure out which window we're talking about. - */ source = gtk_drag_get_source_widget(context); if (nbd[0].name && ! strcmp(nbd[0].name,gtk_widget_get_name(source))) nbdp = nbd;