/* Chrysalide - Outil d'analyse de fichiers binaires * editor.c - fenêtre principale de l'interface graphique * * Copyright (C) 2008-2013 Cyrille Bagard * * This file is part of Chrysalide. * * Chrysalide 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. * * Chrysalide 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "editor.h" #include #include #include #include #include #include "status.h" #include "menus/menubar.h" #include "core/core.h" #include "core/panels.h" #include "panels/panel.h" #include "panels/welcome.h" #include "tb/portions.h" #include "tb/source.h" #include "../analysis/project.h" #include "../common/extstr.h" #include "../core/params.h" #include "../gtkext/easygtk.h" #include "../gtkext/gtkdockable.h" #include "../gtkext/gtkdockstation.h" #include "../gtkext/support.h" /* Gestion des raccourcis clavier */ static GtkAccelGroup *_accgroup = NULL; /* Met en place la liste des icônes de l'éditeur. */ static GList *build_editor_icons_list(void); /* Construit la fenêtre de l'éditeur. */ GtkWidget *create_editor(void); /* Quitte le programme en sortie de la boucle de GTK. */ static gboolean on_delete_editor(GtkWidget *, GdkEvent *, gpointer); /* Quitte le programme en sortie de la boucle de GTK. */ static void on_destroy_editor(GtkWidget *, gpointer); /* ------------------------ INTEGRATION DE LA BARRE D'OUTILS ------------------------ */ /* Construit la barre d'outils de l'éditeur. */ static GtkWidget *build_editor_toolbar(GObject *); /* -------------------- MECANISMES DE (DE)PLACEMENT DES PANNEAUX -------------------- */ /* Elément de la hiérarchie des panneaux */ typedef struct _panel_node { struct _panel_node *parent; /* Noeud parent */ char *path; /* Chemin du nom courant */ union { GtkWidget *widget; /* Accès généraliste */ GtkWidget *station; /* Station d'accueil simple */ GtkWidget *paned; /* Station d'accueil composée */ }; union { /* Version simple */ /* ... */ /* Version composée */ struct { struct _panel_node *first; /* Premier sous élément */ struct _panel_node *second; /* Second sous élément */ }; }; } panel_node; #define IS_SIMPLE_NODE(nd) \ ({ \ bool __result; \ __result = GTK_IS_DOCK_STATION(nd->station); \ assert(__result || GTK_IS_PANED(nd->paned)); \ __result; \ }) /* Support de fond pour les composants. */ static GObject *_global_ref = NULL; static GtkWidget *_support = NULL; static panel_node *_nodes = NULL; /* Crée un nouveau noeud pour un panneau particulier. */ static panel_node *create_simple_panel_node_for_item(GPanelItem *, const char *); /* Prépare une nouvelle sous-division pour deux panneaux. */ static void switch_panel_node_into_paned(panel_node *, bool, bool); /* Met en place un nouveau noeud dans une division. */ static void attach_panel_node_to_paned(panel_node *, panel_node *, bool); /* Met à jour en cascade les chemins d'accès aux noeuds. */ static void update_path_of_paned_nodes(panel_node *, const char *, const char *); /* Détermine la plus grande longueur commune entre éléments. */ static size_t compute_path_common_length(const panel_node *, const char *); /* Place au bon endroit un panneau donné. */ static panel_node *insert_item_as_panel_node(GPanelItem *, panel_node *, const char *, size_t); /* Tente de mettre la main sur une station d'accueil. */ static panel_node *find_node_for_station(panel_node *, GtkWidget *); /* Tente de mettre la main sur un panneau de division graphique. */ static panel_node *find_node_for_paned(panel_node *, GtkWidget *); /* Efface de l'organisation un noeud donné en place. */ static void delete_panel_node(panel_node *); /* ------------------- INTERACTIONS GRAPHIQUES LIEES AUX PANNEAUX ------------------- */ /* Réagit à une demande de placement d'un panneau d'affichage. */ void on_panel_item_dock_request(GPanelItem *, void *); /* Réagit à une demande de suppression d'un panneau d'affichage. */ void on_panel_item_undock_request(GPanelItem *, void *); /* Réagit au changement d'onglet d'un panneau quelconque. */ static void on_dock_item_switch(GtkDockStation *, GtkWidget *, GObject *); /* Réagit à une demande de menu pour rajouter des panneaux. */ static void on_dock_menu_request(GtkDockStation *, GtkWidget *, GObject *); /* Réagit à une demande de fermeture du panneau courant. */ static void on_dock_close_request(GtkDockStation *, GtkWidget *, GObject *); /* Réagit à une variation dans une séparation de panneaux. */ static void notify_paned_handle_position_change(GObject *, GParamSpec *, gpointer); /****************************************************************************** * * * Paramètres : - * * * * Description : Met en place la liste des icônes de l'éditeur. * * * * Retour : Liste d'images dimensionnées. * * * * Remarques : - * * * ******************************************************************************/ static GList *build_editor_icons_list(void) { GList *result; /* Liste à retourner */ GdkPixbuf *pixbuf; /* Image chargée en mémoire */ result = NULL; pixbuf = get_pixbuf_from_file("chrysalide-32.png"); if (pixbuf != NULL) result = g_list_append(result, pixbuf); pixbuf = get_pixbuf_from_file("chrysalide-64.png"); if (pixbuf != NULL) result = g_list_append(result, pixbuf); pixbuf = get_pixbuf_from_file("chrysalide-128.png"); if (pixbuf != NULL) result = g_list_append(result, pixbuf); return result; } /****************************************************************************** * * * Paramètres : - * * * * Description : Construit la fenêtre de l'éditeur. * * * * Retour : Adresse de la fenêtre mise en place. * * * * Remarques : - * * * ******************************************************************************/ GtkWidget *create_editor(void) { GtkWidget *result; /* Fenêtre à renvoyer */ bool hide; /* Cachette de la barre ? */ bool maximized; /* Affichage en plein écran ? */ GList *icons; /* Liste d'images dimensionnées*/ GObject *ref; /* Version de référence */ GEditorItem *editem; /* Menus réactifs principaux */ GtkWidget *menuboard; /* Barre de menus principale */ GtkWidget *toolbar; /* Barre d'outils */ GtkWidget *vbox1; GtkWidget *widget; /* Composant à intégrer */ result = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_set_size_request(result, 1024, 800); gtk_window_set_position(GTK_WINDOW(result), GTK_WIN_POS_CENTER); gtk_container_set_border_width(GTK_CONTAINER(result), 4); gtk_window_set_title(GTK_WINDOW(result), _("Chrysalide")); g_generic_config_get_value(get_main_configuration(), MPK_TITLE_BAR, &hide); gtk_window_set_hide_titlebar_when_maximized(GTK_WINDOW(result), hide); g_generic_config_get_value(get_main_configuration(), MPK_MAXIMIZED, &maximized); gtk_window_maximize(GTK_WINDOW(result)); icons = build_editor_icons_list(); gtk_window_set_icon_list(GTK_WINDOW(result), icons); g_list_free_full(icons, (GDestroyNotify)g_object_unref); g_signal_connect(G_OBJECT(result), "delete-event", G_CALLBACK(on_delete_editor), NULL); g_signal_connect(G_OBJECT(result), "destroy", G_CALLBACK(on_destroy_editor), NULL); ref = G_OBJECT(result); _accgroup = gtk_accel_group_new(); gtk_window_add_accel_group(GTK_WINDOW(result), _accgroup); vbox1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4); gtk_widget_show(vbox1); gtk_container_add(GTK_CONTAINER(result), vbox1); /* Intégration des menus */ editem = g_menu_bar_new(ref, _accgroup); register_editor_item(editem); menuboard = g_editor_item_get_widget(editem); gtk_box_pack_start(GTK_BOX(vbox1), menuboard, FALSE, FALSE, 0); /* Barre d'outils */ toolbar = build_editor_toolbar(G_OBJECT(result)); gtk_box_pack_start(GTK_BOX(vbox1), toolbar, FALSE, FALSE, 0); do { _global_ref = ref; _support = gtk_event_box_new(); gtk_widget_show(_support); //_support = init_panels2(G_CALLBACK(on_dock_item_switch), ref); gtk_box_pack_start(GTK_BOX(vbox1), _support, TRUE, TRUE, 0); /* ... = */load_all_gui_components(ref); } while(0); /* Barre de statut générale */ editem = g_status_info_new(ref); register_editor_item(editem); widget = g_editor_item_get_widget(editem); gtk_box_pack_start(GTK_BOX(vbox1), widget, FALSE, FALSE, 0); /* Autre */ /* ... = */prepare_drag_and_drop_window(); /* Actualisation des contenus */ change_editor_items_current_binary(ref, NULL); return result; } /****************************************************************************** * * * Paramètres : widget = fenêtre de l'éditeur de préférences. * * event = informations liées à l'événement. * * data = adresse non utilisée ici. * * * * Description : Quitte le programme en sortie de la boucle de GTK. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static gboolean on_delete_editor(GtkWidget *widget, GdkEvent *event, gpointer data) { gboolean result; /* Continuation à retourner */ GStudyProject *project; /* Projet courant */ GtkWidget *dialog; /* Boîte à afficher */ result = FALSE; project = get_current_project(); if (project == NULL) goto ode_no_project; if (g_study_project_get_filename(project) == NULL) { dialog = gtk_message_dialog_new(GTK_WINDOW(widget), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("The current project will be lost. Do you you want to save it ?")); gtk_dialog_add_button(GTK_DIALOG(dialog), _("_Yes"), GTK_RESPONSE_YES); gtk_dialog_add_button(GTK_DIALOG(dialog), _("_No"), GTK_RESPONSE_NO); gtk_dialog_add_button(GTK_DIALOG(dialog), _("_Cancel"), GTK_RESPONSE_CANCEL); switch (gtk_dialog_run(GTK_DIALOG(dialog))) { case GTK_RESPONSE_YES: //mcb_file_save_project_as(NULL, widget); /* FIXME */ break; case GTK_RESPONSE_NO: break; case GTK_RESPONSE_CANCEL: result = TRUE; break; } gtk_widget_destroy(dialog); } ode_no_project: return result; } /****************************************************************************** * * * Paramètres : widget = fenêtre de l'éditeur de préférences. * * event = informations liées à l'événement. * * data = adresse non utilisée ici. * * * * Description : Quitte le programme en sortie de la boucle de GTK. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_destroy_editor(GtkWidget *widget, gpointer data) { GStudyProject *project; /* Projet courant */ project = get_current_project(); if (project != NULL) g_object_unref(G_OBJECT(project)); /* Fermeture propre */ /* ... */ gtk_main_quit(); } /* ---------------------------------------------------------------------------------- */ /* INTEGRATION DE LA BARRE D'OUTILS */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : ref = espace de référencement global. * * * * Description : Construit la barre d'outils de l'éditeur. * * * * Retour : Adresse du composant mis en place. * * * * Remarques : - * * * ******************************************************************************/ static GtkWidget *build_editor_toolbar(GObject *ref) { GtkWidget *result; /* Support à retourner */ GEditorItem *item; /* Elément de barre d'outils */ result = gtk_toolbar_new(); gtk_widget_show(result); g_object_set_data(ref, "toolbar", result); item = create_source_tb_item(ref); register_editor_item(item); item = create_portions_tb_item(ref); register_editor_item(item); return result; } /* ---------------------------------------------------------------------------------- */ /* MECANISMES DE (DE)PLACEMENT DES PANNEAUX */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : item = composant à présenter à l'affichage. * * path = partie du chemin représentée ici. * * * * Description : Crée un nouveau noeud pour un panneau particulier. * * * * Retour : Structure d'accueil mise en place. * * * * Remarques : - * * * ******************************************************************************/ static panel_node *create_simple_panel_node_for_item(GPanelItem *item, const char *path) { panel_node *result; /* Structure à retourner */ GtkWidget *station; /* Premier support concentré */ /* Partie graphique */ station = gtk_dock_station_new(); g_signal_connect(station, "switch-widget", G_CALLBACK(on_dock_item_switch), _global_ref); g_signal_connect(station, "menu-requested", G_CALLBACK(on_dock_menu_request), _global_ref); g_signal_connect(station, "close-requested", G_CALLBACK(on_dock_close_request), _global_ref); gtk_widget_show(station); gtk_dock_station_add_dockable(GTK_DOCK_STATION(station), GTK_DOCKABLE(item)); /* Partie invisible */ result = (panel_node *)calloc(1, sizeof(panel_node)); result->station = station; result->path = strdup(path); return result; } /****************************************************************************** * * * Paramètres : node = noeud à diviser. * * horiz = indique le type d'orientation désiré. * * first = indication sur l'emplacement à utiliser. * * * * Description : Prépare une nouvelle sous-division pour deux panneaux. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void switch_panel_node_into_paned(panel_node *node, bool horiz, bool first) { GtkWidget *widget; /* Composant à traiter */ panel_node *parent; /* Lien de parenté à conserver */ panel_node *moved; /* Noeud descendu d'un étage */ /* Décroche graphiquement le support */ widget = node->widget; g_object_ref(G_OBJECT(widget)); gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(widget)), widget); /* Descend l'élément actuel */ parent = node->parent; moved = (panel_node *)calloc(1, sizeof(panel_node)); memcpy(moved, node, sizeof(panel_node)); node->path = NULL; if (!IS_SIMPLE_NODE(moved)) { moved->first->parent = moved; moved->second->parent = moved; } /* Création du nouveau niveau intermédiaire */ if (horiz) node->paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL); else node->paned = gtk_paned_new(GTK_ORIENTATION_VERTICAL); g_signal_connect(node->paned, "notify::position", G_CALLBACK(notify_paned_handle_position_change), NULL); gtk_widget_show(node->paned); if (parent == NULL) attach_panel_node_to_paned(parent, node, true); else attach_panel_node_to_paned(parent, node, parent->first == node); /* Replace le composant d'origine */ attach_panel_node_to_paned(node, moved, first); } /****************************************************************************** * * * Paramètres : parent = noeud d'accueil pour le nouveau noeud. * * node = nouveau noeud à intégrer dans l'ensemble. * * first = indication sur l'emplacement à utiliser. * * * * Description : Met en place un nouveau noeud dans une division. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void attach_panel_node_to_paned(panel_node *parent, panel_node *node, bool first) { node->parent = parent; /* On se trouve à la racine... */ if (parent == NULL) { gtk_container_add(GTK_CONTAINER(_support), node->widget); update_path_of_paned_nodes(node, "", "R"); } else { /* Raccordement hiérarchique */ if (first) parent->first = node; else parent->second = node; /* Raccordement graphique */ assert(!IS_SIMPLE_NODE(parent)); if (first) gtk_paned_pack1(GTK_PANED(parent->paned), node->widget, TRUE, FALSE); else gtk_paned_pack2(GTK_PANED(parent->paned), node->widget, TRUE, FALSE); /* Mise à jour des chemins */ update_path_of_paned_nodes(node, parent->path, first ? "1" : "2"); } } /****************************************************************************** * * * Paramètres : node = noeud dont le chemin est à mettre à jour. * * root = chemin menant au niveau précédent. * * sub = sous-chemin vers lequel l'orientation penche. * * * * Description : Met à jour en cascade les chemins d'accès aux noeuds. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void update_path_of_paned_nodes(panel_node *node, const char *root, const char *sub) { GtkOrientation orientation; /* Orientation du support */ char *key; /* Clef d'accès à un paramètre */ gint position; /* Nouvelle position de barre */ if (!IS_SIMPLE_NODE(node)) { /* Reconstruction locale */ if (node->path != NULL) free(node->path); node->path = strdup(root); node->path = stradd(node->path, sub); orientation = gtk_orientable_get_orientation(GTK_ORIENTABLE(node->paned)); if (orientation == GTK_ORIENTATION_HORIZONTAL) node->path = stradd(node->path, "h"); else node->path = stradd(node->path, "v"); /* Rechargement de la position ? */ asprintf(&key, "gui.panels.positions.%s", node->path); if (g_generic_config_get_value(get_main_configuration(), key, &position)) gtk_paned_set_position(GTK_PANED(node->paned), position); free(key); /* Propagation de la mise à jour */ if (node->first) update_path_of_paned_nodes(node->first, node->path, "1"); if (node->second != NULL) update_path_of_paned_nodes(node->second, node->path, "2"); } } /****************************************************************************** * * * Paramètres : node = noeud d'où lancer les recherches. * * target = identifiant de la position visée. * * * * Description : Détermine la plus grande longueur commune entre éléments. * * * * Retour : Bilan de l'évaluation. * * * * Remarques : - * * * ******************************************************************************/ static size_t compute_path_common_length(const panel_node *node, const char *target) { size_t result; /* Taille à retourner */ size_t len; /* Longueur de comparaison */ size_t common1; /* Tron common avec le côté #1 */ size_t common2; /* Tron common avec le côté #2 */ if (IS_SIMPLE_NODE(node)) { len = MIN(strlen(node->path), strlen(target)); /** * Il n'y a pas forcément de base commune entre deux branches, * donc on parcourt les chemins depuis leur base respective. */ for (result = 0; result < len; result++) if (node->path[result] != target[result]) break; } else { common1 = compute_path_common_length(node->first, target); common2 = compute_path_common_length(node->second, target); result = MAX(common1, common2); } return result; } /****************************************************************************** * * * Paramètres : item = composant à présenter à l'affichage. * * node = point d'insertion courant. * * path = partie du chemin représentée ici. * * consumed = profondeur du chemin utilisé. * * * * Description : Place au bon endroit un panneau donné. * * * * Retour : Noeud final inséré dans la liste. * * * * Remarques : - * * * ******************************************************************************/ static panel_node *insert_item_as_panel_node(GPanelItem *item, panel_node *node, const char *path, size_t consumed) { panel_node *result; /* Noeud ultime à renvoyer */ char div; /* Division demandée */ bool horiz; /* Traduction en composant */ bool first; /* Point d'insertion */ size_t common1; /* Tron common avec le côté #1 */ size_t common2; /* Tron common avec le côté #2 */ fprintf(stderr, "=== INSERTING '%s' (%zu)... -> '%s'\n", path, consumed, IS_SIMPLE_NODE(node) ? node->path : "-"); if (IS_SIMPLE_NODE(node)) { /* Le parcours s'arrête ici ! */ if (strcmp(node->path, path) == 0) { gtk_dock_station_add_dockable(GTK_DOCK_STATION(node->station), GTK_DOCKABLE(item)); result = node; } /* On ne peut aller plus loin, on doit diviser... */ else { div = toupper(path[consumed]); first = (div == 'W' || div == 'N'); horiz = (div == 'W' || div == 'E'); fprintf(stderr, "---%%<--- first=%d horiz=%d\n", first, horiz); fprintf(stderr, "--- cutting %p\n", node->station); switch_panel_node_into_paned(node, horiz, !first); result = create_simple_panel_node_for_item(item, path); attach_panel_node_to_paned(node, result, first); fprintf(stderr, "1# [%p] widget = %p --- split :: %p // %p\n", node, node->station, node->first, node->second); } } /* On regarde des autres côtés... */ else { common1 = compute_path_common_length(node->first, path); common2 = compute_path_common_length(node->second, path); fprintf(stderr, " - L1 :: %zu <-> '%s'\n", common1, node->first->path); fprintf(stderr, " - L2 :: %zu <-> '%s'\n", common2, node->second->path); /* Si une descente est possible... */ if (common1 > 0 || common2 > 0) { if (common1 > common2) result = insert_item_as_panel_node(item, node->first, path, common1); else result = insert_item_as_panel_node(item, node->second, path, common2); } /* Sinon, on doit diviser qqch... */ else { div = toupper(path[consumed]); first = (div == 'W' || div == 'N'); horiz = (div == 'W' || div == 'E'); fprintf(stderr, "---%%<--- first=%d horiz=%d\n", first, horiz); switch_panel_node_into_paned(node, horiz, !first); result = create_simple_panel_node_for_item(item, path); attach_panel_node_to_paned(node, result, first); fprintf(stderr, "2# [%p] split :: %p-%p // %p-%p\n", node, node->first, node->first->widget, node->second, node->second->widget); } } return result; } /****************************************************************************** * * * Paramètres : node = point de départ des recherches locales. * * station = composant graphique à retrouver. * * * * Description : Tente de mettre la main sur une station d'accueil. * * * * Retour : Eventuel noeud trouvé ou NULL. * * * * Remarques : - * * * ******************************************************************************/ static panel_node *find_node_for_station(panel_node *node, GtkWidget *station) { panel_node *result; /* Bilan à remonter */ if (IS_SIMPLE_NODE(node)) result = (node->station == station ? node : NULL); else { result = find_node_for_station(node->first, station); if (result == NULL) result = find_node_for_station(node->second, station); } return result; } /****************************************************************************** * * * Paramètres : node = point de départ des recherches locales. * * paned = composant graphique à retrouver. * * * * Description : Tente de mettre la main sur un panneau de division graphique.* * * * Retour : Eventuel noeud trouvé ou NULL. * * * * Remarques : - * * * ******************************************************************************/ static panel_node *find_node_for_paned(panel_node *node, GtkWidget *paned) { panel_node *result; /* Bilan à remonter */ if (IS_SIMPLE_NODE(node)) result = NULL; else { if (node->paned == paned) result = node; else { result = find_node_for_paned(node->first, paned); if (result == NULL) result = find_node_for_paned(node->second, paned); } } return result; } /****************************************************************************** * * * Paramètres : node = noeud à supprimer de l'arbre des noeuds. * * * * Description : Efface de l'organisation un noeud donné en place. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void delete_panel_node(panel_node *node) { panel_node *parent; /* Noeud parent à transformer */ GtkWidget *widget; /* Composant à traiter */ panel_node *grandparent; /* Noeud supérieur au parent */ panel_node *remaining; /* Noeud restant */ assert(IS_SIMPLE_NODE(node)); parent = node->parent; /* Destruction du noeud */ widget = node->station; gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(widget)), widget); free(node->path); free(node); /* Suppression du niveau intermédiaire */ if (parent != NULL) { remaining = (node == parent->first ? parent->second : parent->first); /* Décroche graphiquement le support */ widget = remaining->widget; g_object_ref(G_OBJECT(widget)); gtk_container_remove(GTK_CONTAINER(parent->paned), widget); /* Supprime le composant graphique intermédiaire */ gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(parent->paned)), parent->paned); /* Réinsère la partie restante */ grandparent = parent->parent; memcpy(parent, remaining, sizeof(panel_node)); if (!IS_SIMPLE_NODE(parent)) { parent->first->parent = parent; parent->second->parent = parent; } free(remaining); if (grandparent == NULL) attach_panel_node_to_paned(grandparent, parent, true); else attach_panel_node_to_paned(grandparent, parent, grandparent->first == parent); } } /* ---------------------------------------------------------------------------------- */ /* INTERACTIONS GRAPHIQUES LIEES AUX PANNEAUX */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : panel = composant à placer dans l'affichage. * * unused = adresse non utilisée ici. * * * * Description : Réagit à une demande de placement d'un panneau d'affichage. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void on_panel_item_dock_request(GPanelItem *panel, void *unused) { const char *path; /* Chemin d'accès */ panel_node *node; /* Noeud à supprimer */ const char *name; /* Nom du dernier panneau */ GPanelItem *welcome; /* Panneau d'accueil */ path = gtk_panel_item_get_path(panel); /* Tout est à faire... */ if (_nodes == NULL) { _nodes = create_simple_panel_node_for_item(panel, path); gtk_container_add(GTK_CONTAINER(_support), _nodes->widget); } else { node = insert_item_as_panel_node(panel, _nodes, path, 0); if (strncmp(node->path, "N", 1) == 0) { name = g_editor_item_get_name(G_EDITOR_ITEM(panel)); if (strcmp(name, PANEL_WELCOME_ID) != 0) { welcome = get_panel_item_by_name(PANEL_WELCOME_ID); if (!g_welcome_panel_get_user_origin(G_WELCOME_PANEL(welcome))) { /* Equivalent d'un appel "g_panel_item_undock(welcome)", avec effet immédiat */ on_panel_item_undock_request(welcome, NULL); } } } } g_panel_item_set_dock_status(panel, true); } /****************************************************************************** * * * Paramètres : panel = composant à retirer de l'affichage. * * unused = adresse non utilisée ici. * * * * Description : Réagit à une demande de suppression d'un panneau d'affichage.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void on_panel_item_undock_request(GPanelItem *panel, void *unused) { GtkWidget *station; /* Support courant */ GtkNotebook *notebook; /* Version parente de station */ panel_node *node; /* Noeud à supprimer */ bool allowed; /* Fermture permise ? */ const char *name; /* Nom du dernier panneau */ GPanelItem *welcome; /* Panneau d'accueil */ gtk_dockable_decompose(GTK_DOCKABLE(panel), &station); notebook = GTK_NOTEBOOK(station); node = find_node_for_station(_nodes, station); assert(node != NULL); allowed = true; if (strcmp(node->path, "N") == 0 && gtk_notebook_get_n_pages(notebook) == 1) { name = g_editor_item_get_name(G_EDITOR_ITEM(panel)); if (strcmp(name, PANEL_WELCOME_ID) == 0) allowed = false; else { welcome = get_panel_item_by_name(PANEL_WELCOME_ID); g_welcome_panel_set_user_origin(G_WELCOME_PANEL(welcome), false); /* Equivalent d'un appel "g_panel_item_dock(welcome)", avec effet immédiat */ on_panel_item_dock_request(welcome, NULL); } } if (allowed) { gtk_dock_station_remove_dockable(GTK_DOCK_STATION(station), GTK_DOCKABLE(panel)); if (gtk_notebook_get_n_pages(notebook) == 0) delete_panel_node(node); g_panel_item_set_dock_status(panel, false); } } /****************************************************************************** * * * Paramètres : station = panneau de support des éléments concerné. * * item = nouvel élément présenté à l'affichage. * * ref = adresse de l'espace de référencement global. * * * * Description : Réagit au changement d'onglet d'un panneau quelconque. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_dock_item_switch(GtkDockStation *station, GtkWidget *widget, GObject *ref) { GLoadedBinary *old_binary; /* Ancien binaire édité */ GLoadedBinary *binary; /* Binaire en cours d'édition */ if (GTK_IS_SCROLLED_WINDOW(widget)) widget = gtk_bin_get_child(GTK_BIN(widget)); if (GTK_IS_VIEWPORT(widget)) widget = gtk_bin_get_child(GTK_BIN(widget)); if (GTK_IS_DISPLAY_PANEL(widget)) { /* Changement de binaire ? */ old_binary = G_LOADED_BINARY(g_object_get_data(ref, "current_binary")); binary = gtk_display_panel_get_binary(GTK_DISPLAY_PANEL(widget)); if (old_binary != binary) { //notify_panels_of_binary_change(binary); change_editor_items_current_binary(ref, binary); } change_editor_items_current_view(ref, GTK_DISPLAY_PANEL(widget)); //notify_panels_of_view_change(GTK_DISPLAY_PANEL(widget), false); g_object_unref(G_OBJECT(binary)); } } /****************************************************************************** * * * Paramètres : station = panneau de support des éléments concerné. * * button = bouton à l'origine de la procédure. * * ref = adresse de l'espace de référencement global. * * * * Description : Réagit à une demande de menu pour rajouter des panneaux. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_dock_menu_request(GtkDockStation *station, GtkWidget *button, GObject *ref) { GtkWidget *active; /* Composant actif modèle */ GPanelItem *model; /* Panneau encapsulé */ GtkContainer *menu; /* Support à retourner */ GList *children; /* Composants mis en place */ GtkWidget *nopanel; /* Sous-élément de menu */ menu = GTK_CONTAINER(gtk_menu_new()); active = gtk_notebook_get_nth_page(GTK_NOTEBOOK(station), 0); model = G_PANEL_ITEM(g_object_get_data(G_OBJECT(active), "dockable")); g_object_set_data_full(G_OBJECT(menu), "path", strdup(gtk_panel_item_get_path(model)), free); /* Ajout des panneaux uniques */ void dock_panel_into_current_station(GtkCheckMenuItem *menuitem, GPanelItem *item) { GtkWidget *parent; /* Menu parent avec chemin */ const char *new_path; /* Nouveau chemin à appliquer */ parent = gtk_widget_get_parent(GTK_WIDGET(menuitem)); new_path = g_object_get_data(G_OBJECT(parent), "path"); gtk_panel_item_set_path(item, new_path); g_panel_item_dock(item); } bool add_side_panel_to_menu(GPanelItem *panel, GtkContainer *support) { const char *name; /* Désignation de l'entrée */ GtkWidget *submenuitem; /* Sous-élément de menu */ const char *bindings; /* Raccourcis clavier bruts */ /* Profil qui ne cadre pas ? */ if (gtk_panel_item_get_personality(panel) != PIP_SINGLETON) goto aptm_exit; if (g_panel_item_is_docked(panel)) goto aptm_exit; /* Elément de menu */ name = g_editor_item_get_name(G_EDITOR_ITEM(panel)); submenuitem = qck_create_menu_item(NULL, NULL, name, G_CALLBACK(dock_panel_into_current_station), panel); bindings = gtk_panel_item_get_key_bindings(panel); if (bindings != NULL) add_accelerator_to_menu_item(submenuitem, bindings, _accgroup); gtk_container_add(support, submenuitem); aptm_exit: return true; } browse_all_item_panels((handle_panel_item_fc)add_side_panel_to_menu, menu); /* Avertissement en cas d'indisponibilité */ children = gtk_container_get_children(GTK_CONTAINER(menu)); if (children == NULL) { nopanel = qck_create_menu_item(NULL, NULL, "No available free panel", NULL, NULL); gtk_widget_set_sensitive(nopanel, FALSE); gtk_container_add(GTK_CONTAINER(menu), nopanel); } else g_list_free(children); /* Affichage du menu */ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, (GtkMenuPositionFunc)attach_popup_menu_to_widget, button, 0, gtk_get_current_event_time()); } /****************************************************************************** * * * Paramètres : station = panneau de support des éléments concerné. * * button = bouton à l'origine de la procédure. * * ref = adresse de l'espace de référencement global. * * * * Description : Réagit à une demande de fermeture du panneau courant. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_dock_close_request(GtkDockStation *station, GtkWidget *button, GObject *ref) { gint index; /* Indice de la page courante */ GtkWidget *active; /* Composant actif modèle */ GPanelItem *panel; /* Panneau encapsulé */ index = gtk_notebook_get_current_page(GTK_NOTEBOOK(station)); active = gtk_notebook_get_nth_page(GTK_NOTEBOOK(station), index); panel = G_PANEL_ITEM(g_object_get_data(G_OBJECT(active), "dockable")); g_panel_item_undock(panel); } /****************************************************************************** * * * Paramètres : obj = objet à l'origine de la procédure. * * pspec = spécification de l'attribut qui a varié. * * unused = adresse non utilisée ici. * * * * Description : Réagit à une variation dans une séparation de panneaux. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void notify_paned_handle_position_change(GObject *obj, GParamSpec *pspec, gpointer unused) { GtkPaned *paned; /* Panneau concerné */ panel_node *node; /* Noeud hiérarchique associé */ gint position; /* Nouvelle position de barre */ char *key; /* Clef d'accès à un paramètre */ paned = GTK_PANED(obj); node = find_node_for_paned(_nodes, GTK_WIDGET(paned)); position = gtk_paned_get_position(paned); asprintf(&key, "gui.panels.positions.%s", node->path); g_generic_config_create_or_udpdate_param(get_main_configuration(), key, CPT_INTEGER, position); free(key); }