/* Chrysalide - Outil d'analyse de fichiers binaires * view.c - gestion du menu 'Affichage' * * Copyright (C) 2012-2017 Cyrille Bagard * * This view 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 "view.h" #include #include #include "../editem-int.h" #include "../core/global.h" #include "../core/items.h" #include "../core/panels.h" #include "../../analysis/loaded.h" #include "../../gtkext/easygtk.h" #include "../../gtkext/gtkblockdisplay.h" #include "../../gtkext/gtkgraphdisplay.h" /* Met à jour les accès du menu "Affichage -> Basculer...". */ static void update_switch_access_in_menu_view(void); /* Réagit avec le menu "Affichage -> Panneaux latéraux". */ static void mcb_view_update_side_panels_list(GtkMenuItem *, GMenuBar *); /* Réagit avec le menu "Affichage -> Panneaux latéraux -> ...". */ static void mcb_view_change_panel_docking(GtkCheckMenuItem *, GPanelItem *); /* Réagit avec le menu "Affichage -> Vue xxx". */ static void mcb_view_change_support(GtkRadioMenuItem *, gpointer); /* Réagit avec le menu "Affichage -> Basculer vers le suivant". */ static void mcb_view_switch_to_next_support(GtkRadioMenuItem *, gpointer); /* Réagit avec le menu "Affichage -> Basculer vers le précédent". */ static void mcb_view_switch_to_prev_support(GtkRadioMenuItem *, gpointer); /* Effectue la bascule d'un panneau de chargement à un autre. */ static void change_current_view_support(unsigned int); /* Réagit avec le menu "Affichage -> (colonne xxx)". */ static void mcb_view_display_column(GtkCheckMenuItem *, gpointer); /* Réagit avec le menu "Affichage -> Plein écran". */ static void mcb_view_show_full_screen(GtkCheckMenuItem *, GMenuBar *); /****************************************************************************** * * * Paramètres : ref = espace de référencement global. * * accgroup = groupe d'accélérateurs pour les menus. * * bar = barre de menu parente. * * * * Description : Construit le menu "Affichage". * * * * Retour : Panneau de menus mis en place. * * * * Remarques : - * * * ******************************************************************************/ GtkWidget *build_menu_view(GObject *ref, GtkAccelGroup *accgroup, GMenuBar *bar) { GtkWidget *result; /* Support à retourner */ GtkWidget *menubar; /* Support pour éléments */ GtkWidget *submenuitem; /* Sous-élément de menu */ GSList *rgroup; /* Groupe des boutons radio */ result = gtk_menu_item_new_with_mnemonic(_("_View")); gtk_widget_show(result); menubar = qck_create_menu(GTK_MENU_ITEM(result)); /* Affichage -> Panneaux latéraux */ submenuitem = qck_create_menu_item(NULL, NULL, _("Side panels"), NULL, NULL); g_signal_connect(submenuitem, "select", G_CALLBACK(mcb_view_update_side_panels_list), bar); gtk_container_add(GTK_CONTAINER(menubar), submenuitem); qck_create_menu(GTK_MENU_ITEM(submenuitem)); /* Séparation */ submenuitem = qck_create_menu_separator(); gtk_container_add(GTK_CONTAINER(menubar), submenuitem); /* Types de panneau de code */ submenuitem = qck_create_radio_menu_item(ref, "mnu_view_switch_textview", NULL, _("Text view"), G_CALLBACK(mcb_view_change_support), NULL); add_accelerator_to_menu_item(submenuitem, "F3", accgroup); g_object_set_data(G_OBJECT(submenuitem), "kind_of_view", GUINT_TO_POINTER(BVW_BLOCK)); g_object_set_data(G_OBJECT(submenuitem), "kind_of_display", GSIZE_TO_POINTER(GTK_TYPE_BLOCK_DISPLAY)); gtk_container_add(GTK_CONTAINER(menubar), submenuitem); rgroup = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(submenuitem)); submenuitem = qck_create_radio_menu_item(ref, "mnu_view_switch_graphview", rgroup, _("Graph view"), G_CALLBACK(mcb_view_change_support), NULL); add_accelerator_to_menu_item(submenuitem, "F4", accgroup); g_object_set_data(G_OBJECT(submenuitem), "kind_of_view", GUINT_TO_POINTER(BVW_GRAPH)); g_object_set_data(G_OBJECT(submenuitem), "kind_of_display", GSIZE_TO_POINTER(GTK_TYPE_GRAPH_DISPLAY)); gtk_container_add(GTK_CONTAINER(menubar), submenuitem); submenuitem = qck_create_menu_item(ref, "mcb_view_switch_to_next_support", _("Switch to next"), G_CALLBACK(mcb_view_switch_to_next_support), NULL); add_accelerator_to_menu_item(submenuitem, "Tab", accgroup); gtk_container_add(GTK_CONTAINER(menubar), submenuitem); submenuitem = qck_create_menu_item(ref, "mcb_view_switch_to_prev_support", _("Switch to previous"), G_CALLBACK(mcb_view_switch_to_prev_support), NULL); add_accelerator_to_menu_item(submenuitem, "Tab", accgroup); gtk_container_add(GTK_CONTAINER(menubar), submenuitem); /* Séparation */ submenuitem = qck_create_menu_separator(); gtk_container_add(GTK_CONTAINER(menubar), submenuitem); /* Affichage des données */ submenuitem = qck_create_check_menu_item(ref, "mnu_view_display_off", _("Physical offset"), G_CALLBACK(mcb_view_display_column), NULL); g_object_set_data(G_OBJECT(submenuitem), "kind_of_opt", GUINT_TO_POINTER(BLC_PHYSICAL)); gtk_container_add(GTK_CONTAINER(menubar), submenuitem); submenuitem = qck_create_check_menu_item(ref, "mnu_view_display_addr", _("Virtual address"), G_CALLBACK(mcb_view_display_column), NULL); g_object_set_data(G_OBJECT(submenuitem), "kind_of_opt", GUINT_TO_POINTER(BLC_VIRTUAL)); gtk_container_add(GTK_CONTAINER(menubar), submenuitem); submenuitem = qck_create_check_menu_item(ref, "mnu_view_display_code", _("Binary code"), G_CALLBACK(mcb_view_display_column), NULL); g_object_set_data(G_OBJECT(submenuitem), "kind_of_opt", GUINT_TO_POINTER(BLC_BINARY)); gtk_container_add(GTK_CONTAINER(menubar), submenuitem); /* Séparation */ submenuitem = qck_create_menu_separator(); gtk_container_add(GTK_CONTAINER(menubar), submenuitem); /* Affichage -> Plein écran */ submenuitem = qck_create_check_menu_item(NULL, NULL, _("Full screen"), G_CALLBACK(mcb_view_show_full_screen), bar); add_accelerator_to_menu_item(submenuitem, "F11", accgroup); gtk_container_add(GTK_CONTAINER(menubar), submenuitem); return result; } /****************************************************************************** * * * Paramètres : widget = menu principal à actualiser. * * panel = nouveau panneau d'affichage actif. * * bar = barre de menu parente. * * * * Description : Lance une actualisation du fait d'un changement de vue. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void rebuild_menu_view(GtkWidget *widget, GLoadedPanel *panel, GMenuBar *bar) { GObject *ref; /* Espace de référencements */ GtkRadioMenuItem *item; /* Elément de menu arbitraire */ GSList *radios; /* Liste des menus d'affichage */ GSList *found; /* Elément de menu à activer */ GLoadedContent *content; /* Contenu global représenté */ unsigned int view_index; /* Indice de représentation */ const bool *display; /* Règles d'affichage courantes*/ GtkWidget *submenuitem; /* Sous-élément de menu */ bool status; /* Consigne d'affichage */ ref = g_editor_item_get_global_ref(G_EDITOR_ITEM(bar)); /* Types de panneau de code */ item = GTK_RADIO_MENU_ITEM(g_object_get_data(ref, "mnu_view_switch_graphview")); radios = gtk_radio_menu_item_get_group(item); void disconnect_display_radio(GtkWidget *wgt, gpointer unused) { g_signal_handlers_disconnect_by_func(wgt, G_CALLBACK(mcb_view_change_support), NULL); } g_slist_foreach(radios, (GFunc)disconnect_display_radio, NULL); gint find_suitable_display_radio(GObject *rdo, GObject *pnl) { GType rdo_type; /* Type d'affichage supporté */ GType pnl_type; /* Type du panneau affiché */ rdo_type = GPOINTER_TO_SIZE(g_object_get_data(rdo, "kind_of_display")); pnl_type = G_OBJECT_TYPE(pnl); return (rdo_type == pnl_type ? 0 : -1); } found = g_slist_find_custom(radios, G_OBJECT(panel), (GCompareFunc)find_suitable_display_radio); assert(found != NULL); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(found->data), TRUE); void reconnect_display_radio(GtkWidget *wgt, gpointer unused) { g_signal_connect(wgt, "toggled", G_CALLBACK(mcb_view_change_support), NULL); } g_slist_foreach(radios, (GFunc)reconnect_display_radio, NULL); /* Séparation */ content = g_loaded_panel_get_content(panel); view_index = g_loaded_content_get_view_index(content, GTK_WIDGET(panel)); display = g_loaded_content_get_all_display_options(content, view_index); g_object_unref(G_OBJECT(content)); /* Positions physiques */ submenuitem = g_object_get_data(ref, "mnu_view_display_off"); g_signal_handlers_disconnect_by_func(submenuitem, G_CALLBACK(mcb_view_display_column), NULL); status = display[BLC_PHYSICAL]; gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(submenuitem), status); g_signal_connect(submenuitem, "toggled", G_CALLBACK(mcb_view_display_column), NULL); /* Adresses virtuelles */ submenuitem = g_object_get_data(ref, "mnu_view_display_addr"); g_signal_handlers_disconnect_by_func(submenuitem, G_CALLBACK(mcb_view_display_column), NULL); status = display[BLC_VIRTUAL]; gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(submenuitem), status); g_signal_connect(submenuitem, "toggled", G_CALLBACK(mcb_view_display_column), NULL); /* Code binaire */ submenuitem = g_object_get_data(ref, "mnu_view_display_code"); g_signal_handlers_disconnect_by_func(submenuitem, G_CALLBACK(mcb_view_display_column), NULL); status = display[BLC_BINARY]; gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(submenuitem), status); g_signal_connect(submenuitem, "toggled", G_CALLBACK(mcb_view_display_column), NULL); } /****************************************************************************** * * * Paramètres : ref = espace de référencements à consulter. * * panel = panneau d'affichage actif ou NULL si aucun. * * * * Description : Met à jour les accès du menu "Affichage" selon le contenu. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void update_access_for_view_in_menu_view(GObject *ref, GLoadedPanel *panel) { gboolean access; /* Accès à déterminer */ GtkWidget *item; /* Elément de menu à traiter */ /* Préliminaire */ access = (panel != NULL); /* Bascules */ update_switch_access_in_menu_view(); /* Affichage des données */ item = GTK_WIDGET(g_object_get_data(ref, "mnu_view_display_off")); gtk_widget_set_sensitive(item, access); item = GTK_WIDGET(g_object_get_data(ref, "mnu_view_display_addr")); gtk_widget_set_sensitive(item, access); item = GTK_WIDGET(g_object_get_data(ref, "mnu_view_display_code")); gtk_widget_set_sensitive(item, access); } /****************************************************************************** * * * Paramètres : - * * * * Description : Met à jour les accès du menu "Affichage -> Basculer...". * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void update_switch_access_in_menu_view(void) { GObject *ref; /* Espace de références */ GLoadedPanel *panel; /* Afficheur effectif de code */ GLoadedContent *content; /* Contenu représenté */ unsigned int count; /* Nombre de vues possibles */ unsigned int index; /* Indice de la vue courante */ gboolean access; /* Accès à déterminer */ GtkWidget *item; /* Elément de menu à traiter */ ref = get_global_ref(); panel = get_current_view(); if (panel == NULL) content = NULL; else { content = g_loaded_panel_get_content(panel); count = g_loaded_content_count_views(content); index = g_loaded_content_get_view_index(content, GTK_WIDGET(panel)); } access = (panel != NULL && (index + 1) < count); item = GTK_WIDGET(g_object_get_data(ref, "mcb_view_switch_to_next_support")); gtk_widget_set_sensitive(item, access); access = (panel != NULL && index > 0); item = GTK_WIDGET(g_object_get_data(ref, "mcb_view_switch_to_prev_support")); gtk_widget_set_sensitive(item, access); if (panel != NULL) { g_object_unref(G_OBJECT(content)); g_object_unref(G_OBJECT(panel)); } } /****************************************************************************** * * * Paramètres : menuitem = élément de menu sélectionné. * * bar = barre de menu parente. * * * * Description : Réagit avec le menu "Affichage -> Panneaux latéraux". * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void mcb_view_update_side_panels_list(GtkMenuItem *menuitem, GMenuBar *bar) { GtkWidget *menubar; /* Support pour éléments */ GtkAccelGroup *accgroup; /* Groupe de raccourcis */ typedef struct _panels_loading_filter { GtkContainer *support; /* Support pour éléments */ GtkAccelGroup *accel; /* Groupe de raccourcis */ PanelItemPersonality personality; /* Nature des éléments attendus*/ bool first; /* Premier ajout ? */ } panels_loading_filter; panels_loading_filter pfilter; menubar = gtk_menu_item_get_submenu(menuitem); /* Réinitialisation */ void remove_panel_menu_item(GtkWidget *widget, GtkContainer *container) { gtk_container_remove(container, widget); } gtk_container_foreach(GTK_CONTAINER(menubar), (GtkCallback)remove_panel_menu_item, menubar); /* Ajout des panneaux uniques */ bool add_side_panel_to_list(GPanelItem *panel, panels_loading_filter *filter) { const char *name; /* Désignation de l'entrée */ GtkWidget *submenuitem; /* Sous-élément de menu */ const char *bindings; /* Raccourcis clavier bruts */ if (gtk_panel_item_get_personality(panel) != filter->personality) goto aptl_exit; /* Séparation */ if (filter->first) { filter->first = false; submenuitem = qck_create_menu_separator(); gtk_container_add(GTK_CONTAINER(menubar), submenuitem); } /* Elément de menu */ name = g_editor_item_get_name(G_EDITOR_ITEM(panel)); submenuitem = qck_create_check_menu_item(NULL, NULL, name, G_CALLBACK(mcb_view_change_panel_docking), panel); bindings = gtk_panel_item_get_key_bindings(panel); if (bindings != NULL) add_accelerator_to_menu_item(submenuitem, bindings, filter->accel); gtk_container_add(filter->support, submenuitem); /* Statut de la coche */ if (g_panel_item_is_docked(panel)) { g_signal_handlers_disconnect_by_func(submenuitem, G_CALLBACK(mcb_view_change_panel_docking), panel); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(submenuitem), TRUE); g_signal_connect(submenuitem, "toggled", G_CALLBACK(mcb_view_change_panel_docking), panel); } aptl_exit: return true; } accgroup = GTK_ACCEL_GROUP(g_object_get_data(G_OBJECT(bar), "accgroup")); pfilter.support = GTK_CONTAINER(menubar); pfilter.accel = accgroup; pfilter.personality = PIP_SINGLETON; pfilter.first = false; browse_all_item_panels((handle_panel_item_fc)add_side_panel_to_list, &pfilter); pfilter.personality = PIP_OTHER; pfilter.first = true; browse_all_item_panels((handle_panel_item_fc)add_side_panel_to_list, &pfilter); } /****************************************************************************** * * * Paramètres : menuitem = élément de menu ayant basculé. * * bar = barre de menu parente. * * * * Description : Réagit avec le menu "Affichage -> Panneaux latéraux -> ...". * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void mcb_view_change_panel_docking(GtkCheckMenuItem *menuitem, GPanelItem *item) { gboolean active; /* Etat de sélection du menu */ active = gtk_check_menu_item_get_active(menuitem); if (active) g_panel_item_dock(item); else g_panel_item_undock(item); } /****************************************************************************** * * * Paramètres : menuitem = élément de menu ayant basculé. * * unused = adresse non utilisée ici. * * * * Description : Réagit avec le menu "Affichage -> Vue xxx". * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void mcb_view_change_support(GtkRadioMenuItem *menuitem, gpointer unused) { GSList *group; /* Liste de menus radio */ GSList *iter; /* Boucle de parcours */ unsigned int wanted; /* Nouvelle vue à présenter */ /* On ne traite qu'une seule fois ! */ if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) return; group = gtk_radio_menu_item_get_group(menuitem); for (iter = group; iter != NULL; iter = g_slist_next(iter)) { if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(iter->data))) continue; wanted = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(iter->data), "kind_of_view")); change_current_view_support(wanted); } } /****************************************************************************** * * * Paramètres : menuitem = élément de menu ayant basculé. * * unused = adresse non utilisée ici. * * * * Description : Réagit avec le menu "Affichage -> Basculer vers le suivant". * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void mcb_view_switch_to_next_support(GtkRadioMenuItem *menuitem, gpointer unused) { GLoadedPanel *panel; /* Afficheur effectif de code */ GLoadedContent *content; /* Contenu représenté */ unsigned int index; /* Indice de la vue courante */ #ifndef NDEBUG unsigned int count; /* Nombre de vues possibles */ #endif panel = get_current_view(); content = g_loaded_panel_get_content(panel); index = g_loaded_content_get_view_index(content, GTK_WIDGET(panel)); #ifndef NDEBUG count = g_loaded_content_count_views(content); assert((index + 1) < count); #endif change_current_view_support(index + 1); g_object_unref(G_OBJECT(content)); g_object_unref(G_OBJECT(panel)); update_switch_access_in_menu_view(); } /****************************************************************************** * * * Paramètres : menuitem = élément de menu ayant basculé. * * unused = adresse non utilisée ici. * * * * Description : Réagit avec le menu "Affichage -> Basculer ... précédent". * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void mcb_view_switch_to_prev_support(GtkRadioMenuItem *menuitem, gpointer unused) { GLoadedPanel *panel; /* Afficheur effectif de code */ GLoadedContent *content; /* Contenu représenté */ unsigned int index; /* Indice de la vue courante */ panel = get_current_view(); content = g_loaded_panel_get_content(panel); index = g_loaded_content_get_view_index(content, GTK_WIDGET(panel)); assert(index > 0); change_current_view_support(index - 1); g_object_unref(G_OBJECT(content)); g_object_unref(G_OBJECT(panel)); update_switch_access_in_menu_view(); } /****************************************************************************** * * * Paramètres : wanted = indice de la vue désirée. * * * * Description : Effectue la bascule d'un panneau de chargement à un autre. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void change_current_view_support(unsigned int wanted) { GLoadedPanel *panel; /* Afficheur effectif de code */ GtkDockStation *station; /* Base du remplacement */ GLoadedContent *content; /* Contenu représenté */ GtkWidget *support; /* Nouvel afficheur généraliste*/ GLineCursor *cursor; /* Position à transmettre */ GLoadedPanel *new; /* Panneau encapsulé */ panel = get_current_view(); station = get_dock_station_for_view_panel(GTK_WIDGET(panel)); content = g_loaded_panel_get_content(panel); support = g_loaded_content_build_view(content, wanted); g_object_unref(G_OBJECT(content)); cursor = g_loaded_panel_get_cursor(panel); gtk_dock_panel_change_active_widget(station, support); new = G_LOADED_PANEL(get_loaded_panel_from_built_view(support)); g_loaded_panel_set_cursor(new, cursor); change_editor_items_current_view(new); g_object_unref(G_OBJECT(cursor)); g_object_unref(G_OBJECT(panel)); } /****************************************************************************** * * * Paramètres : menuitem = élément de menu ayant basculé. * * unused = adresse non utilisée ici. * * * * Description : Réagit avec le menu "Affichage -> (colonne xxx)". * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void mcb_view_display_column(GtkCheckMenuItem *menuitem, gpointer unused) { unsigned int option; /* Paramètre à traiter */ gboolean active; /* Etat de sélection du menu */ GLoadedPanel *panel; /* Afficheur effectif de code */ GLoadedContent *content; /* Contenu représenté */ unsigned int index; /* Indice de la vue courante */ option = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(menuitem), "kind_of_opt")); active = gtk_check_menu_item_get_active(menuitem); panel = get_current_view(); content = g_loaded_panel_get_content(panel); index = g_loaded_content_get_view_index(content, GTK_WIDGET(panel)); g_loaded_content_set_display_option(content, index, option, active); g_object_unref(G_OBJECT(content)); g_object_unref(G_OBJECT(panel)); } /****************************************************************************** * * * Paramètres : menuitem = élément de menu sélectionné. * * bar = barre de menu parente. * * * * Description : Réagit avec le menu "Affichage -> Plein écran". * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void mcb_view_show_full_screen(GtkCheckMenuItem *menuitem, GMenuBar *bar) { GObject *ref; /* Espace de référencements */ gboolean active; /* Etat de sélection du menu */ ref = g_editor_item_get_global_ref(G_EDITOR_ITEM(bar)); active = gtk_check_menu_item_get_active(menuitem); if (active) gtk_window_fullscreen(GTK_WINDOW(ref)); else gtk_window_unfullscreen(GTK_WINDOW(ref)); }