NetworkInador/client-gtk/ni-window-route.c

652 lines
24 KiB
C

/*
* 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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/if_addr.h>
#include <linux/if.h>
#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);
}