/* Chrysalide - Outil d'analyse de fichiers binaires * history.c - panneau de la liste des évolutions d'utilisateur(s) * * Copyright (C) 2015-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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "history.h" #include #include #include #include "../panel-int.h" #include "../../analysis/binary.h" #include "../../glibext/chrysamarshal.h" #include "../../glibext/signal.h" #include "../../gtkext/easygtk.h" #include "../../gtkext/named.h" /* Panneau de la liste des évolutions utilisateur(s) (instance) */ struct _GHistoryPanel { GPanelItem parent; /* A laisser en premier */ GLoadedBinary *binary; /* Binaire à prendre en compte */ }; /* Panneau de la liste des évolutions utilisateur(s) (classe) */ struct _GHistoryPanelClass { GPanelItemClass parent; /* A laisser en premier */ }; /* Colonnes de la liste des évolutions */ typedef enum _HistoryColumn { HTC_ITEM, /* Elément d'évolution */ HTC_PICTURE, /* Image de représentation */ HTC_FOREGROUND, /* Couleur d'impression */ HTC_LABEL, /* Désignation humaine */ HTC_COUNT /* Nombre de colonnes */ } HistoryColumn; /* Initialise la classe des panneaux de la liste des évolutions utilisateur(s). */ static void g_history_panel_class_init(GHistoryPanelClass *); /* Initialise une instance de panneau d'aperçu de graphiques. */ static void g_history_panel_init(GHistoryPanel *); /* Supprime toutes les références externes. */ static void g_history_panel_dispose(GHistoryPanel *); /* Procède à la libération totale de la mémoire. */ static void g_history_panel_finalize(GHistoryPanel *); /* Fournit le nom interne attribué à l'élément réactif. */ static char *g_history_panel_class_get_key(const GHistoryPanelClass *); /* Indique le chemin initial de la localisation d'un panneau. */ static char *g_history_panel_class_get_path(const GHistoryPanelClass *); /* Réagit à un changement d'affichage principal de contenu. */ static void change_history_panel_current_content(GHistoryPanel *, GLoadedContent *, GLoadedContent *); /* Réagit à une modification au sein d'une collection donnée. */ static void on_history_changed(GDbCollection *, DBAction, GDbItem *, GHistoryPanel *); /* Compare deux lignes entre elles pour le tri des évolutions. */ static gint sort_history_lines(GtkTreeModel *, GtkTreeIter *, GtkTreeIter *, gpointer); /* Réagit au changement de sélection des éléments d'historique. */ static void on_history_selection_change(GtkTreeSelection *, GHistoryPanel *); /* Annule l'élément d'évolution courant. */ static void do_history_undo(GtkButton *, GHistoryPanel *); /* Restaure l'élément d'évolution suivant. */ static void do_history_redo(GtkButton *, GHistoryPanel *); /* Effectue un nettoyage de l'historique. */ static void do_history_clean(GtkButton *, GHistoryPanel *); /* Indique le type défini pour un panneau d'aperçu de graphiques. */ G_DEFINE_TYPE(GHistoryPanel, g_history_panel, G_TYPE_PANEL_ITEM); /****************************************************************************** * * * Paramètres : class = classe à initialiser. * * * * Description : Initialise la classe des panneaux d'aperçu de graphiques. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_history_panel_class_init(GHistoryPanelClass *class) { GObjectClass *object; /* Autre version de la classe */ GEditorItemClass *item; /* Encore une autre vision... */ GPanelItemClass *panel; /* Version parente de la classe*/ object = G_OBJECT_CLASS(class); object->dispose = (GObjectFinalizeFunc/* ! */)g_history_panel_dispose; object->finalize = (GObjectFinalizeFunc)g_history_panel_finalize; item = G_EDITOR_ITEM_CLASS(class); item->get_key = (get_item_key_fc)g_history_panel_class_get_key; item->change_content = (change_item_content_fc)change_history_panel_current_content; panel = G_PANEL_ITEM_CLASS(class); panel->get_path = (get_panel_path_fc)g_history_panel_class_get_path; } /****************************************************************************** * * * Paramètres : panel = instance à initialiser. * * * * Description : Initialise une instance de panneau d'aperçu de graphiques. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_history_panel_init(GHistoryPanel *panel) { GPanelItem *pitem; /* Version parente du panneau */ GtkBuilder *builder; /* Constructeur utilisé */ GtkListStore *store; /* Modèle de gestion */ /* Eléments de base */ pitem = G_PANEL_ITEM(panel); pitem->widget = G_NAMED_WIDGET(gtk_built_named_widget_new_for_panel(_("History"), _("Change history"), PANEL_HISTORY_ID)); /* Représentation graphique */ builder = gtk_built_named_widget_get_builder(GTK_BUILT_NAMED_WIDGET(pitem->widget)); store = GTK_LIST_STORE(gtk_builder_get_object(builder, "store")); gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(store), sort_history_lines, NULL, NULL); /* Connexion des signaux */ gtk_builder_add_callback_symbols(builder, BUILDER_CALLBACK(on_history_selection_change), BUILDER_CALLBACK(do_history_undo), BUILDER_CALLBACK(do_history_redo), BUILDER_CALLBACK(do_history_clean), NULL); gtk_builder_connect_signals(builder, panel); g_object_unref(G_OBJECT(builder)); } /****************************************************************************** * * * Paramètres : panel = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_history_panel_dispose(GHistoryPanel *panel) { g_clear_object(&panel->binary); G_OBJECT_CLASS(g_history_panel_parent_class)->dispose(G_OBJECT(panel)); } /****************************************************************************** * * * Paramètres : panel = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_history_panel_finalize(GHistoryPanel *panel) { G_OBJECT_CLASS(g_history_panel_parent_class)->finalize(G_OBJECT(panel)); } /****************************************************************************** * * * Paramètres : class = classe à consulter. * * * * Description : Fournit le nom interne attribué à l'élément réactif. * * * * Retour : Désignation (courte) de l'élément de l'éditeur. * * * * Remarques : - * * * ******************************************************************************/ static char *g_history_panel_class_get_key(const GHistoryPanelClass *class) { char *result; /* Description à renvoyer */ result = strdup(PANEL_HISTORY_ID); return result; } /****************************************************************************** * * * Paramètres : class = classe à consulter. * * * * Description : Indique le chemin initial de la localisation d'un panneau. * * * * Retour : Chemin fixé associé à la position initiale. * * * * Remarques : - * * * ******************************************************************************/ static char *g_history_panel_class_get_path(const GHistoryPanelClass *class) { char *result; /* Emplacement à retourner */ result = strdup("MEN"); return result; } /****************************************************************************** * * * Paramètres : - * * * * Description : Crée un panneau d'affichage des symboles. * * * * Retour : Adresse de la structure mise en place. * * * * Remarques : - * * * ******************************************************************************/ GPanelItem *g_history_panel_new(void) { GPanelItem *result; /* Structure à retourner */ result = g_object_new(G_TYPE_HISTORY_PANEL, NULL); return result; } /****************************************************************************** * * * Paramètres : panel = panneau à mettre à jour. * * old = ancien contenu chargé analysé. * * new = nouveau contenu chargé à analyser. * * * * Description : Réagit à un changement d'affichage principal de contenu. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void change_history_panel_current_content(GHistoryPanel *panel, GLoadedContent *old, GLoadedContent *new) { GLoadedBinary *binary; /* Autre version de l'instance */ GDbCollection **collections; /* Ensemble de collections */ size_t count; /* Taille de cet ensemble */ size_t k; /* Boucle de parcours #1 */ GtkBuilder *builder; /* Constructeur utilisé */ GtkListStore *store; /* Modèle de gestion */ GList *items; /* Liste des éléments groupés */ GList *i; /* Boucle de parcours #2 */ GDbItem *item; /* Elément à intégrer */ char *label; /* Etiquette de représentation */ GtkTreeIter iter; /* Point d'insertion */ if (G_IS_LOADED_BINARY(new)) binary = G_LOADED_BINARY(new); else binary = NULL; /* Basculement du binaire utilisé */ if (panel->binary != NULL) { collections = g_loaded_binary_get_collections(panel->binary, &count); for (k = 0; k < count; k++) { g_signal_handlers_disconnect_by_func(collections[k], G_CALLBACK(on_history_changed), panel); g_object_unref(G_OBJECT(collections[k])); } if (collections != NULL) free(collections); g_object_unref(G_OBJECT(panel->binary)); } panel->binary = binary; if (panel->binary != NULL) g_object_ref(G_OBJECT(binary)); builder = gtk_built_named_widget_get_builder(GTK_BUILT_NAMED_WIDGET(G_PANEL_ITEM(panel)->widget)); store = GTK_LIST_STORE(gtk_builder_get_object(builder, "store")); gtk_list_store_clear(store); g_object_unref(G_OBJECT(builder)); /* Si le panneau actif ne représente pas un binaire... */ if (binary == NULL) return; /* Actualisation de l'affichage */ collections = g_loaded_binary_get_collections(binary, &count); for (k = 0; k < count; k++) { g_db_collection_rlock(collections[k]); /* items = g_db_collection_get_items(collections[k]); for (i = g_list_first(items); i != NULL; i = g_list_next(i)) { item = G_DB_ITEM(i->data); label = g_db_item_get_label(item); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, HTC_ITEM, item, //HTC_PICTURE, G_BOOKMARKS_PANEL_GET_CLASS(panel)->bookmark_img, HTC_FOREGROUND, g_db_item_is_enabled(item) ? NULL : "grey", HTC_LABEL, label, -1); free(label); } */ //g_signal_connect_to_main(collections[k], "content-changed", G_CALLBACK(on_history_changed), panel, // g_cclosure_user_marshal_VOID__ENUM_OBJECT); g_db_collection_runlock(collections[k]); g_object_unref(G_OBJECT(collections[k])); } if (collections != NULL) free(collections); /* Force une sélection initiale */ on_history_changed(NULL, DBA_COUNT, NULL, panel); } /****************************************************************************** * * * Paramètres : collec = collection dont le contenu a évolué. * * action = type d'évolution rencontrée. * * item = élément ajouté, modifié ou supprimé. * * panel = panneau d'historique concerné par la procédure. * * * * Description : Réagit à une modification au sein d'une collection donnée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_history_changed(GDbCollection *collec, DBAction action, GDbItem *item, GHistoryPanel *panel) { GtkBuilder *builder; /* Constructeur utilisé */ GtkTreeView *treeview; /* Arborescence manipulée */ GtkListStore *store; /* Modèle de gestion courant */ GtkTreeModel *model; /* Modèle de gestion générique */ GtkTreeSelection *selection; /* Nouvelle sélection à établir*/ char *label; /* Etiquette de représentation */ GtkTreeIter iter; /* Boucle de parcours */ builder = gtk_built_named_widget_get_builder(GTK_BUILT_NAMED_WIDGET(G_PANEL_ITEM(panel)->widget)); treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview")); store = GTK_LIST_STORE(gtk_builder_get_object(builder, "store")); model = GTK_TREE_MODEL(store); selection = gtk_tree_view_get_selection(treeview); /* Mise à jour de la liste affichée */ bool find_changed_item(GtkTreeModel *_model, GDbItem *target, GtkTreeIter *_found) { bool status; GtkTreeIter candidate; GDbItem *displayed; status = false; if (gtk_tree_model_get_iter_first(_model, &candidate)) do { gtk_tree_model_get(_model, &candidate, HTC_ITEM, &displayed, -1); if (target == displayed) { *_found = candidate; status = true; } g_object_unref(G_OBJECT(displayed)); } while (!status && gtk_tree_model_iter_next(_model, &candidate)); return status; } switch (action) { case DBA_ADD_ITEM: label = g_db_item_get_label(item); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, HTC_ITEM, item, //HTC_PICTURE, G_BOOKMARKS_PANEL_GET_CLASS(panel)->bookmark_img, HTC_FOREGROUND, g_db_item_has_flag(item, DIF_DISABLED) ? "grey" : NULL, HTC_LABEL, label, -1); free(label); break; case DBA_REM_ITEM: if (find_changed_item(model, item, &iter)) gtk_list_store_remove(store, &iter); break; case DBA_CHANGE_STATE: if (find_changed_item(model, item, &iter)) gtk_list_store_set(store, &iter, HTC_FOREGROUND, g_db_item_has_flag(item, DIF_DISABLED) ? "grey" : NULL, -1); break; case DBA_COUNT: /* Actualisation artificielle de la sélection */ break; } /* Redéfinition de la sélection */ if (gtk_tree_model_get_iter_first(model, &iter)) { gboolean find_last_active(GtkTreeModel *_model, GtkTreePath *_path, GtkTreeIter *_iter, GtkTreeIter *last) { GDbItem *item; gboolean active; gtk_tree_model_get(_model, _iter, HTC_ITEM, &item, -1); active = !g_db_item_has_flag(item, DIF_DISABLED); g_object_unref(G_OBJECT(item)); if (active) *last = *_iter; return !active; } gtk_tree_model_foreach(model, (GtkTreeModelForeachFunc)find_last_active, &iter); gtk_tree_selection_select_iter(selection, &iter); } /* Actualisation des accès */ on_history_selection_change(selection, panel); g_object_unref(G_OBJECT(builder)); } /****************************************************************************** * * * Paramètres : model = gestionnaire de données pour la liste traitée. * * a = premier point de comparaison. * * b = second point de comparaison. * * dummy = adresse non utilisée ici. * * * * Description : Compare deux lignes entre elles pour le tri des évolutions. * * * * Retour : -1, 0 ou 1 selon le résultat de la comparaison. * * * * Remarques : - * * * ******************************************************************************/ static gint sort_history_lines(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer dummy) { gint result; /* Bilan à retourner */ GDbItem *item_a; /* Elément de collection A */ GDbItem *item_b; /* Elément de collection B */ gtk_tree_model_get(model, a, HTC_ITEM, &item_a, -1); gtk_tree_model_get(model, b, HTC_ITEM, &item_b, -1); result = g_db_item_cmp(item_a, item_b); g_object_unref(G_OBJECT(item_a)); g_object_unref(G_OBJECT(item_b)); return result; } /****************************************************************************** * * * Paramètres : selection = sélection modifiée. * * panel = structure contenant les informations maîtresses. * * * * Description : Réagit au changement de sélection des éléments d'historique. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_history_selection_change(GtkTreeSelection *selection, GHistoryPanel *panel) { GtkBuilder *builder; /* Constructeur utilisé */ GtkTreeIter iter; /* Point de sélection */ GtkTreeModel *model; /* Modèle de gestion */ GDbItem *item; /* Elément de collection */ GtkWidget *button; /* Bouton de barre de contrôle */ builder = gtk_built_named_widget_get_builder(GTK_BUILT_NAMED_WIDGET(G_PANEL_ITEM(panel)->widget)); if (gtk_tree_selection_get_selected(selection, &model, &iter)) { gtk_tree_model_get(model, &iter, HTC_ITEM, &item, -1); button = GTK_WIDGET(gtk_builder_get_object(builder, "undo")); gtk_widget_set_sensitive(button, !g_db_item_has_flag(item, DIF_DISABLED)); button = GTK_WIDGET(gtk_builder_get_object(builder, "redo")); gtk_widget_set_sensitive(button, g_db_item_has_flag(item, DIF_DISABLED)); g_object_unref(G_OBJECT(item)); } else { button = GTK_WIDGET(gtk_builder_get_object(builder, "undo")); gtk_widget_set_sensitive(button, FALSE); button = GTK_WIDGET(gtk_builder_get_object(builder, "redo")); gtk_widget_set_sensitive(button, FALSE); } g_object_unref(G_OBJECT(builder)); } /****************************************************************************** * * * Paramètres : button = bouton d'édition de l'historique d'évolution. * * panel = panneau d'affichage de l'historique. * * * * Description : Annule l'élément d'évolution courant. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void do_history_undo(GtkButton *button, GHistoryPanel *panel) { GtkBuilder *builder; /* Constructeur utilisé */ GtkTreeView *treeview; /* Arborescence manipulée */ GtkTreeSelection *selection; /* Sélection courante */ GtkTreeModel *model; /* Modèle de gestion de données*/ GtkTreeIter iter; /* Pointeur vers la ligne visée*/ GDbItem *item; /* Elément de collection */ GAnalystClient *client; /* Connexion vers la base */ builder = gtk_built_named_widget_get_builder(GTK_BUILT_NAMED_WIDGET(G_PANEL_ITEM(panel)->widget)); treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview")); selection = gtk_tree_view_get_selection(treeview); if (gtk_tree_selection_get_selected(selection, &model, &iter)) { if (gtk_tree_model_iter_previous(model, &iter)) { gtk_tree_model_get(model, &iter, HTC_ITEM, &item, -1); client = g_loaded_binary_get_client(panel->binary); g_analyst_client_set_last_active(client, g_db_item_get_timestamp(item)); g_object_unref(G_OBJECT(client)); g_object_unref(G_OBJECT(item)); } } g_object_unref(G_OBJECT(builder)); } /****************************************************************************** * * * Paramètres : button = bouton d'édition de l'historique d'évolution. * * panel = panneau d'affichage de l'historique. * * * * Description : Restaure l'élément d'évolution suivant. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void do_history_redo(GtkButton *button, GHistoryPanel *panel) { GtkBuilder *builder; /* Constructeur utilisé */ GtkTreeView *treeview; /* Arborescence manipulée */ GtkTreeSelection *selection; /* Sélection courante */ GtkTreeModel *model; /* Modèle de gestion de données*/ GtkTreeIter iter; /* Pointeur vers la ligne visée*/ GDbItem *item; /* Elément de collection */ GAnalystClient *client; /* Connexion vers la base */ builder = gtk_built_named_widget_get_builder(GTK_BUILT_NAMED_WIDGET(G_PANEL_ITEM(panel)->widget)); treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview")); selection = gtk_tree_view_get_selection(treeview); if (gtk_tree_selection_get_selected(selection, &model, &iter)) { gtk_tree_model_get(model, &iter, HTC_ITEM, &item, -1); client = g_loaded_binary_get_client(panel->binary); g_analyst_client_set_last_active(client, g_db_item_get_timestamp(item)); g_object_unref(G_OBJECT(client)); g_object_unref(G_OBJECT(item)); } g_object_unref(G_OBJECT(builder)); } /****************************************************************************** * * * Paramètres : button = bouton d'édition de l'historique d'évolution. * * panel = panneau d'affichage de l'historique. * * * * Description : Effectue un nettoyage de l'historique. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void do_history_clean(GtkButton *button, GHistoryPanel *panel) { /* TODO */ }