/* 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 .
*/
#include "export_graph.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include "../../common/extstr.h"
#include "../../core/logs.h"
#include "../../glibext/gbinarycursor.h"
#include "../../glibext/gloadedpanel.h"
#include "../../gtkext/easygtk.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_retrieve(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,
BUILDER_CALLBACK(graph_export_assistant_cancel),
BUILDER_CALLBACK(graph_export_assistant_close),
BUILDER_CALLBACK(on_output_format_toggled),
BUILDER_CALLBACK(on_output_filename_changed),
BUILDER_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);
}