diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/glibext/gbufferview.c | 14 | ||||
-rw-r--r-- | src/glibext/gbufferview.h | 2 | ||||
-rw-r--r-- | src/gtkext/gtkbufferdisplay.c | 25 | ||||
-rw-r--r-- | src/gtkext/gtkdisplaypanel-int.h | 7 | ||||
-rw-r--r-- | src/gtkext/gtkdisplaypanel.c | 29 | ||||
-rw-r--r-- | src/gtkext/gtkdisplaypanel.h | 7 | ||||
-rw-r--r-- | src/gtkext/gtkgraphdisplay.c | 63 | ||||
-rw-r--r-- | src/gtkext/gtkgraphdisplay.h | 15 | ||||
-rw-r--r-- | src/gui/dialogs/Makefile.am | 4 | ||||
-rw-r--r-- | src/gui/dialogs/export_disass.c (renamed from src/gui/dialogs/export.c) | 4 | ||||
-rw-r--r-- | src/gui/dialogs/export_disass.h (renamed from src/gui/dialogs/export.h) | 8 | ||||
-rw-r--r-- | src/gui/dialogs/export_graph.c | 459 | ||||
-rw-r--r-- | src/gui/dialogs/export_graph.h | 41 | ||||
-rw-r--r-- | src/gui/dialogs/export_graph.ui | 196 | ||||
-rw-r--r-- | src/gui/dialogs/gresource.xml | 1 | ||||
-rw-r--r-- | src/gui/menus/binary.c | 104 | ||||
-rw-r--r-- | src/gui/menus/binary.h | 3 | ||||
-rw-r--r-- | src/gui/menus/menubar.c | 2 |
18 files changed, 946 insertions, 38 deletions
diff --git a/src/glibext/gbufferview.c b/src/glibext/gbufferview.c index 2239d23..c5021f2 100644 --- a/src/glibext/gbufferview.c +++ b/src/glibext/gbufferview.c @@ -1100,6 +1100,7 @@ bool g_buffer_view_highlight_segments(GBufferView *view, gint x, gint y, const G * options = règles d'affichage des colonnes modulables. * * offsets = décalages supplémentaires à appliquer. * * selected = ordonnée d'une ligne sélectionnée ou NULL. * +* export = indique si la vue est en cours d'exportation. * * * * Description : Imprime la visualisation du tampon de lignes quelconques. * * * @@ -1109,12 +1110,13 @@ bool g_buffer_view_highlight_segments(GBufferView *view, gint x, gint y, const G * * ******************************************************************************/ -void g_buffer_view_draw(const GBufferView *view, cairo_t *cr, gint virt_y, const cairo_rectangle_int_t *area, const GDisplayOptions *options, const line_width_summary *offsets, gint *selected) +void g_buffer_view_draw(const GBufferView *view, cairo_t *cr, gint virt_y, const cairo_rectangle_int_t *area, const GDisplayOptions *options, const line_width_summary *offsets, gint *selected, bool export) { gint line_height; /* Hauteur d'une ligne */ gint cr_y; /* Ordonnée pour le dessin */ size_t first; /* Première ligne visée */ size_t last; /* Dernière ligne visée */ + segcnt_list *highlighted; /* Segments mis en évidence */ line_height = g_buffer_cache_get_line_height(view->cache); @@ -1144,7 +1146,15 @@ void g_buffer_view_draw(const GBufferView *view, cairo_t *cr, gint virt_y, const if (selected != NULL) *selected -= cr_y; - g_buffer_cache_draw(view->cache, cr, first, last, area, options, offsets, selected, view->highlighted); + if (export) + highlighted = init_segment_content_list(); + else + highlighted = view->highlighted; + + g_buffer_cache_draw(view->cache, cr, first, last, area, options, offsets, selected, highlighted); + + if (export) + unref_segment_content_list(highlighted); } diff --git a/src/glibext/gbufferview.h b/src/glibext/gbufferview.h index 7e29cfd..9d40cbd 100644 --- a/src/glibext/gbufferview.h +++ b/src/glibext/gbufferview.h @@ -98,7 +98,7 @@ bool g_buffer_view_unhighlight_segments(GBufferView *); bool g_buffer_view_highlight_segments(GBufferView *, gint, gint, const GDisplayOptions *, const line_width_summary *); /* Imprime la visualisation du tampon de lignes quelconques. */ -void g_buffer_view_draw(const GBufferView *, cairo_t *, gint, const cairo_rectangle_int_t *, const GDisplayOptions *, const line_width_summary *, gint *); +void g_buffer_view_draw(const GBufferView *, cairo_t *, gint, const cairo_rectangle_int_t *, const GDisplayOptions *, const line_width_summary *, gint *, bool); diff --git a/src/gtkext/gtkbufferdisplay.c b/src/gtkext/gtkbufferdisplay.c index 0794dd4..01df570 100644 --- a/src/gtkext/gtkbufferdisplay.c +++ b/src/gtkext/gtkbufferdisplay.c @@ -382,6 +382,7 @@ static gboolean gtk_buffer_display_draw(GtkWidget *widget, cairo_t *cr) GtkBufferDisplay *display; /* Autre version du composant */ GtkDisplayPanel *parent; /* Autre version du composant */ GdkWindow *window; /* Fenêtre à redessiner */ + GtkAllocation allocation; /* Aire complète du composant */ cairo_region_t *region; /* Région visible à redessiner */ cairo_rectangle_int_t area; /* Surface correspondante */ GtkStyleContext *context; /* Contexte du thème actuel */ @@ -401,9 +402,22 @@ static gboolean gtk_buffer_display_draw(GtkWidget *widget, cairo_t *cr) gtk_cairo_transform_to_window(cr, widget, window); - region = gdk_window_get_clip_region(window); - cairo_region_get_extents(region, &area); - cairo_region_destroy(region); + if (parent->export) + { + gtk_widget_get_allocation(widget, &allocation); + + area.x = 0; + area.y = 0; + area.width = allocation.width; + area.height = allocation.height; + + } + else + { + region = gdk_window_get_clip_region(window); + cairo_region_get_extents(region, &area); + cairo_region_destroy(region); + } context = gtk_widget_get_style_context(widget); @@ -473,7 +487,7 @@ static gboolean gtk_buffer_display_draw(GtkWidget *widget, cairo_t *cr) g_generic_config_get_value(get_main_configuration(), MPK_SELECTION_LINE, &sel_line); sel_line &= gtk_widget_has_focus(widget); - if (!sel_line || display->cursor == NULL || !g_line_cursor_is_valid(display->cursor)) + if (!sel_line || display->cursor == NULL || !g_line_cursor_is_valid(display->cursor) || parent->export) selected = NULL; else { @@ -484,7 +498,8 @@ static gboolean gtk_buffer_display_draw(GtkWidget *widget, cairo_t *cr) area.x -= virt_x; virt_y += area.y; - g_buffer_view_draw(display->view, cr, virt_y, &area, parent->options, &display->offsets, selected); + g_buffer_view_draw(display->view, cr, virt_y, &area, parent->options, &display->offsets, + selected, parent->export); } diff --git a/src/gtkext/gtkdisplaypanel-int.h b/src/gtkext/gtkdisplaypanel-int.h index 62c990e..7be5616 100644 --- a/src/gtkext/gtkdisplaypanel-int.h +++ b/src/gtkext/gtkdisplaypanel-int.h @@ -71,6 +71,9 @@ typedef GLineCursor * (* get_cursor_fc) (const GtkDisplayPanel *); /* Place en cache un rendu destiné à l'aperçu graphique rapide. */ typedef void (* cache_glance_fc) (GtkDisplayPanel *, cairo_t *, const GtkAllocation *, double); +/* Marque ou non le composant pour une exportation prochaine. */ +typedef void (* prepare_export_fc) (GtkDisplayPanel *, bool); + /* Composant d'affichage générique (instance) */ @@ -89,6 +92,8 @@ struct _GtkDisplayPanel GLoadedBinary *binary; /* Binaire à visualiser */ + bool export; /* Exportation du rendu ? */ + }; /* Composant d'affichage générique (classe) */ @@ -107,6 +112,8 @@ struct _GtkDisplayPanelClass get_cursor_fc get_cursor; /* Fourniture d'une position */ cache_glance_fc cache_glance; /* Cache de la mignature */ + prepare_export_fc prepare_export; /* Préparation d'exportation */ + }; /* Propriétés propres au composant d'affichage */ diff --git a/src/gtkext/gtkdisplaypanel.c b/src/gtkext/gtkdisplaypanel.c index 010d648..b32adfa 100644 --- a/src/gtkext/gtkdisplaypanel.c +++ b/src/gtkext/gtkdisplaypanel.c @@ -181,6 +181,8 @@ static void gtk_display_panel_init(GtkDisplayPanel *panel) gtk_widget_set_has_window(GTK_WIDGET(panel), TRUE); gtk_widget_set_can_focus(GTK_WIDGET(panel), TRUE); + panel->export = false; + } @@ -756,6 +758,33 @@ void gtk_display_panel_show_border(GtkDisplayPanel *panel, bool show) /****************************************************************************** * * +* Paramètres : panel = composant GTK à mettre à jour. * +* export = préparation d'une exportation complète du rendu ? * +* * +* Description : Marque ou non le composant pour une exportation prochaine. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_display_panel_prepare_export(GtkDisplayPanel *panel, bool export) +{ + GtkDisplayPanelClass *class; /* Classe associée au composant*/ + + panel->export = export; + + class = GTK_DISPLAY_PANEL_GET_CLASS(panel); + + if (class->prepare_export != NULL) + class->prepare_export(panel, export); + +} + + +/****************************************************************************** +* * * Paramètres : panel = composant GTK à venir consulter. * * cr = contexte graphique associé à l'événement. * * offset = décalage éventuel à appliquer. * diff --git a/src/gtkext/gtkdisplaypanel.h b/src/gtkext/gtkdisplaypanel.h index 954991c..937d41d 100644 --- a/src/gtkext/gtkdisplaypanel.h +++ b/src/gtkext/gtkdisplaypanel.h @@ -55,11 +55,8 @@ GType gtk_display_panel_get_type(void); /* Définit si une bordure est à afficher. */ void gtk_display_panel_show_border(GtkDisplayPanel *, bool); -/* Définit si les adresses doivent apparaître dans le rendu. */ -void gtk_display_panel_set_addresses_display(GtkDisplayPanel *, bool); - -/* Définit si le code doit apparaître dans le rendu. */ -void gtk_display_panel_set_code_display(GtkDisplayPanel *, bool); +/* Marque ou non le composant pour une exportation prochaine. */ +void gtk_display_panel_prepare_export(GtkDisplayPanel *, bool); /* Indique la position d'affichage d'un emplacement donné. */ bool gtk_display_panel_get_cursor_coordinates(const GtkDisplayPanel *, const GLineCursor *, gint *, gint *, ScrollPositionTweak); diff --git a/src/gtkext/gtkgraphdisplay.c b/src/gtkext/gtkgraphdisplay.c index fb89b2e..97bb4d3 100644 --- a/src/gtkext/gtkgraphdisplay.c +++ b/src/gtkext/gtkgraphdisplay.c @@ -146,6 +146,9 @@ static GLineCursor *gtk_graph_display_get_cursor(const GtkGraphDisplay *); /* Place en cache un rendu destiné à l'aperçu graphique rapide. */ static void gtk_graph_display_cache_glance(GtkGraphDisplay *, cairo_t *, const GtkAllocation *, double); +/* Marque ou non le composant pour une exportation prochaine. */ +static void gtk_graph_display_prepare_export(GtkGraphDisplay *, bool); + /* Supprime tout contenu de l'afficheur de code en graphique. */ static void gtk_graph_display_reset(GtkGraphDisplay *, bool); @@ -202,6 +205,8 @@ static void gtk_graph_display_class_init(GtkGraphDisplayClass *class) panel_class->get_cursor = (get_cursor_fc)gtk_graph_display_get_cursor; panel_class->cache_glance = (cache_glance_fc)gtk_graph_display_cache_glance; + panel_class->prepare_export = (prepare_export_fc)gtk_graph_display_prepare_export; + } @@ -465,15 +470,15 @@ static void gtk_graph_display_adjust_scroll_value(GtkGraphDisplay *display, GtkA static gboolean gtk_graph_display_draw(GtkWidget *widget, cairo_t *cr, GtkGraphDisplay *display) { - size_t i; /* Boucle de parcours */ cairo_surface_t *pat_image; /* Fond du futur pinceau */ cairo_t *pat_cr; /* Pinceau pour le pinceau */ cairo_pattern_t *pattern; /* Patron de remplissage */ double degrees; /* Conversion en degrés */ + size_t i; /* Boucle de parcours */ /* Eventuel fond pour la zone de compression */ - if (display->may_collapsing) + if (display->may_collapsing && !GTK_DISPLAY_PANEL(display)->export) { /* Préparation du pinceau */ @@ -1158,6 +1163,35 @@ static void gtk_graph_display_cache_glance(GtkGraphDisplay *display, cairo_t *cr /****************************************************************************** * * +* Paramètres : display = composant GTK à mettre à jour. * +* export = préparation d'une exportation complète du rendu ? * +* * +* Description : Marque ou non le composant pour une exportation prochaine. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_graph_display_prepare_export(GtkGraphDisplay *display, bool export) +{ + void prepare_child_export(GtkWidget *child, gpointer unused) + { + if (!GTK_IS_BUFFER_DISPLAY(child)) + return; + + gtk_display_panel_prepare_export(GTK_DISPLAY_PANEL(child), export); + + } + + gtk_container_foreach(GTK_CONTAINER(display->support), (GtkCallback)prepare_child_export, NULL); + +} + + +/****************************************************************************** +* * * Paramètres : - * * * * Description : Crée un nouveau composant pour l'affichage en graphique. * @@ -1177,6 +1211,31 @@ GtkWidget *gtk_graph_display_new(void) /****************************************************************************** * * +* Paramètres : display = composant GTK à consulter. * +* * +* Description : Fournit le support utilisé pour le rendu graphique. * +* * +* Retour : Composant GTK de support. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkWidget *gtk_graph_display_get_support(GtkGraphDisplay *display) +{ + GtkWidget *result; /* Instance à retourner */ + + result = display->support; + + g_object_ref(G_OBJECT(result)); + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : display = composant GTK à mettre à jour. * * widget = composant GTK à insérer. * * x = abscisse du point d'insertion. * diff --git a/src/gtkext/gtkgraphdisplay.h b/src/gtkext/gtkgraphdisplay.h index e1ff00b..78008a1 100644 --- a/src/gtkext/gtkgraphdisplay.h +++ b/src/gtkext/gtkgraphdisplay.h @@ -32,12 +32,12 @@ -#define GTK_TYPE_GRAPH_DISPLAY (gtk_graph_display_get_type()) -#define GTK_GRAPH_DISPLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_GRAPH_DISPLAY, GtkGraphDisplay)) -#define GTK_GRAPH_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_GRAPH_DISPLAY, GtkGraphDisplayClass)) -#define GTK_IS_GRAPH_DISPLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_GRAPH_DISPLAY)) -#define GTK_IS_GRAPH_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_GRAPH_DISPLAY)) -#define GTK_GRAPH_DISPLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_GRAPH_DISPLAY, GtkGraphDisplayClass)) +#define GTK_TYPE_GRAPH_DISPLAY (gtk_graph_display_get_type()) +#define GTK_GRAPH_DISPLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_GRAPH_DISPLAY, GtkGraphDisplay)) +#define GTK_GRAPH_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_GRAPH_DISPLAY, GtkGraphDisplayClass)) +#define GTK_IS_GRAPH_DISPLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_GRAPH_DISPLAY)) +#define GTK_IS_GRAPH_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_GRAPH_DISPLAY)) +#define GTK_GRAPH_DISPLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_GRAPH_DISPLAY, GtkGraphDisplayClass)) /* Composant d'affichage sous forme graphique (instance) */ @@ -53,6 +53,9 @@ GType gtk_graph_display_get_type(void); /* Crée un nouveau composant pour l'affichage en graphique. */ GtkWidget *gtk_graph_display_new(void); +/* Fournit le support utilisé pour le rendu graphique. */ +GtkWidget *gtk_graph_display_get_support(GtkGraphDisplay *); + /* Place une vue sous forme de bloc dans le graphique. */ void gtk_graph_display_put(GtkGraphDisplay *, GtkWidget *, const GtkAllocation *); diff --git a/src/gui/dialogs/Makefile.am b/src/gui/dialogs/Makefile.am index 869fa7a..ae5180b 100644 --- a/src/gui/dialogs/Makefile.am +++ b/src/gui/dialogs/Makefile.am @@ -5,6 +5,7 @@ noinst_LTLIBRARIES = libguidialogs.la UI_FILES = \ bookmark.ui \ + export_graph.ui \ identity.ui \ preferences.ui \ prefs_fgraph.ui \ @@ -14,7 +15,8 @@ UI_FILES = \ libguidialogs_la_SOURCES = \ about.h about.c \ bookmark.h bookmark.c \ - export.h export.c \ + export_disass.h export_disass.c \ + export_graph.h export_graph.c \ goto.h goto.c \ gotox.h gotox.c \ identity.h identity.c \ diff --git a/src/gui/dialogs/export.c b/src/gui/dialogs/export_disass.c index 5a66d02..be7d2a9 100644 --- a/src/gui/dialogs/export.c +++ b/src/gui/dialogs/export_disass.c @@ -1,6 +1,6 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * export.c - assistant d'exportation de contenu binaire + * export_disass.c - assistant d'exportation de contenu binaire * * Copyright (C) 2015-2017 Cyrille Bagard * @@ -21,7 +21,7 @@ */ -#include "export.h" +#include "export_disass.h" #include <assert.h> diff --git a/src/gui/dialogs/export.h b/src/gui/dialogs/export_disass.h index 209dea8..86fe4a7 100644 --- a/src/gui/dialogs/export.h +++ b/src/gui/dialogs/export_disass.h @@ -1,6 +1,6 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * export.h - prototypes pour l'assistant d'exportation de contenu binaire + * export_disass.h - prototypes pour l'assistant d'exportation de contenu binaire * * Copyright (C) 2015-2017 Cyrille Bagard * @@ -21,8 +21,8 @@ */ -#ifndef _GUI_DIALOGS_EXPORT_H -#define _GUI_DIALOGS_EXPORT_H +#ifndef _GUI_DIALOGS_EXPORT_DISASS_H +#define _GUI_DIALOGS_EXPORT_DISASS_H #include <gtk/gtk.h> @@ -37,4 +37,4 @@ void run_export_assistant(GLoadedBinary *, GtkWindow *); -#endif /* _GUI_DIALOGS_EXPORT_H */ +#endif /* _GUI_DIALOGS_EXPORT_DISASS_H */ diff --git a/src/gui/dialogs/export_graph.c b/src/gui/dialogs/export_graph.c new file mode 100644 index 0000000..55556b6 --- /dev/null +++ b/src/gui/dialogs/export_graph.c @@ -0,0 +1,459 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * export_graph.c - assistant d'exportation de vues graphiques + * + * Copyright (C) 2019 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "export_graph.h" + + +#include <assert.h> +#include <cairo-pdf.h> +#include <cairo-svg.h> +#include <cairo-ps.h> +#include <malloc.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> + + +#include "../../common/extstr.h" +#include "../../core/logs.h" +#include "../../glibext/gbinarycursor.h" +#include "../../glibext/gloadedpanel.h" +#include "../../gtkext/gtkdisplaypanel.h" + + + +/* Ferme l'assistant sans dérouler la procédure. */ +static void graph_export_assistant_cancel(GtkAssistant *, GtkBuilder *); + +/* Réalise l'exportation du contenu sous la forme choisie. */ +static void graph_export_assistant_close(GtkAssistant *, GtkBuilder *); + +/* Actualise l'extension du fichier de sortie. */ +static void on_output_format_toggled(GtkToggleButton *, GtkBuilder *); + +/* Prend note d'un changement dans la saisie du fichier final. */ +static void on_output_filename_changed(GtkEditable *, GtkBuilder *); + +/* Réagit à la demande de sélection d'un nouveau fichier final. */ +static void on_output_filename_selection(GtkButton *, GtkBuilder *); + + + +/****************************************************************************** +* * +* Paramètres : binary = contenu bnaire chargé en mémoire. * +* display = vue graphique à traiter. * +* parent = fenêtre principale de l'éditeur. * +* * +* Description : Crée et affiche un assistant d'aide à l'exportation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void run_graph_export_assistant(GLoadedBinary *binary, GtkGraphDisplay *display, GtkWindow *parent) +{ + GtkBuilder *builder; /* Constructeur utilisé */ + GtkWidget *assistant; /* Fenêtre à afficher */ +#if !defined CAIRO_HAS_PDF_SURFACE || !defined CAIRO_HAS_PS_SURFACE || !defined CAIRO_HAS_SVG_SURFACE + GtkWIdget *button; /* Bouton de sélection */ +#endif + GLineCursor *cursor; /* Position dans la vue */ + vmpa2t target; /* Localisation ciblée */ + GBinFormat *format; /* Format de fichier reconnu */ + bool status; /* Bilan d'un appel */ + GBinSymbol *symbol; /* Symbole affiché */ + char *label; /* Etiquette humaine associée */ + GtkEntry *entry; /* Zone de texte */ + + builder = gtk_builder_new_from_resource("/org/chrysalide/gui/dialogs/export_graph.ui"); + + assistant = GTK_WIDGET(gtk_builder_get_object(builder, "window")); + + gtk_window_set_transient_for(GTK_WINDOW(assistant), parent); + + /* Validation des formats de sortie */ + +#ifndef CAIRO_HAS_PDF_SURFACE + button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "as_pdf")); + gtk_widget_set_sensitive(button, FALSE); +#endif + +#ifndef CAIRO_HAS_PS_SURFACE + button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "as_ps")); + gtk_widget_set_sensitive(button, FALSE); +#endif + +#ifndef CAIRO_HAS_SVG_SURFACE + button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "as_svg")); + gtk_widget_set_sensitive(button, FALSE); +#endif + + /* Choix du fichier d'exportation par défaut */ + + cursor = g_loaded_panel_get_cursor(G_LOADED_PANEL(display)); + + if (cursor != NULL) + { + g_binary_cursor_get_info(G_BINARY_CURSOR(cursor), &target); + + g_object_unref(G_OBJECT(cursor)); + + format = G_BIN_FORMAT(g_loaded_binary_get_format(binary)); + + status = g_binary_format_find_symbol_for(format, &target, &symbol); + + g_object_unref(G_OBJECT(format)); + + if (status) + { + label = g_binary_symbol_get_label(symbol); + + entry = GTK_ENTRY(gtk_builder_get_object(builder, "output")); + + gtk_entry_set_text(entry, label); + + free(label); + + g_object_unref(G_OBJECT(symbol)); + + } + + } + + on_output_format_toggled(NULL, builder); + + /* Mémorisation pour les traitement */ + + g_object_ref(G_OBJECT(display)); + g_object_set_data_full(G_OBJECT(assistant), "display", display, g_object_unref); + + /* Connexion des signaux */ + + gtk_builder_add_callback_symbols(builder, + "graph_export_assistant_cancel", G_CALLBACK(graph_export_assistant_cancel), + "graph_export_assistant_close", G_CALLBACK(graph_export_assistant_close), + "on_output_format_toggled", G_CALLBACK(on_output_format_toggled), + "on_output_filename_changed", G_CALLBACK(on_output_filename_changed), + "on_output_filename_selection", G_CALLBACK(on_output_filename_selection), + NULL); + + gtk_builder_connect_signals(builder, builder); + + gtk_widget_show_all(assistant); + +} + + +/****************************************************************************** +* * +* Paramètres : assistant = fenêtre à compléter et référencement global. * +* builder = espace de référencement global. * +* * +* Description : Ferme l'assistant sans dérouler la procédure. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void graph_export_assistant_cancel(GtkAssistant *assistant, GtkBuilder *builder) +{ + g_object_set_data(G_OBJECT(assistant), "binary", NULL); + g_object_set_data(G_OBJECT(assistant), "display", NULL); + + g_object_ref(G_OBJECT(assistant)); + gtk_widget_destroy(GTK_WIDGET(assistant)); + + g_object_unref(G_OBJECT(builder)); + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton à l'origine de la procédure. * +* builder = espace de référencement global. * +* * +* Description : Réalise l'exportation du contenu sous la forme choisie. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void graph_export_assistant_close(GtkAssistant *assistant, GtkBuilder *builder) +{ + GtkEntry *entry; /* Zone de texte */ + const gchar *cur_filename; /* Fichier de sortie courant */ + GtkGraphDisplay *display; /* Vue grahique associée */ + GtkRequisition size; /* Taille idéale associée */ + bool as_png; /* Exportation en PNG ? */ + GtkToggleButton *button; /* Bouton de sélection */ + gboolean state; /* Etat de la sélection */ + cairo_surface_t *surface; /* Zone de dessin nouvelle */ + cairo_t *cr; /* Contexte de rendu */ + GtkWidget *widget; /* Composant GTK à dessiner */ + cairo_status_t status; /* Bilan de l'écriture */ + + /* Collecte des informations de base */ + + entry = GTK_ENTRY(gtk_builder_get_object(builder, "output")); + + cur_filename = gtk_entry_get_text(entry); + + display = GTK_GRAPH_DISPLAY(g_object_get_data(G_OBJECT(assistant), "display")); + + gtk_widget_get_preferred_size(GTK_WIDGET(display), &size, NULL); + + /* Préparation du fichier de sortie */ + + as_png = false; + + button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "as_pdf")); + state = gtk_toggle_button_get_active(button); + + if (state) + { + surface = cairo_pdf_surface_create(cur_filename, size.width, size.height); + goto do_export; + } + + button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "as_ps")); + state = gtk_toggle_button_get_active(button); + + if (state) + { + surface = cairo_ps_surface_create(cur_filename, size.width, size.height); + goto do_export; + } + + button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "as_svg")); + state = gtk_toggle_button_get_active(button); + + if (state) + { + surface = cairo_svg_surface_create(cur_filename, size.width, size.height); + goto do_export; + } + + surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, size.width, size.height); + as_png = true; + + /* Exportation du rendu */ + + do_export: + + cr = cairo_create(surface); + + gtk_display_panel_prepare_export(GTK_DISPLAY_PANEL(display), true); + + widget = gtk_graph_display_get_support(display); + + gtk_widget_draw(widget, cr); + + g_object_unref(G_OBJECT(widget)); + + gtk_display_panel_prepare_export(GTK_DISPLAY_PANEL(display), false); + + if (as_png) + { + status = cairo_surface_write_to_png(surface, cur_filename); + + if (status != CAIRO_STATUS_SUCCESS) + log_variadic_message(LMT_ERROR, "Export error: %s (%u)", cairo_status_to_string(status), status); + + } + else + cairo_show_page(cr); + + cairo_destroy(cr); + + cairo_surface_destroy(surface); + + graph_export_assistant_cancel(assistant, builder); + +} + + +/****************************************************************************** +* * +* Paramètres : tbutton = bouton à l'origine de la procédure. * +* builder = espace de référencement global. * +* * +* Description : Actualise l'extension du fichier de sortie. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_output_format_toggled(GtkToggleButton *tbutton, GtkBuilder *builder) +{ + GtkToggleButton *button; /* Bouton de sélection */ + gboolean state; /* Etat de la sélection */ + const char *ext; /* Extension attendue */ + GtkEntry *entry; /* Zone de texte */ + const gchar *cur_filename; /* Fichier de sortie courant */ + char *found; /* Point final trouvé */ + char *new_filename; /* Nouveau fichier de sortie */ + + button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "as_pdf")); + state = gtk_toggle_button_get_active(button); + + if (state) + { + ext = "pdf"; + goto do_update; + } + + button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "as_ps")); + state = gtk_toggle_button_get_active(button); + + if (state) + { + ext = "ps"; + goto do_update; + } + + button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "as_svg")); + state = gtk_toggle_button_get_active(button); + + if (state) + { + ext = "svg"; + goto do_update; + } + + ext = "png"; + + do_update: + + entry = GTK_ENTRY(gtk_builder_get_object(builder, "output")); + + cur_filename = gtk_entry_get_text(entry); + + found = rindex(cur_filename, '.'); + + if (found == NULL) + asprintf(&new_filename, "%s.%s", cur_filename, ext); + + else + { + new_filename = strndup(cur_filename, found - cur_filename); + + new_filename = stradd(new_filename, "."); + new_filename = stradd(new_filename, ext); + + } + + gtk_entry_set_text(entry, new_filename); + + free(new_filename); + +} + + +/****************************************************************************** +* * +* Paramètres : editable = zone de texte à l'origine de la procédure. * +* builder = espace de référencement global. * +* * +* Description : Prend note d'un changement dans la saisie du fichier final. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_output_filename_changed(GtkEditable *editable, GtkBuilder *builder) +{ + const gchar *cur_filename; /* Fichier de sortie courant */ + GtkAssistant *assistant; /* Fenêtre affichée */ + GtkWidget *page; /* Composant associé à une page*/ + + cur_filename = gtk_entry_get_text(GTK_ENTRY(editable)); + + assistant = GTK_ASSISTANT(gtk_builder_get_object(builder, "window")); + + page = gtk_assistant_get_nth_page(assistant, 1); + + gtk_assistant_set_page_complete(assistant, page, strlen(cur_filename) > 0); + +} + + +/****************************************************************************** +* * +* Paramètres : button = bouton à l'origine de la procédure. * +* builder = espace de référencement global. * +* * +* Description : Réagit à la demande de sélection d'un nouveau fichier final. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_output_filename_selection(GtkButton *button, GtkBuilder *builder) +{ + GtkWindow *assistant; /* Fenêtre affichée */ + GtkEntry *entry; /* Zone de texte */ + const gchar *cur_filename; /* Fichier de sortie courant */ + GtkWidget *dialog; /* Boîte à afficher */ + gchar *new_filename; /* Nouveau fichier de sortie */ + + assistant = GTK_WINDOW(gtk_builder_get_object(builder, "window")); + + entry = GTK_ENTRY(gtk_builder_get_object(builder, "output")); + + cur_filename = gtk_entry_get_text(entry); + + dialog = gtk_file_chooser_dialog_new(_("Save the output as..."), assistant, + GTK_FILE_CHOOSER_ACTION_SAVE, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Save"), GTK_RESPONSE_ACCEPT, + NULL); + + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), cur_filename); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) + { + new_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + + gtk_entry_set_text(entry, new_filename); + + g_free(new_filename); + + } + + gtk_widget_destroy(dialog); + +} diff --git a/src/gui/dialogs/export_graph.h b/src/gui/dialogs/export_graph.h new file mode 100644 index 0000000..a88db3e --- /dev/null +++ b/src/gui/dialogs/export_graph.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * export_graph.h - prototypes pour l'assistant d'exportation de vues graphiques + * + * Copyright (C) 2019 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GUI_DIALOGS_EXPORT_GRAPH_H +#define _GUI_DIALOGS_EXPORT_GRAPH_H + + +#include <gtk/gtk.h> + + +#include "../../analysis/binary.h" +#include "../../gtkext/gtkgraphdisplay.h" + + + +/* Crée et affiche un assistant d'aide à l'exportation. */ +void run_graph_export_assistant(GLoadedBinary *, GtkGraphDisplay *, GtkWindow *); + + + +#endif /* _GUI_DIALOGS_EXPORT_GRAPH_H */ diff --git a/src/gui/dialogs/export_graph.ui b/src/gui/dialogs/export_graph.ui new file mode 100644 index 0000000..1c34bd3 --- /dev/null +++ b/src/gui/dialogs/export_graph.ui @@ -0,0 +1,196 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.21.0 --> +<interface> + <requires lib="gtk+" version="3.20"/> + <object class="GtkAssistant" id="window"> + <property name="can_focus">False</property> + <property name="modal">True</property> + <property name="window_position">center</property> + <property name="default_width">440</property> + <property name="default_height">250</property> + <property name="type_hint">dialog</property> + <property name="use_header_bar">1</property> + <signal name="cancel" handler="graph_export_assistant_cancel" swapped="no"/> + <signal name="close" handler="graph_export_assistant_close" swapped="no"/> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="margin_left">8</property> + <property name="margin_right">8</property> + <property name="margin_top">8</property> + <property name="margin_bottom">8</property> + <property name="orientation">vertical</property> + <property name="spacing">8</property> + <child> + <object class="GtkRadioButton" id="as_png"> + <property name="label" translatable="yes">PNG image</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="active">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_output_format_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="as_pdf"> + <property name="label" translatable="yes">PDF document</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + <property name="group">as_png</property> + <signal name="toggled" handler="on_output_format_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="as_ps"> + <property name="label" translatable="yes">PostScript document</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + <property name="group">as_png</property> + <signal name="toggled" handler="on_output_format_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="as_svg"> + <property name="label" translatable="yes">SVG image</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="draw_indicator">True</property> + <property name="group">as_png</property> + <signal name="toggled" handler="on_output_format_toggled" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="title" translatable="yes">Export format</property> + <property name="complete">True</property> + <property name="has_padding">False</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="valign">center</property> + <property name="margin_left">8</property> + <property name="margin_right">8</property> + <property name="margin_top">8</property> + <property name="margin_bottom">8</property> + <property name="orientation">vertical</property> + <property name="spacing">8</property> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkEntry" id="output"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton"> + <property name="label" translatable="yes">Parcourir...</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <signal name="clicked" handler="on_output_filename_selection" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Warning: the output file will be overwritten if it exists. </property> + <property name="xalign">0</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="page_type">confirm</property> + <property name="title" translatable="yes">Output file</property> + <property name="complete">True</property> + <property name="has_padding">False</property> + </packing> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child internal-child="action_area"> + <object class="GtkBox"> + <property name="can_focus">False</property> + <property name="halign">end</property> + <property name="margin_left">6</property> + <property name="margin_right">6</property> + <property name="margin_start">6</property> + <property name="margin_end">6</property> + <property name="margin_top">6</property> + <property name="margin_bottom">6</property> + <property name="spacing">6</property> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </object> + </child> + </object> +</interface> diff --git a/src/gui/dialogs/gresource.xml b/src/gui/dialogs/gresource.xml index ff606ba..1d18e39 100644 --- a/src/gui/dialogs/gresource.xml +++ b/src/gui/dialogs/gresource.xml @@ -2,6 +2,7 @@ <gresources> <gresource prefix="/org/chrysalide/gui/dialogs"> <file compressed="true">bookmark.ui</file> + <file compressed="true">export_graph.ui</file> <file compressed="true">identity.ui</file> <file compressed="true">preferences.ui</file> <file compressed="true">prefs_fgraph.ui</file> diff --git a/src/gui/menus/binary.c b/src/gui/menus/binary.c index c4c4e22..3a143da 100644 --- a/src/gui/menus/binary.c +++ b/src/gui/menus/binary.c @@ -31,11 +31,13 @@ #include "../agroup.h" #include "../editem-int.h" #include "../core/global.h" -#include "../dialogs/export.h" +#include "../dialogs/export_disass.h" +#include "../dialogs/export_graph.h" #include "../dialogs/gotox.h" #include "../dialogs/storage.h" #include "../../gtkext/easygtk.h" #include "../../gtkext/gtkdisplaypanel.h" +#include "../../gtkext/gtkgraphdisplay.h" @@ -48,8 +50,11 @@ static void mcb_binary_attach_debugger(GtkMenuItem *, GMenuBar *); /* Réagit au menu "Binaire -> Enregistrements...". */ static void mcb_binary_storage(GtkMenuItem *, GMenuBar *); -/* Réagit au menu "Binaire -> Exporter...". */ -static void mcb_binary_export(GtkMenuItem *, GMenuBar *); +/* Réagit au menu "Binaire -> Exporter -> Désassemblage". */ +static void mcb_binary_export_disass(GtkMenuItem *, gpointer); + +/* Réagit au menu "Binaire -> Exporter -> Vue graphique". */ +static void mcb_binary_export_graph(GtkMenuItem *, gpointer); @@ -70,7 +75,9 @@ GtkWidget *build_menu_binary(GObject *ref, GMenuBar *bar) { GtkWidget *result; /* Support à retourner */ GtkWidget *menubar; /* Support pour éléments */ - GtkWidget *submenuitem; /* Sous-élément de menu */ + GtkWidget *submenuitem; /* Sous-élément de menu #1 */ + GtkWidget *deepmenubar; /* Support pour éléments #2 */ + GtkWidget *deepmenuitem; /* Sous-élément de menu #2 */ result = gtk_menu_item_new_with_mnemonic(_("_Binary")); gtk_widget_show(result); @@ -96,10 +103,26 @@ GtkWidget *build_menu_binary(GObject *ref, GMenuBar *bar) G_CALLBACK(mcb_binary_storage), bar); gtk_container_add(GTK_CONTAINER(menubar), submenuitem); - submenuitem = qck_create_menu_item(ref, "mnu_binary_export", _("Export..."), - G_CALLBACK(mcb_binary_export), bar); + /* Séparation */ + + submenuitem = qck_create_menu_separator(); + gtk_container_add(GTK_CONTAINER(menubar), submenuitem); + + /* Exportations */ + + submenuitem = qck_create_menu_item(NULL, NULL, _("Export"), NULL, NULL); gtk_container_add(GTK_CONTAINER(menubar), submenuitem); + deepmenubar = qck_create_menu(GTK_MENU_ITEM(submenuitem)); + + deepmenuitem = qck_create_menu_item(ref, "mnu_binary_export_disass", _("Disassembly"), + G_CALLBACK(mcb_binary_export_disass), bar); + gtk_container_add(GTK_CONTAINER(deepmenubar), deepmenuitem); + + deepmenuitem = qck_create_menu_item(ref, "mnu_binary_export_graph", _("Graph view"), + G_CALLBACK(mcb_binary_export_graph), NULL); + gtk_container_add(GTK_CONTAINER(deepmenubar), deepmenuitem); + return result; } @@ -137,7 +160,37 @@ void update_access_for_content_in_menu_binary(GLoadedContent *new) item = GTK_WIDGET(g_object_get_data(ref, "mnu_binary_storage")); gtk_widget_set_sensitive(item, access); - item = GTK_WIDGET(g_object_get_data(ref, "mnu_binary_export")); + item = GTK_WIDGET(g_object_get_data(ref, "mnu_binary_export_disass")); + gtk_widget_set_sensitive(item, access); + +} + + +/****************************************************************************** +* * +* Paramètres : new = nouvelle vue du contenu chargé analysé. * +* * +* Description : Lance une actualisation du fait d'un changement de support. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void update_access_for_view_in_menu_binary(GLoadedPanel *new) +{ + GObject *ref; /* Espace de référencements */ + gboolean access; /* Accès à déterminer */ + GtkWidget *item; /* Elément de menu à traiter */ + + ref = get_global_ref(); + + /* Exportation de graphiques */ + + access = GTK_IS_GRAPH_DISPLAY(new); + + item = GTK_WIDGET(g_object_get_data(ref, "mnu_binary_export_graph")); gtk_widget_set_sensitive(item, access); } @@ -249,9 +302,9 @@ static void mcb_binary_storage(GtkMenuItem *menuitem, GMenuBar *bar) /****************************************************************************** * * * Paramètres : menuitem = élément de menu sélectionné. * -* bar = barre de menu parente. * +* unused = adresse non utilisée ici. * * * -* Description : Réagit au menu "Binaire -> Exporter...". * +* Description : Réagit au menu "Binaire -> Exporter -> Désassemblage". * * * * Retour : - * * * @@ -259,7 +312,7 @@ static void mcb_binary_storage(GtkMenuItem *menuitem, GMenuBar *bar) * * ******************************************************************************/ -static void mcb_binary_export(GtkMenuItem *menuitem, GMenuBar *bar) +static void mcb_binary_export_disass(GtkMenuItem *menuitem, gpointer unused) { GLoadedBinary *binary; /* Edition courante */ @@ -270,3 +323,34 @@ static void mcb_binary_export(GtkMenuItem *menuitem, GMenuBar *bar) g_object_unref(G_OBJECT(binary)); } + + +/****************************************************************************** +* * +* Paramètres : menuitem = élément de menu sélectionné. * +* unused = adresse non utilisée ici. * +* * +* Description : Réagit au menu "Binaire -> Exporter -> Vue graphique". * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void mcb_binary_export_graph(GtkMenuItem *menuitem, gpointer unused) +{ + GtkGraphDisplay *panel; /* Panneau de code courant */ + GLoadedBinary *binary; /* Edition courante */ + + binary = G_LOADED_BINARY(get_current_content()); + + panel = GTK_GRAPH_DISPLAY(get_current_view()); + + run_graph_export_assistant(binary, panel, get_editor_window()); + + g_object_unref(G_OBJECT(panel)); + + g_object_unref(G_OBJECT(binary)); + +} diff --git a/src/gui/menus/binary.h b/src/gui/menus/binary.h index c68b67c..a752626 100644 --- a/src/gui/menus/binary.h +++ b/src/gui/menus/binary.h @@ -40,6 +40,9 @@ GtkWidget *build_menu_binary(GObject *, GMenuBar *); /* Réagit à un changement d'affichage principal de contenu. */ void update_access_for_content_in_menu_binary(GLoadedContent *); +/* Lance une actualisation du fait d'un changement de support. */ +void update_access_for_view_in_menu_binary(GLoadedPanel *); + #endif /* _GUI_MENUS_BINARY_H */ diff --git a/src/gui/menus/menubar.c b/src/gui/menus/menubar.c index 886387c..aa89a13 100644 --- a/src/gui/menus/menubar.c +++ b/src/gui/menus/menubar.c @@ -311,6 +311,8 @@ static void change_menubar_current_view(GMenuBar *bar, GLoadedPanel *old, GLoade update_access_for_view_in_menu_view(G_EDITOR_ITEM(bar)->ref, new); + update_access_for_view_in_menu_binary(new); + } |