/* * ni-window-route.c * This file is part of Network Inador * * Copyright (C) 2022 - Félix Arreola Rodríguez * * Network Inador 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 2 of the License, or * (at your option) any later version. * * Network Inador 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 Network Inador; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include "ni-window-route.h" #include "ni-client.h" #include "ni-route.h" #include "ni-interface.h" struct _NIWindowRoutePrivate { NIClient *ni_client; int family; uint32_t table_filter; GtkWidget *vbox, *tree_routes; GtkListStore *routes_store; GtkTreeModel *routes_filtered; GtkListStore *tables_store; GtkWidget *del_route_button; GtkWidget *combo_tables; }; enum { PROP_NI_CLIENT = 1, PROP_NI_FAMILY, PROP_NI_TABLE_FILTER, N_PROPERTIES }; enum { ROUTE_STORE_TABLE, ROUTE_STORE_COL_DST_GW, ROUTE_STORE_COL_METRIC, ROUTE_STORE_COL_PREFSRC, // TODO: Agregar tipo de protocolo, tos, scope ROUTE_STORE_COL_OBJECT, NUM_ROUTE_STORE_COLS }; enum { TABLE_STORE_TABLE, TABLE_STORE_NAME, TABLE_STORE_IS_NEW, NUM_TABLE_STORE_COLS }; struct _NIWindowRouteSearchTable { gboolean found; uint32_t tabla; GtkTreeIter iter; }; struct _NIWindowRouteSearchRoute { gboolean found; void *obj; GtkTreeIter iter; }; G_DEFINE_TYPE_WITH_PRIVATE (NIWindowRoute, ni_window_route, GTK_TYPE_WINDOW) static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; static gboolean ni_window_route_foreach_tables_search_id (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); static gboolean ni_window_route_foreach_routes_search_route (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); static void ni_window_interface_new_ip_cb (NIClient *ni_client, NIRoute *ni_route, gpointer data); static void ni_window_interface_delete_ip_cb (NIClient *ni_client, NIRoute *ni_route, gpointer data); static void ni_window_route_updated_route_cb (NIRoute *ni_route, gpointer data) { NIWindowRoute *window_route = NI_WINDOW_ROUTE (data); struct _NIWindowRouteSearchRoute for_search; char buffer_ip[256]; guint priority; const struct_addr *dest; int family; family = ni_route_get_family (ni_route); /* Preparar el pref-src, si lo tiene */ buffer_ip[0] = 0; if (ni_route_has_prefsrc (ni_route)) { dest = ni_route_get_prefsrc (ni_route); inet_ntop (family, dest, buffer_ip, sizeof (buffer_ip)); } priority = ni_route_get_priority (ni_route); for_search.found = FALSE; for_search.obj = ni_route; gtk_tree_model_foreach (GTK_TREE_MODEL (window_route->priv->routes_store), ni_window_route_foreach_routes_search_route, &for_search); if (for_search.found == TRUE) { /* Tengo fila, actualizar los valores */ gtk_list_store_set (window_route->priv->routes_store, &for_search.iter, ROUTE_STORE_COL_METRIC, priority, ROUTE_STORE_COL_PREFSRC, buffer_ip, -1); } } static void ni_window_route_route_added_cb (NIWindowRoute *window_route, NIRoute *ni_route) { int family, prefix; char buffer_ip[256], buffer[512]; const struct_addr *dest; uint32_t table; guint priority; struct _NIWindowRouteSearchTable for_search; family = ni_route_get_family (ni_route); dest = ni_route_get_dest (ni_route); prefix = ni_route_get_prefix (ni_route); table = ni_route_get_table (ni_route); priority = ni_route_get_priority (ni_route); if (family != window_route->priv->family) { return; } /* Preparar el dest-gw */ inet_ntop (family, dest, buffer_ip, sizeof (buffer_ip)); if (family == AF_INET && prefix == 32) { snprintf (buffer, sizeof (buffer), "%s", buffer_ip); } else if (family == AF_INET6 && prefix == 128) { snprintf (buffer, sizeof (buffer), "%s", buffer_ip); } else { snprintf (buffer, sizeof (buffer), "%s/%i", buffer_ip, prefix); } /* Preparar el pref-src, si lo tiene */ buffer_ip[0] = 0; if (ni_route_has_prefsrc (ni_route)) { dest = ni_route_get_prefsrc (ni_route); inet_ntop (family, dest, buffer_ip, sizeof (buffer_ip)); } /* Revisar que la tabla exista, si la tabla no existe, la creamos en el menú de la lista de tablas */ for_search.found = FALSE; for_search.tabla = table; gtk_tree_model_foreach (GTK_TREE_MODEL (window_route->priv->tables_store), ni_window_route_foreach_tables_search_id, &for_search); if (for_search.found == FALSE) { /* Insertar el valor en el list store de las tablas */ gtk_list_store_insert_with_values (window_route->priv->tables_store, NULL, -1, TABLE_STORE_TABLE, table, TABLE_STORE_NAME, NULL, TABLE_STORE_IS_NEW, FALSE, -1); } gtk_list_store_insert_with_values (window_route->priv->routes_store, NULL, -1, ROUTE_STORE_TABLE, table, ROUTE_STORE_COL_DST_GW, buffer, ROUTE_STORE_COL_METRIC, priority, ROUTE_STORE_COL_PREFSRC, buffer_ip, ROUTE_STORE_COL_OBJECT, ni_route, -1); g_signal_connect (ni_route, "updated", G_CALLBACK (ni_window_route_updated_route_cb), window_route); } /* Función para renderizar la tabla y el nombre con guion separado */ void ni_window_route_cell_renderer_table_id (GtkCellLayout *cell_layout, GtkCellRenderer *cell, GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data) { uint32_t tabla; const gchar *name; gboolean new; char buffer[1024]; gtk_tree_model_get (tree_model, iter, TABLE_STORE_TABLE, &tabla, TABLE_STORE_NAME, &name, TABLE_STORE_IS_NEW, &new, -1); if (new == FALSE) { if (name == NULL) { g_snprintf (buffer, sizeof (buffer), "%i - (Sin nombre)", tabla); } else { g_snprintf (buffer, sizeof (buffer), "%i - %s", tabla, name); } g_object_set (cell, "text", buffer, NULL); } else { g_object_set (cell, "text", "Nueva tabla...", NULL); } } /* Función para renderizar la gateway */ void ni_window_route_cell_renderer_gw_oiface (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data) { NIRoute *ni_route; NIClient *ni_client; NIInterface *ni_interface; int family; char buffer_ip[256], buffer[8192]; int buffer_len; int g, num_nexthops; const NIRouteNH *nexthops; gtk_tree_model_get (tree_model, iter, ROUTE_STORE_COL_OBJECT, &ni_route, -1); family = ni_route_get_family (ni_route); ni_client = ni_route_get_client (ni_route); num_nexthops = ni_route_get_num_nexthops (ni_route); nexthops = ni_route_get_nexthops (ni_route); buffer_len = 0; for (g = 0; g < num_nexthops; g++) { if (g != 0) { /* Anexar la coma, */ buffer_len += g_snprintf (&buffer[buffer_len], sizeof (buffer) - buffer_len, ", "); } if (nexthops[g].has_gw) { inet_ntop (family, &nexthops[g].gw, buffer_ip, sizeof (buffer_ip)); buffer_len += g_snprintf (&buffer[buffer_len], sizeof (buffer) - buffer_len, "%s ", buffer_ip); } ni_interface = ni_client_get_interface_by_index (ni_client, nexthops[g].out_index); if (ni_interface == NULL) { buffer_len += g_snprintf (&buffer[buffer_len], sizeof (buffer) - buffer_len, "[¿?]"); } else { buffer_len += g_snprintf (&buffer[buffer_len], sizeof (buffer) - buffer_len, "[%s]", ni_interface_get_name (ni_interface)); } } g_object_set (cell, "text", buffer, NULL); } static gboolean ni_window_route_delete_event (GtkWidget *widget, GdkEventAny *event) { return gtk_widget_hide_on_delete (widget); } static void ni_window_route_constructed (GObject *obj) { GObjectClass *parent_class = G_OBJECT_CLASS (g_type_class_peek (GTK_TYPE_WINDOW)); NIWindowRoute *window_route = NI_WINDOW_ROUTE (obj); const GList *route_list = NULL, *g; NIRoute *ni_route; parent_class->constructed (obj); if (window_route->priv->ni_client == NULL) return; /* Cambiar el título name = g_strdup_printf ("Interfaz %s", ni_interface_get_name (window_iface->priv->ni_interface)); gtk_window_set_title (GTK_WINDOW (obj), name); g_free (name);*/ /* Conectar la señal de ruta agregada y ruta eliminada */ g_signal_connect (window_route->priv->ni_client, "new-route", G_CALLBACK (ni_window_interface_new_ip_cb), window_route); g_signal_connect (window_route->priv->ni_client, "delete-route", G_CALLBACK (ni_window_interface_delete_ip_cb), window_route); /* Recorrer las rutas, y procesar la información */ if (window_route->priv->family == AF_INET) { route_list = ni_client_get_routes_v4 (window_route->priv->ni_client); } else if (window_route->priv->family == AF_INET6) { route_list = ni_client_get_routes_v6 (window_route->priv->ni_client); } for (g = route_list; g != NULL; g = g->next) { ni_route = (NIRoute *) g->data; ni_window_route_route_added_cb (window_route, ni_route); } } static void ni_window_route_dispose (GObject *obj) { NIWindowRoute *window_route; GObjectClass *parent_class = G_OBJECT_CLASS (g_type_class_peek (GTK_TYPE_WINDOW)); window_route = NI_WINDOW_ROUTE (obj); g_object_unref (window_route->priv->ni_client); window_route->priv->ni_client = NULL; parent_class->dispose (obj); } static void ni_window_route_change_filter_table_id (NIWindowRoute *window_route, uint32_t new_table) { /* Primero, guardar el valor nuevo */ window_route->priv->table_filter = new_table; gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (window_route->priv->routes_filtered)); /* Notificar el cambio de propiedad */ g_object_notify_by_pspec (G_OBJECT (window_route), obj_properties[PROP_NI_TABLE_FILTER]); } static gboolean ni_window_route_foreach_tables_search_id (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { struct _NIWindowRouteSearchTable *for_search = (struct _NIWindowRouteSearchTable *) data; uint32_t tabla; gboolean is_new; gtk_tree_model_get (tree_model, iter, TABLE_STORE_TABLE, &tabla, TABLE_STORE_IS_NEW, &is_new, -1); if (is_new) return FALSE; if (tabla == for_search->tabla) { for_search->found = TRUE; for_search->iter = *iter; return TRUE; } return FALSE; } static gboolean ni_window_route_foreach_routes_search_route (GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { struct _NIWindowRouteSearchRoute *for_search = (struct _NIWindowRouteSearchRoute *) data; void *route; gtk_tree_model_get (tree_model, iter, ROUTE_STORE_COL_OBJECT, &route, -1); if (route == for_search->obj) { for_search->found = TRUE; for_search->iter = *iter; return TRUE; } return FALSE; } static gint ni_window_route_sort_tables_func (GtkTreeModel *tree_model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data) { uint32_t tabla_a, tabla_b; gboolean is_new_a, is_new_b; gtk_tree_model_get (tree_model, a, TABLE_STORE_TABLE, &tabla_a, TABLE_STORE_IS_NEW, &is_new_a, -1); gtk_tree_model_get (tree_model, b, TABLE_STORE_TABLE, &tabla_b, TABLE_STORE_IS_NEW, &is_new_b, -1); if (is_new_a) { return 1; } if (is_new_b) { return -1; } return tabla_a - tabla_b; } static void ni_window_route_changed_combo_table_cb (GtkWidget *combo, gpointer data) { NIWindowRoute *window_route = NI_WINDOW_ROUTE (data); GtkTreeIter iter; uint32_t tabla; gboolean is_new; struct _NIWindowRouteSearchTable for_search; gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter); gtk_tree_model_get (GTK_TREE_MODEL (window_route->priv->tables_store), &iter, TABLE_STORE_TABLE, &tabla, TABLE_STORE_IS_NEW, &is_new, -1); if (is_new) { /* TODO: Presentar el cuadro de dialogo de creación de nueva tabla */ /* Localizar el valor previo de la tabla, para regresar el combo */ for_search.found = FALSE; for_search.tabla = window_route->priv->table_filter; gtk_tree_model_foreach (GTK_TREE_MODEL (window_route->priv->tables_store), ni_window_route_foreach_tables_search_id, &for_search); if (for_search.found) { gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &for_search.iter); } else { gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), NULL); } } else { /* Aplicar el nuevo filtro */ ni_window_route_change_filter_table_id (window_route, tabla); } } gboolean ni_window_route_tree_filter_routes_func (GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data) { NIWindowRoute *window_route = NI_WINDOW_ROUTE (data); uint32_t tabla; if (window_route->priv->table_filter == 0) return TRUE; gtk_tree_model_get (tree_model, iter, ROUTE_STORE_TABLE, &tabla, -1); if (tabla == window_route->priv->table_filter) { return TRUE; } return FALSE; } static void ni_window_interface_new_ip_cb (NIClient *ni_client, NIRoute *ni_route, gpointer data) { NIWindowRoute *window_route = NI_WINDOW_ROUTE (data); ni_window_route_route_added_cb (window_route, ni_route); } static void ni_window_interface_delete_ip_cb (NIClient *ni_client, NIRoute *ni_route, gpointer data) { NIWindowRoute *window_route = NI_WINDOW_ROUTE (data); struct _NIWindowRouteSearchRoute for_search; for_search.found = FALSE; for_search.obj = ni_route; gtk_tree_model_foreach (GTK_TREE_MODEL (window_route->priv->routes_store), ni_window_route_foreach_routes_search_route, &for_search); if (for_search.found == TRUE) { /* Tengo fila, actualizar los valores */ gtk_list_store_remove (window_route->priv->routes_store, &for_search.iter); } } static void ni_window_route_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NIWindowRoute *window_route = NI_WINDOW_ROUTE (object); g_return_if_fail (NI_IS_WINDOW_ROUTE (object)); switch (prop_id) { case PROP_NI_CLIENT: window_route->priv->ni_client = NI_CLIENT (g_value_get_object (value)); g_object_ref (window_route->priv->ni_client); break; case PROP_NI_FAMILY: window_route->priv->family = g_value_get_uint (value); break; case PROP_NI_TABLE_FILTER: ni_window_route_change_filter_table_id (window_route, g_value_get_uint (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void ni_window_route_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NIWindowRoute *window_route = NI_WINDOW_ROUTE (object); g_return_if_fail (NI_IS_WINDOW_ROUTE (object)); switch (prop_id) { case PROP_NI_CLIENT: g_value_set_object (value, window_route->priv->ni_client); break; case PROP_NI_FAMILY: g_value_set_uint (value, window_route->priv->family); break; case PROP_NI_TABLE_FILTER: g_value_set_uint (value, window_route->priv->table_filter); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void ni_window_route_class_init (NIWindowRouteClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWindowClass *window_class = GTK_WINDOW_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->set_property = ni_window_route_set_property; object_class->get_property = ni_window_route_get_property; object_class->constructed = ni_window_route_constructed; object_class->dispose = ni_window_route_dispose; widget_class->delete_event = ni_window_route_delete_event; obj_properties[PROP_NI_CLIENT] = g_param_spec_object ( "ni-client", "Network Inador Client", "The client object", NI_TYPE_CLIENT, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); obj_properties[PROP_NI_FAMILY] = g_param_spec_uint ( "family", "Family", "The routing family.", 0, G_MAXUINT, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); obj_properties[PROP_NI_TABLE_FILTER] = g_param_spec_uint ( "table-filter", "Tabla filtrada", "Tabla filtrada.", 0, G_MAXUINT, 0, G_PARAM_READWRITE); g_object_class_install_properties (object_class, N_PROPERTIES, obj_properties); } GtkWidget *ni_window_route_create_tree_for_routes (GtkTreeModel *store, GtkWidget **tree) { GtkWidget *scrolled; GtkAdjustment *h, *v; GtkTreeViewColumn *column; GtkCellRenderer *renderer; *tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (*tree), GTK_TREE_VIEW_GRID_LINES_HORIZONTAL); h = gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (*tree)); v = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (*tree)); scrolled = gtk_scrolled_window_new (h, v); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes ("Destino", renderer, "text", ROUTE_STORE_COL_DST_GW, NULL); gtk_tree_view_column_set_resizable (GTK_TREE_VIEW_COLUMN (column), TRUE); gtk_tree_view_append_column (GTK_TREE_VIEW (*tree), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new (); gtk_tree_view_column_set_resizable (GTK_TREE_VIEW_COLUMN (column), TRUE); gtk_tree_view_column_set_title (GTK_TREE_VIEW_COLUMN (column), "Puerta de enlace"); gtk_tree_view_column_pack_start (GTK_TREE_VIEW_COLUMN (column), renderer, TRUE); gtk_tree_view_column_set_cell_data_func (GTK_TREE_VIEW_COLUMN (column), renderer, ni_window_route_cell_renderer_gw_oiface, NULL, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (*tree), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes ("Métrica", renderer, "text", ROUTE_STORE_COL_METRIC, NULL); gtk_tree_view_column_set_resizable (GTK_TREE_VIEW_COLUMN (column), TRUE); gtk_tree_view_append_column (GTK_TREE_VIEW (*tree), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes ("Dir. Origen", renderer, "text", ROUTE_STORE_COL_PREFSRC, NULL); gtk_tree_view_column_set_resizable (GTK_TREE_VIEW_COLUMN (column), TRUE); gtk_tree_view_append_column (GTK_TREE_VIEW (*tree), column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes ("Tabla", renderer, "text", ROUTE_STORE_TABLE, NULL); gtk_tree_view_column_set_resizable (GTK_TREE_VIEW_COLUMN (column), TRUE); gtk_tree_view_append_column (GTK_TREE_VIEW (*tree), column); gtk_container_add (GTK_CONTAINER (scrolled), *tree); return scrolled; } static void ni_window_route_init (NIWindowRoute *window_route) { NIWindowRoutePrivate *priv = ni_window_route_get_instance_private (window_route); window_route->priv = priv; GtkWindow *window = GTK_WINDOW (window_route); GtkWidget *hbox, *label, *button, *image; GtkWidget *vbox, *scrolled, *vbox2; GtkTreeSelection *selection; GtkSizeGroup *size_l; GtkTreeIter iter; GtkCellRenderer *renderer; /* initialize all public and private members to reasonable default values. * They are all automatically initialized to 0 to begin with. */ priv->ni_client = NULL; priv->family = 0; gtk_window_set_title (window, "Tabla de ruteo"); priv->vbox = vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); gtk_container_add (GTK_CONTAINER (window), priv->vbox); vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 5); label = gtk_label_new ("Seleccione tabla de ruteo:"); gtk_label_set_xalign (GTK_LABEL (label), 0.0); gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0); priv->tables_store = gtk_list_store_new (NUM_TABLE_STORE_COLS, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_BOOLEAN); gtk_list_store_insert_with_values (priv->tables_store, &iter, -1, TABLE_STORE_TABLE, 0, TABLE_STORE_NAME, "Todas", TABLE_STORE_IS_NEW, FALSE, -1); gtk_list_store_insert_with_values (priv->tables_store, NULL, -1, TABLE_STORE_TABLE, 254, TABLE_STORE_NAME, "Main", TABLE_STORE_IS_NEW, FALSE, -1); gtk_list_store_insert_with_values (priv->tables_store, NULL, -1, TABLE_STORE_TABLE, 255, TABLE_STORE_NAME, "Local", TABLE_STORE_IS_NEW, FALSE, -1); gtk_list_store_insert_with_values (priv->tables_store, NULL, -1, TABLE_STORE_TABLE, 0, TABLE_STORE_NAME, NULL, TABLE_STORE_IS_NEW, TRUE, -1); /* Mantener ordenadas las tablas */ gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (priv->tables_store), ni_window_route_sort_tables_func, NULL, NULL); gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->tables_store), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING); priv->combo_tables = gtk_combo_box_new_with_model (GTK_TREE_MODEL (priv->tables_store)); renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_tables), renderer, TRUE); gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->combo_tables), renderer, ni_window_route_cell_renderer_table_id, NULL, NULL); gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_tables), &iter); gtk_box_pack_start (GTK_BOX (vbox2), priv->combo_tables, FALSE, FALSE, 0); g_signal_connect (priv->combo_tables, "changed", G_CALLBACK (ni_window_route_changed_combo_table_cb), window_route); /* Preparar el list store */ priv->routes_store = gtk_list_store_new (NUM_ROUTE_STORE_COLS, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_POINTER); priv->routes_filtered = gtk_tree_model_filter_new (GTK_TREE_MODEL (priv->routes_store), NULL); gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->routes_filtered), ni_window_route_tree_filter_routes_func, window_route, NULL); label = gtk_label_new ("Rutas:"); gtk_label_set_xalign (GTK_LABEL (label), 0.0); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5); gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); scrolled = ni_window_route_create_tree_for_routes (priv->routes_filtered, &priv->tree_routes); gtk_box_pack_start (GTK_BOX (hbox), scrolled, TRUE, TRUE, 0); /* Botonera del tree view */ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0); button = gtk_button_new_from_icon_name ("list-add", GTK_ICON_SIZE_LARGE_TOOLBAR); gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0); //g_signal_connect (button, "clicked", G_CALLBACK (ni_window_interface_addr_v6_button_add_cb), window_iface); priv->del_route_button = gtk_button_new_from_icon_name ("list-remove", GTK_ICON_SIZE_LARGE_TOOLBAR); gtk_box_pack_start (GTK_BOX (vbox2), priv->del_route_button, FALSE, FALSE, 0); //g_signal_connect (priv->del_route_button, "clicked", G_CALLBACK (ni_window_interface_addr_v6_button_del_cb), window_iface); gtk_widget_set_sensitive (priv->del_route_button, FALSE); /* Conectar la señal de cambio de selección del tree view */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_routes)); //g_signal_connect (selection, "changed", G_CALLBACK (has_ip_selected_v6_cb), window_iface); /* La botonera inferior */ hbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); gtk_button_box_set_layout (GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_END); gtk_box_pack_start (GTK_BOX (priv->vbox), hbox, FALSE, FALSE, 0); /* El boton de cerrar */ button = gtk_button_new_with_label ("Cerrar"); g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_hide_on_delete), window); image = gtk_image_new_from_icon_name ("window-close", GTK_ICON_SIZE_BUTTON); gtk_button_set_image (GTK_BUTTON (button), image); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); gtk_widget_show_all (priv->vbox); } GtkWidget* ni_window_route_new (NIClient *ni_client, int family) { NIWindowRoute *window_route; window_route = g_object_new (NI_TYPE_WINDOW_ROUTE, "type", GTK_WINDOW_TOPLEVEL, "ni-client", ni_client, "family", family, NULL); return GTK_WIDGET (window_route); }