/* OpenIDA - Outil d'analyse de fichiers binaires * gtkdockpanel.c - manipulation et affichage de panneaux dockables * * Copyright (C) 2009-2012 Cyrille Bagard * * This file is part of OpenIDA. * * OpenIDA 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 3 of the License, or * (at your option) any later version. * * OpenIDA 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 Foobar. If not, see . */ #include "gtkdockpanel.h" #include #include #include "gtkdropwindow.h" #include "iodamarshal.h" /* Valide ou non le terme d'un "Drag and drop". */ static gboolean gtk_dock_panel_drag_drop_cb(GtkDockPanel *, GdkDragContext *, gint, gint, guint, gpointer); /* Procède au "Drag and drop" effectif. */ static void gtk_dock_panel_drag_data_received_cb(GtkDockPanel *, GdkDragContext *, gint, gint, GtkSelectionData *, guint, guint, gpointer); /* Accompagne le déplacement sur une cible de "Drag and drop". */ static gboolean gtk_dock_panel_drag_motion_cb(GtkDockPanel *, GdkDragContext *, gint, gint, guint, gpointer); /* Note la fin des visites sur une cible de "Drag and drop". */ static void gtk_dock_panel_drag_leave_cb(GtkDockPanel *, GdkDragContext *, guint, gpointer); /* Procède au démarrage d'un "Drag and drop". */ static void gtk_dock_panel_drag_begin_cb(GtkDockPanel *, GdkDragContext *, gpointer); /* Procède à l'envoi depuis la source à la destination. */ static void gtk_dock_panel_drag_data_get_cb(GtkDockPanel *, GdkDragContext *, GtkSelectionData *, guint, guint, gpointer ); /* Marque l'arrêt d'un "Drag and drop". */ static void gtk_dock_panel_drag_end_cb(GtkDockPanel *, GdkDragContext *, gpointer); /* Nettoie les traces d'un "Drag and drop". */ //static void gtk_dock_panel_drag_data_delete_cb(GtkDockPanel *, GdkDragContext *, gpointer); /* Ajoute un paquet d'informations à la station dockable. */ static void _gtk_dock_panel_add_item(GtkDockPanel *, GDockItem *, gint); /* Remplace le panneau d'un membre actuellement affiché. */ static void on_dock_item_content_changed(GDockItem *, GtkWidget *, GtkWidget *, GtkDockPanel *); /* Supprime un paquet d'informations à la station dockable. */ static void _gtk_dock_panel_remove_item(GtkDockPanel *, GDockItem *, GtkWidget *); /* Met à jour le titre du support de panneaux dockables. */ static gboolean gtk_dock_panel_update_title(GtkNotebook *, gpointer *, guint, gpointer); /******************************************************************************/ #define _BYTE 8 #define _WORD 16 #define _DWORD 32 /******************************************************************************/ /* Define a list of data types called "targets" that a destination widget will * accept. The string type is arbitrary, and negotiated between DnD widgets by * the developer. An enum or GQuark can serve as the integer target id. */ enum { TARGET_INT32, TARGET_STRING, TARGET_DOCKITEM, TARGET_ROOTWIN }; /* datatype (string), restrictions on DnD (GtkTargetFlags), datatype (int) */ static GtkTargetEntry target_list[] = { { "INTEGER", 0, TARGET_INT32 }, { "STRING", 0, TARGET_STRING }, { "OpenIDA/dock-item", 0, TARGET_DOCKITEM }, { "application/x-rootwindow-drop", 0, TARGET_ROOTWIN } }; static guint n_targets = G_N_ELEMENTS (target_list); /* Détermine le type du composant d'affichage des morceaux. */ G_DEFINE_TYPE(GtkDockPanel, gtk_dock_panel, GTK_TYPE_VBOX) /****************************************************************************** * * * Paramètres : class = classe GTK à initialiser. * * * * Description : Procède à l'initialisation de l'afficheur de morceaux. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_dock_panel_class_init(GtkDockPanelClass *class) { g_signal_new("switch-item", GTK_TYPE_DOCK_PANEL, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GtkDockPanelClass, switch_item), NULL, NULL, g_cclosure_user_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_DOCK_ITEM); } /****************************************************************************** * * * Paramètres : dpanel = composant GTK à initialiser. * * * * Description : Procède à l'initialisation de la station dockable. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_dock_panel_init(GtkDockPanel *dpanel) { GtkWidget *eventbox1; GtkWidget *hbox1; GtkWidget *button1; GtkWidget *image1; GtkWidget *button2; GtkWidget *image2; eventbox1 = gtk_event_box_new (); gtk_widget_show (eventbox1); gtk_box_pack_start (GTK_BOX (dpanel), eventbox1, FALSE, TRUE, 0); hbox1 = gtk_hbox_new (FALSE, 0); gtk_widget_show (hbox1); gtk_container_add (GTK_CONTAINER (eventbox1), hbox1); dpanel->title = GTK_LABEL(gtk_label_new(("titre"))); gtk_widget_show(GTK_WIDGET(dpanel->title)); gtk_box_pack_start(GTK_BOX (hbox1), GTK_WIDGET(dpanel->title), TRUE, TRUE, 0); gtk_label_set_use_markup(GTK_LABEL(dpanel->title), TRUE); gtk_misc_set_alignment(GTK_MISC(dpanel->title), 0, 0.5); button1 = gtk_button_new (); gtk_widget_show (button1); gtk_box_pack_start (GTK_BOX (hbox1), button1, FALSE, FALSE, 0); gtk_button_set_relief (GTK_BUTTON (button1), GTK_RELIEF_NONE); image1 = gtk_image_new_from_stock ("gtk-media-play", GTK_ICON_SIZE_MENU); gtk_widget_show (image1); gtk_container_add (GTK_CONTAINER (button1), image1); gtk_widget_set_size_request (image1, 10, 10); button2 = gtk_button_new (); gtk_widget_show (button2); gtk_box_pack_start (GTK_BOX (hbox1), button2, FALSE, FALSE, 0); gtk_button_set_relief (GTK_BUTTON (button2), GTK_RELIEF_NONE); image2 = gtk_image_new_from_stock ("gtk-close", GTK_ICON_SIZE_MENU); gtk_widget_show (image2); gtk_container_add (GTK_CONTAINER (button2), image2); gtk_widget_set_size_request (image2, 10, 10); dpanel->notebook = gtk_notebook_new (); gtk_widget_show (dpanel->notebook); gtk_box_pack_start (GTK_BOX (dpanel), dpanel->notebook, TRUE, TRUE, 0); gtk_notebook_set_show_border (GTK_NOTEBOOK (dpanel->notebook), FALSE); gtk_notebook_set_tab_pos (GTK_NOTEBOOK (dpanel->notebook), GTK_POS_BOTTOM); gtk_notebook_set_scrollable (GTK_NOTEBOOK (dpanel->notebook), TRUE); /* Make the "well label" a DnD destination. */ gtk_drag_dest_set ( GTK_WIDGET(dpanel), /* widget that will accept a drop */ GTK_DEST_DEFAULT_MOTION /* default actions for dest on DnD */ | GTK_DEST_DEFAULT_HIGHLIGHT, target_list, /* lists of target to support */ n_targets, /* size of list */ GDK_ACTION_COPY /* what to do with data after dropped */ ); /* Make the "coin button" a DnD source. */ /* Why doesn't GtkLabel work here? */ gtk_drag_source_set ( GTK_WIDGET(dpanel), /* widget will be drag-able */ GDK_BUTTON1_MASK, /* modifier that will start a drag */ target_list, /* lists of target to support */ n_targets, /* size of list */ GDK_ACTION_COPY /* what to do with data after dropped */ ); /* Côté destination */ g_signal_connect(dpanel, "drag-drop", G_CALLBACK(gtk_dock_panel_drag_drop_cb), NULL); g_signal_connect(dpanel, "drag-data-received", G_CALLBACK(gtk_dock_panel_drag_data_received_cb), NULL); g_signal_connect(dpanel, "drag-motion", G_CALLBACK(gtk_dock_panel_drag_motion_cb), NULL); g_signal_connect(dpanel, "drag-leave", G_CALLBACK(gtk_dock_panel_drag_leave_cb), NULL); /* Côté source */ g_signal_connect(dpanel, "drag-begin", G_CALLBACK(gtk_dock_panel_drag_begin_cb), NULL); g_signal_connect(dpanel, "drag-data-get", G_CALLBACK(gtk_dock_panel_drag_data_get_cb), NULL); g_signal_connect(dpanel, "drag-end", G_CALLBACK(gtk_dock_panel_drag_end_cb), NULL); //g_signal_connect(dpanel, "drag-data-delete", G_CALLBACK(gtk_dock_panel_drag_data_delete_cb), NULL); g_signal_connect(dpanel->notebook, "switch-page", G_CALLBACK(gtk_dock_panel_update_title), dpanel); dpanel->dropwin = gtk_drop_window_new(); /* Make the "well label" a DnD destination. */ gtk_drag_dest_set ( dpanel->dropwin, /* widget that will accept a drop */ GTK_DEST_DEFAULT_MOTION /* default actions for dest on DnD */ | GTK_DEST_DEFAULT_HIGHLIGHT, target_list, /* lists of target to support */ n_targets, /* size of list */ GDK_ACTION_COPY /* what to do with data after dropped */ ); } /****************************************************************************** * * * Paramètres : - * * * * Description : Crée un nouveau composant pour station dockable. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ GtkWidget *gtk_dock_panel_new(void) { return g_object_new(GTK_TYPE_DOCK_PANEL, NULL); } /****************************************************************************** * * * Paramètres : dpanel = composant à l'origine de la manoeuvre. * * context = contexte de l'opération de "Drag and drop". * * x = abscisse du pointeur relaché. * * y = ordonnée du pointeur relaché. * * time = date de l'opération. * * data = adresse non utilisée ici. * * * * Description : Valide ou non le terme d'un "Drag and drop". * * * * Retour : TRUE si l'opération peut continuer, FALSE sinon. * * * * Remarques : - * * * ******************************************************************************/ static gboolean gtk_dock_panel_drag_drop_cb(GtkDockPanel *dpanel, GdkDragContext *context, gint x, gint y, guint time, gpointer data) { gboolean result; /* Ordre à retourner */ GdkAtom target; /* Type d'élément à déplacer */ result = (context->targets != NULL); printf(" ## DRAG DROP ## %p\n", dpanel); if (context->targets != NULL) { target = GDK_POINTER_TO_ATOM(g_list_nth_data(context->targets, TARGET_DOCKITEM)); gtk_drag_get_data(GTK_WIDGET(dpanel), context, target, time); } return result; } /****************************************************************************** * * * Paramètres : dpanel = composant à l'origine de la manoeuvre. * * context = contexte de l'opération de "Drag and drop". * * x = abscisse du pointeur relaché. * * y = ordonnée du pointeur relaché. * * selection = réceptacle pour la transmission. * * target = type de données demandé. * * time = date de l'opération. * * data = adresse non utilisée ici. * * * * Description : Procède au "Drag and drop" effectif. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_dock_panel_drag_data_received_cb(GtkDockPanel *dpanel, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection, guint target, guint time, gpointer data) { GDockItem *ditem; /* Elément transféré */ gboolean success; /* Bilan de l'opération */ success = FALSE; if (selection != NULL && selection->length >= 0) switch (target) { case TARGET_DOCKITEM: success = (selection->length == sizeof(GDockItem *)); printf(" ## DRAG DATA RCV ## %p\n", dpanel); printf(" -- source :: %p\n", gtk_drag_get_source_widget(context)); printf(" -- dest :: %p\n", dpanel); if (success) { ditem = G_DOCK_ITEM(*((GDockItem **)selection->data)); printf(" :: get ? %p - %d\n", ditem, G_IS_DOCK_ITEM(ditem)); gtk_dock_panel_remove_item(gtk_drag_get_source_widget(context), ditem); gtk_dock_panel_add_item(dpanel, ditem); } break; } gtk_drag_finish(context, success, context->action == GDK_ACTION_MOVE, time); } /****************************************************************************** * * * Paramètres : dpanel = composant à l'origine de la manoeuvre. * * context = contexte de l'opération de "Drag and drop". * * x = abscisse du pointeur relaché. * * y = ordonnée du pointeur relaché. * * time = date de l'opération. * * data = adresse non utilisée ici. * * * * Description : Accompagne le déplacement sur une cible de "Drag and drop". * * * * Retour : TRUE pour continuer la propagation, FALSE sinon. * * * * Remarques : - * * * ******************************************************************************/ static gboolean gtk_dock_panel_drag_motion_cb(GtkDockPanel *dpanel, GdkDragContext *context, gint x, gint y, guint time, gpointer data) { gint new_x; /* Abscisse de la fenêtre */ gint new_y; /* Ordonnée de la fenêtre */ GtkRequisition req; /* Taille actuelle du panneau */ if (!gtk_widget_get_visible(dpanel->dropwin)) { gdk_window_get_origin(GTK_WIDGET(dpanel)->window, &new_x, &new_y); new_x += GTK_WIDGET(dpanel)->allocation.x + (GTK_WIDGET(dpanel)->allocation.width - 89) / 2; new_y += GTK_WIDGET(dpanel)->allocation.y + (GTK_WIDGET(dpanel)->allocation.height - 89) / 2; gtk_widget_set_uposition(dpanel->dropwin, new_x, new_y); gtk_widget_show(dpanel->dropwin); } return FALSE; } /****************************************************************************** * * * Paramètres : dpanel = composant à l'origine de la manoeuvre. * * context = contexte de l'opération de "Drag and drop". * * x = abscisse du pointeur relaché. * * y = ordonnée du pointeur relaché. * * time = date de l'opération. * * data = adresse non utilisée ici. * * * * Description : Note la fin des visites sur une cible de "Drag and drop". * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_dock_panel_drag_leave_cb(GtkDockPanel *dpanel, GdkDragContext *context, guint time, gpointer data) { gtk_widget_hide(dpanel->dropwin); } /****************************************************************************** * * * Paramètres : dpanel = composant à l'origine de la manoeuvre. * * context = contexte de l'opération de "Drag and drop". * * data = adresse non utilisée ici. * * * * Description : Procède au démarrage d'un "Drag and drop". * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_dock_panel_drag_begin_cb(GtkDockPanel *dpanel, GdkDragContext *context, gpointer data) { printf(" ## DRAG BEGIN ## %p\n", dpanel); } /****************************************************************************** * * * Paramètres : dpanel = composant à l'origine de la manoeuvre. * * context = contexte de l'opération de "Drag and drop". * * selection = réceptacle pour la transmission. * * target = type de données demandé. * * time = date de l'opération. * * data = adresse non utilisée ici. * * * * Description : Procède à l'envoi depuis la source à la destination. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_dock_panel_drag_data_get_cb(GtkDockPanel *dpanel, GdkDragContext *context, GtkSelectionData *selection, guint target, guint time, gpointer data) { gint current; /* Indice de l'onglet courant */ GDockItem *ditem; /* Elément à transférer */ switch (target) { case TARGET_DOCKITEM: printf(" ## DRAG GET DATA ## %p\n", dpanel); current = gtk_notebook_get_current_page(dpanel->notebook); ditem = G_DOCK_ITEM(g_list_nth_data(dpanel->ditems, current)); printf(" %d nth item is %p\n", current, ditem); printf(" :: set ? %p - %d\n", ditem, G_IS_DOCK_ITEM(ditem)); gtk_selection_data_set(selection, selection->target, 32, (guchar *)&ditem, sizeof(GDockItem *)); break; } } /****************************************************************************** * * * Paramètres : dpanel = composant à l'origine de la manoeuvre. * * context = contexte de l'opération de "Drag and drop". * * data = adresse non utilisée ici. * * * * Description : Marque l'arrêt d'un "Drag and drop". * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void gtk_dock_panel_drag_end_cb(GtkDockPanel *dpanel, GdkDragContext *context, gpointer data) { printf(" ## DRAG END ## %p\n", dpanel); } /****************************************************************************** * * * Paramètres : dpanel = composant dont le contenu est à parcourir. * * name = désignation humaine du membre à retrouver. * * * * Description : Retrouve un membre du panneau d'après son nom. * * * * Retour : Membre trouvé ou NULL si aucun. * * * * Remarques : - * * * ******************************************************************************/ GDockItem *gtk_dock_panel_item_from_name(GtkDockPanel *dpanel, const char *name) { GDockItem *result; /* Trouvaille à remonter */ GList *iter; /* Boucle de parcours */ const char *tmp; /* Autre nom à consulter */ result = NULL; for (iter = dpanel->ditems; iter != NULL && result == NULL; iter = g_list_next(iter)) { tmp = g_dock_item_get_name(G_DOCK_ITEM(iter->data)); if (strcmp(name, tmp) == 0) result = G_DOCK_ITEM(iter->data); } return result; } /****************************************************************************** * * * Paramètres : dpanel = composant GTK à compléter. * * ditem = nouvel élément à intégrer. * * * * Description : Ajoute un paquet d'informations à la station dockable. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void gtk_dock_panel_add_item(GtkDockPanel *dpanel, GDockItem *ditem) { _gtk_dock_panel_add_item(dpanel, ditem, -1); } /****************************************************************************** * * * Paramètres : dpanel = composant GTK à compléter. * * ditem = nouvel élément à intégrer. * * position = point d'insertion (-1 pour la fin). * * * * Description : Ajoute un paquet d'informations à la station dockable. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void _gtk_dock_panel_add_item(GtkDockPanel *dpanel, GDockItem *ditem, gint position) { GtkWidget *label; /* Etiquette d'onglet */ dpanel->ditems = g_list_insert(dpanel->ditems, ditem, position); printf("[add %p to %p] list len :: %u\n", ditem, dpanel, g_list_length(dpanel->ditems)); label = gtk_label_new(g_dock_item_get_name(ditem)); gtk_widget_show(label); gtk_notebook_insert_page(dpanel->notebook, g_dock_item_get_panel(ditem), label, position); gtk_notebook_set_show_tabs(dpanel->notebook, g_list_length(dpanel->ditems) > 1); g_signal_connect(ditem, "content-changed", G_CALLBACK(on_dock_item_content_changed), dpanel); } /****************************************************************************** * * * Paramètres : ditem = composant GTK déjà mis à jour. * * old = ancien panneau retiré. * * new = nouveau panneau présenté. * * dpanel = composant GTK à mettre à jour. * * * * Description : Remplace le panneau d'un membre actuellement affiché. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_dock_item_content_changed(GDockItem *ditem, GtkWidget *old, GtkWidget *new, GtkDockPanel *dpanel) { gint position; /* Position de l'onglet à maj */ position = gtk_notebook_page_num(dpanel->notebook, old); g_signal_handlers_disconnect_by_func(dpanel->notebook, G_CALLBACK(gtk_dock_panel_update_title), dpanel); //g_object_ref(G_OBJECT(ditem)); _gtk_dock_panel_remove_item(dpanel, ditem, old); _gtk_dock_panel_add_item(dpanel, ditem, position); //g_object_unref(G_OBJECT(ditem)); gtk_notebook_set_current_page(dpanel->notebook, position); g_signal_connect(dpanel->notebook, "switch-page", G_CALLBACK(gtk_dock_panel_update_title), dpanel); } /****************************************************************************** * * * Paramètres : dpanel = composant GTK à mettre à jour. * * ditem = nouvel élément à sortir. * * * * Description : Supprime un paquet d'informations à la station dockable. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void gtk_dock_panel_remove_item(GtkDockPanel *dpanel, GDockItem *ditem) { gint pos; /* Position de l'élément visé */ g_signal_handlers_disconnect_by_func(ditem, G_CALLBACK(on_dock_item_content_changed), dpanel); pos = g_list_index(dpanel->ditems, ditem); dpanel->ditems = g_list_remove(dpanel->ditems, ditem); printf("[rem %p from %p] list len :: %u\n", ditem, dpanel, g_list_length(dpanel->ditems)); gtk_widget_ref(g_dock_item_get_panel(ditem)); gtk_container_remove(GTK_CONTAINER(dpanel->notebook), g_dock_item_get_panel(ditem)); //gtk_notebook_remove_page(dpanel->notebook, pos); g_object_unref(G_OBJECT(ditem)); gtk_notebook_set_show_tabs(dpanel->notebook, g_list_length(dpanel->ditems) > 1); } /****************************************************************************** * * * Paramètres : dpanel = composant GTK à mettre à jour. * * ditem = nouvel élément à sortir. * * panel = panneau GTK de l'élément à supprimer. * * * * Description : Supprime un paquet d'informations à la station dockable. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void _gtk_dock_panel_remove_item(GtkDockPanel *dpanel, GDockItem *ditem, GtkWidget *panel) { gint pos; /* Position de l'élément visé */ g_signal_handlers_disconnect_by_func(ditem, G_CALLBACK(on_dock_item_content_changed), dpanel); pos = g_list_index(dpanel->ditems, ditem); dpanel->ditems = g_list_remove(dpanel->ditems, ditem); printf("[rem %p from %p] list len :: %u\n", ditem, dpanel, g_list_length(dpanel->ditems)); gtk_widget_ref(panel); gtk_container_remove(GTK_CONTAINER(dpanel->notebook), panel); //gtk_notebook_remove_page(dpanel->notebook, pos); gtk_notebook_set_show_tabs(dpanel->notebook, g_list_length(dpanel->ditems) > 1); } /****************************************************************************** * * * Paramètres : notebook = support à l'origine de la mise à jour. * * page = onglet mis en avant. * * index = indice de l'onglet actuellement actif. * * data = adresse du conteneur supérieur. * * * * Description : Met à jour le titre du support de panneaux dockables. * * * * Retour : TRUE ? * * * * Remarques : - * * * ******************************************************************************/ static gboolean gtk_dock_panel_update_title(GtkNotebook *notebook, gpointer *page, guint index, gpointer data) { GDockItem *ditem; /* Elément nouvellement actif */ const gchar *desc; /* Description à afficher */ char *str; /* Valeur finale reconstituée */ //printf("[%p] list len :: %u / %u\n", data, index, g_list_length(GTK_DOCK_PANEL(data)->ditems)); if (index >= g_list_length(GTK_DOCK_PANEL(data)->ditems)) return FALSE; //printf(" >> ditem = %p\n", g_list_nth_data(GTK_DOCK_PANEL(data)->ditems, index)); //printf(" >> index :: %u vs %d\n", index, gtk_notebook_get_current_page(GTK_DOCK_PANEL(data)->notebook)); ditem = G_DOCK_ITEM(g_list_nth_data(GTK_DOCK_PANEL(data)->ditems, index)); desc = g_dock_item_get_desc(ditem); str = calloc(strlen("") + strlen(desc) + strlen("") + 1, sizeof(char)); strcpy(str, ""); strcat(str, desc); strcat(str, ""); gtk_label_set_markup(GTK_DOCK_PANEL(data)->title, str); free(str); g_signal_emit_by_name(GTK_DOCK_PANEL(data), "switch-item", ditem); return TRUE; }