/* OpenIDA - Outil d'analyse de fichiers binaires * panel.c - prototypes pour la gestion des éléments réactifs spécifiques aux panneaux * * Copyright (C) 2009-2012 Cyrille Bagard * * This file is part of OpenIDA. * * OpenIDA 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. * * OpenIDA 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 "panel.h" #include #include #include "log.h" #include "panel-int.h" #include "symbols.h" #include "welcome.h" #include "../../gtkext/easygtk.h" #include "../../gtkext/gtkdockstation.h" /* Support de fond pour les composants. */ static GtkWidget *_support; static panel_node *_nodes = NULL; /* Procédure à appeler lors des changements de panneaux. */ static GCallback _handler; static gpointer _data; /* Liste des panneaux en place. */ static GPanelItem *_panels_list = NULL; /* Initialise la classe des éléments réactifs de l'éditeur. */ static void g_panel_item_class_init(GPanelItemClass *); /* Initialise une instance d'élément réactif pour l'éditeur. */ static void g_panel_item_init(GPanelItem *); /* ---------------------- MECANISMES DE PLACEMENT DES PANNEAUX ---------------------- */ /* Crée un nouveau noeud pour un panneau particulier. */ static panel_node *create_simple_panel_node_for_item(GPanelItem *, const char *, size_t); /* Prépare une nouvelle sous-division pour deux panneaux. */ static void switch_panel_node_into_paned(panel_node *, bool, bool); /* Met en place un nouveau noeud dans une division. */ static void attach_panel_node_to_paned(panel_node *, panel_node *, bool); /* Indique si un noeud correspond à la branche recherchée. */ static bool is_panel_node_matching(const panel_node *, char); /* Place au bon endroit un panneau donné. */ static void insert_item_as_panel_node(GPanelItem *, panel_node *, const char *, size_t); /* Met à jour l'affichage suite à un changement hiérarchique. */ static void rebuild_panels_interface(const panel_node *); /* Indique le type défini pour un élément destiné à un panneau. */ G_DEFINE_TYPE(GPanelItem, g_panel_item, G_TYPE_EDITOR_ITEM); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des éléments réactifs de l'éditeur. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_panel_item_class_init(GPanelItemClass *klass) { } /****************************************************************************** * * * Paramètres : item = instance à initialiser. * * * * Description : Initialise une instance d'élément réactif pour l'éditeur. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_panel_item_init(GPanelItem *item) { DL_LIST_ITEM_INIT(&item->link); } /****************************************************************************** * * * Paramètres : item = composant à présenter à l'affichage. * * ref = espace de référencement global. * * name = nom associé à l'élément. * * lname = description longue du panneau. * * widget = composant à présenter à l'affichage. * * path = chemin vers la place idéale pour le futur panneau. * * * * Description : Initialise dynamique les propriétés de l'instance. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_panel_item_init_ext(GPanelItem *item, GObject *ref, const char *name, const char *lname, GtkWidget *widget, const char *path) { GEditorItem *parent; /* Autre version de l'élément */ parent = G_EDITOR_ITEM(item); g_object_ref(ref); parent->ref = ref; parent->name = name; item->lname = lname; g_object_ref(widget); parent->widget = widget; g_object_set_data(G_OBJECT(widget), "pitem", item); item->path = path; panels_list_add_tail(item, &_panels_list); } /****************************************************************************** * * * Paramètres : ref = espace de référencement global. * * name = nom associé à l'élément. * * lname = description longue du panneau. * * widget = composant à présenter à l'affichage. * * path = chemin vers la place idéale pour le futur panneau. * * * * Description : Crée un élément de panneau réactif. * * * * Retour : Adresse de la structure mise en place. * * * * Remarques : - * * * ******************************************************************************/ GEditorItem *g_panel_item_new(GObject *ref, const char *name, const char *lname, GtkWidget *widget, const char *path) { GPanelItem *result; /* Structure à retourner */ result = g_object_new(G_TYPE_PANEL_ITEM, NULL); g_panel_item_init_ext(result, ref, name, lname, widget, path); return G_EDITOR_ITEM(result); } /****************************************************************************** * * * Paramètres : name = désignation courte servant de clef. * * * * Description : Recherche un panneau à partir de son nom court. * * * * Retour : Panneau trouvé ou NULL si aucun. * * * * Remarques : Le parcours peut se faire aussi depuis la classe parente, * * mais il est plus rapide par ici. * * * ******************************************************************************/ GPanelItem *g_panel_item_get(const char *name) { GPanelItem *iter; /* Boucle de parcours */ panels_list_for_each(iter, _panels_list) { if (strcmp(G_EDITOR_ITEM(iter)->name, name) == 0) return iter; } return NULL; } /****************************************************************************** * * * Paramètres : item = composant à présenter à l'affichage. * * * * Description : Place un panneau dans l'ensemble affiché. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_panel_item_dock(GPanelItem *item) { /* Tout est à faire... */ if (_nodes == NULL) { _nodes = create_simple_panel_node_for_item(item, item->path, 0); gtk_container_add(GTK_CONTAINER(_support), _nodes->station); } else insert_item_as_panel_node(item, _nodes, item->path, 0); } /****************************************************************************** * * * Paramètres : item = composant à retirer de l'affichage. * * * * Description : Supprime un panneau de l'ensemble affiché. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_panel_item_undock(GPanelItem *item) { GtkWidget *station; /* Base du remplacement */ station = gtk_widget_get_parent(G_EDITOR_ITEM(item)->widget); /* NoteBook */ station = gtk_widget_get_parent(station); /* DockStation */ gtk_dock_panel_remove_widget(GTK_DOCK_STATION(station), G_EDITOR_ITEM(item)->widget); } /* ---------------------------------------------------------------------------------- */ /* PLACEMENTS DES DIFFERENTS PANNEAUX */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : handler = procédure à réveiller en cas de changements. * * data = donnée à faire suivre. * * * * Description : Prépare le terrain pour l'affichage central. * * * * Retour : Composant de support sur lequel tout va se placer. * * * * Remarques : - * * * ******************************************************************************/ GtkWidget *init_panels2(GCallback handler, gpointer data) { GtkWidget *result; /* Support à retourner */ result = qck_create_padded_alignment(0, 0, 0, 0); gtk_widget_show(result); _support = result; _handler = handler; _data = data; return result; } /****************************************************************************** * * * Paramètres : ref = espace de référencement global. * * * * Description : Charge les principaux panneaux de l'éditeur. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void load_main_panels(GObject *ref) { GPanelItem *item; /* Panneau de base à charger */ item = create_welcome_panel(ref); g_panel_item_dock(item); item = create_log_panel(ref); g_panel_item_dock(item); item = create_symbols_panel(ref); g_panel_item_dock(item); } /* ---------------------------------------------------------------------------------- */ /* MECANISMES DE PLACEMENT DES PANNEAUX */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : item = composant à présenter à l'affichage. * * path = partie du chemin représentée ici. * * depth = profondeur du chemin utilisé. * * * * Description : Crée un nouveau noeud pour un panneau particulier. * * * * Retour : Structure d'accueil mise en place. * * * * Remarques : - * * * ******************************************************************************/ static panel_node *create_simple_panel_node_for_item(GPanelItem *item, const char *path, size_t depth) { panel_node *result; /* Structure à retourner */ GtkWidget *station; /* Premier support concentré */ GEditorItem *editem; /* Autre vision des choses */ result = (panel_node *)calloc(1, sizeof(panel_node)); result->path = path[depth]; result->depth = depth; result->simple = true; station = gtk_dock_station_new(); g_signal_connect(station, "switch-widget", _handler, _data); gtk_widget_show(station); result->station = station; editem = G_EDITOR_ITEM(item); gtk_dock_panel_add_widget(GTK_DOCK_STATION(station), editem->widget, editem->name); return result; } /****************************************************************************** * * * Paramètres : item = composant à présenter à l'affichage. * * horiz = indique le type d'orientation désiré. * * first = indication sur l'emplacement à utiliser. * * * * Description : Prépare une nouvelle sous-division pour deux panneaux. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void switch_panel_node_into_paned(panel_node *current, bool horiz, bool first) { panel_node *moved; /* Noeud descendu d'un étage */ GtkWidget *widget; /* Composant à traiter */ /* Descend l'élément actuel */ moved = (panel_node *)calloc(1, sizeof(panel_node)); memcpy(moved, current, sizeof(panel_node)); moved->parent = current; widget = GET_PANEL_NODE_WIDGET(current); g_object_ref(G_OBJECT(widget)); gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(widget)), widget); if (first) current->first = moved; else current->second = moved; /* Achève la transformation */ current->path = '\0'; current->simple = false; current->station = NULL; if (horiz) current->paned = gtk_hpaned_new(); else current->paned = gtk_vpaned_new(); gtk_widget_show(current->paned); /* Replace le composant d'origine */ if (first) gtk_paned_add1(GTK_PANED(current->paned), widget); else gtk_paned_add2(GTK_PANED(current->paned), widget); g_object_unref(G_OBJECT(widget)); } /****************************************************************************** * * * Paramètres : parent = noeud d'accueil pour le nouveau noeud. * * node = nouveau noeud à intégrer dans l'ensemble. * * first = indication sur l'emplacement à utiliser. * * * * Description : Met en place un nouveau noeud dans une division. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void attach_panel_node_to_paned(panel_node *parent, panel_node *node, bool first) { GtkWidget *widget; /* Composant à traiter */ /* Raccordement hiérarchique */ if (first) parent->first = node; else parent->second = node; node->parent = parent; /* Raccordement graphique */ widget = GET_PANEL_NODE_WIDGET(node); if (first) gtk_paned_add1(GTK_PANED(parent->paned), widget); else gtk_paned_add2(GTK_PANED(parent->paned), widget); } /****************************************************************************** * * * Paramètres : node = noeud d'où lancer les recherches. * * target = identifiant de la position visée. * * * * Description : Indique si un noeud correspond à la branche recherchée. * * * * Retour : Bilan de l'évaluation. * * * * Remarques : - * * * ******************************************************************************/ static bool is_panel_node_matching(const panel_node *node, char target) { if (node->path == target) return true; if (node->simple) return false; return is_panel_node_matching(node->first, target) || is_panel_node_matching(node->second, target); } /****************************************************************************** * * * Paramètres : item = composant à présenter à l'affichage. * * node = point d'insertion courant. * * path = partie du chemin représentée ici. * * depth = profondeur du chemin utilisé. * * * * Description : Place au bon endroit un panneau donné. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void insert_item_as_panel_node(GPanelItem *item, panel_node *node, const char *path, size_t depth) { char div; /* Division demandée */ bool horiz; /* Traduction en composant */ bool first; /* Point d'insertion */ panel_node *new; /* Nouveau noeud créé */ panel_node *support; /* Noeud d'accueil désigné */ if (node->simple) { /* Si on est sur la bonne voie... */ if (path[depth] == '\0' || path[depth] == node->path) { /* Le parcours s'arrête ici ! */ if (path[depth] == '\0' || path[depth + 1] == '\0') gtk_dock_panel_add_widget(GTK_DOCK_STATION(node->station), G_EDITOR_ITEM(item)->widget, G_EDITOR_ITEM(item)->name); /* On ne peut aller plus loin, on doit diviser... */ else { div = toupper(path[depth + 1]); first = (div == 'E' || div == 'S'); horiz = (div == 'W' || div == 'E'); switch_panel_node_into_paned(node, horiz, first); new = create_simple_panel_node_for_item(item, path, depth + 1); attach_panel_node_to_paned(node, new, !first); rebuild_panels_interface(node); } } /* On ne peut aller plus loin, on doit diviser... */ else { div = toupper(path[depth]); first = (div == 'E' || div == 'S'); horiz = (div == 'W' || div == 'E'); switch_panel_node_into_paned(node, horiz, first); new = create_simple_panel_node_for_item(item, path, depth); attach_panel_node_to_paned(node, new, !first); rebuild_panels_interface(node); } } /* On regarde des autres côtés... */ else if (is_panel_node_matching(node->first, path[depth])) insert_item_as_panel_node(item, node->first, path, depth + (node->first->simple ? 1 : 0)); else if (is_panel_node_matching(node->second, path[depth])) insert_item_as_panel_node(item, node->second, path, depth + (node->second->simple ? 1 : 0)); /* On doit diviser qqch... */ else { /* Si l'élément doit passer en force */ if (isupper(path[depth])) { div = path[depth]; first = (div == 'E' || div == 'S'); horiz = (div == 'W' || div == 'E'); switch_panel_node_into_paned(node, horiz, first); new = create_simple_panel_node_for_item(item, path, depth); attach_panel_node_to_paned(node, new, !first); rebuild_panels_interface(node); } else { if (is_panel_node_matching(node->second, 'M')) support = node->second; else support = node->first; div = toupper(path[depth]); first = (div == 'E' || div == 'S'); horiz = (div == 'W' || div == 'E'); switch_panel_node_into_paned(support, horiz, first); new = create_simple_panel_node_for_item(item, path, depth); attach_panel_node_to_paned(support, new, !first); rebuild_panels_interface(support); } } } /****************************************************************************** * * * Paramètres : current = point de départ de la réorganisation graphique. * * * * Description : Met à jour l'affichage suite à un changement hiérarchique. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void rebuild_panels_interface(const panel_node *current) { GtkWidget *widget; /* Composant à traiter */ panel_node *parent; /* Raccourci confortable */ widget = GET_PANEL_NODE_WIDGET(current); /* On se trouve à la racine... */ if (current->parent == NULL) gtk_container_add(GTK_CONTAINER(_support), widget); /* Sinon, une sous-division ne peut venir que d'une division... */ else { /* BUG_ON(parent->simple) */ parent = current->parent; if (current == parent->first) gtk_paned_add1(GTK_PANED(parent->paned), widget); else gtk_paned_add2(GTK_PANED(parent->paned), widget); } }