network: Use a table-like widget to edit routes

According to the latest mockups for the connection editor dialog [1],
the IPv4 and IPv6 pages are supposed to use a table-like editor to
manage the routes. This editor is not only easier to comprehend, but
also improves the size of the dialog, requiring much less vertical
space to present the routes.

The current implementation, however, uses a vertical layout and a toolbar,
which is inefficient in its usage of space.

Fix that by implementing the table-like editor widget, both in IPv4
and IPv6 pages.

[1] https://raw.githubusercontent.com/gnome-design-team/gnome-mockups/master/system-settings/network/aday2/network-wires.png

https://bugzilla.gnome.org/show_bug.cgi?id=779841
This commit is contained in:
Georges Basile Stavracas Neto 2017-05-23 13:04:52 -03:00 committed by Rui Matos
parent 2e570099f6
commit 08657fac44
4 changed files with 320 additions and 142 deletions

View file

@ -34,6 +34,8 @@
#define RADIO_IS_ACTIVE(x) (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object(CE_PAGE (page)->builder, x))))
static void ensure_empty_routes_row (CEPageIP4 *page);
G_DEFINE_TYPE (CEPageIP4, ce_page_ip4, CE_TYPE_PAGE)
enum {
@ -88,7 +90,7 @@ static void
update_row_sensitivity (CEPageIP4 *page, GtkWidget *list)
{
GList *children, *l;
gint rows = 0;
gint rows = 0, i = 0;
children = gtk_container_get_children (GTK_CONTAINER (list));
for (l = children; l; l = l->next) {
@ -105,7 +107,7 @@ update_row_sensitivity (CEPageIP4 *page, GtkWidget *list)
button = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "delete-button"));
if (button != NULL)
gtk_widget_set_sensitive (button, rows > 1);
gtk_widget_set_sensitive (button, rows > 1 && ++i < rows);
}
g_list_free (children);
}
@ -152,6 +154,29 @@ remove_row (GtkButton *button, CEPageIP4 *page)
update_row_gateway_visibility (page);
}
static gboolean
validate_row (GtkWidget *row)
{
GtkWidget *box;
GList *children, *l;
gboolean valid;
valid = FALSE;
box = gtk_bin_get_child (GTK_BIN (row));
children = gtk_container_get_children (GTK_CONTAINER (box));
for (l = children; l != NULL; l = l->next) {
if (!GTK_IS_ENTRY (l->data))
continue;
valid = valid || gtk_entry_get_text_length (l->data) > 0;
}
g_list_free (children);
return valid;
}
static gint
sort_first_last (gconstpointer a, gconstpointer b, gpointer data)
{
@ -235,6 +260,7 @@ add_address_row (CEPageIP4 *page,
gtk_widget_set_no_show_all (widget, FALSE);
delete_button = gtk_button_new ();
gtk_widget_set_sensitive (delete_button, FALSE);
gtk_style_context_add_class (gtk_widget_get_style_context (delete_button), "image-button");
g_signal_connect (delete_button, "clicked", G_CALLBACK (remove_row), page);
image = gtk_image_new_from_icon_name ("user-trash-symbolic", GTK_ICON_SIZE_MENU);
@ -382,92 +408,76 @@ add_route_row (CEPageIP4 *page,
const gchar *gateway,
gint metric)
{
GtkSizeGroup *group;
GtkWidget *row;
GtkWidget *row_grid;
GtkWidget *label;
GtkWidget *row_box;
GtkWidget *widget;
GtkWidget *delete_button;
GtkWidget *image;
row = gtk_list_box_row_new ();
row_grid = gtk_grid_new ();
label = gtk_label_new (_("Address"));
gtk_widget_set_halign (label, GTK_ALIGN_END);
gtk_grid_attach (GTK_GRID (row_grid), label, 1, 1, 1, 1);
row_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_style_context_add_class (gtk_widget_get_style_context (row_box), "linked");
widget = gtk_entry_new ();
gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
g_signal_connect_swapped (widget, "changed", G_CALLBACK (ce_page_changed), page);
g_signal_connect_swapped (widget, "activate", G_CALLBACK (ensure_empty_routes_row), page);
g_object_set_data (G_OBJECT (row), "address", widget);
gtk_entry_set_text (GTK_ENTRY (widget), address);
gtk_widget_set_margin_start (widget, 10);
gtk_widget_set_margin_end (widget, 10);
gtk_entry_set_width_chars (GTK_ENTRY (widget), 16);
gtk_widget_set_hexpand (widget, TRUE);
gtk_grid_attach (GTK_GRID (row_grid), widget, 2, 1, 1, 1);
gtk_container_add (GTK_CONTAINER (row_box), widget);
label = gtk_label_new (_("Netmask"));
gtk_widget_set_halign (label, GTK_ALIGN_END);
gtk_grid_attach (GTK_GRID (row_grid), label, 1, 2, 1, 1);
widget = gtk_entry_new ();
gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
g_signal_connect_swapped (widget, "changed", G_CALLBACK (ce_page_changed), page);
g_signal_connect_swapped (widget, "activate", G_CALLBACK (ensure_empty_routes_row), page);
g_object_set_data (G_OBJECT (row), "netmask", widget);
gtk_entry_set_text (GTK_ENTRY (widget), netmask);
gtk_widget_set_margin_start (widget, 10);
gtk_widget_set_margin_end (widget, 10);
gtk_entry_set_width_chars (GTK_ENTRY (widget), 16);
gtk_widget_set_hexpand (widget, TRUE);
gtk_grid_attach (GTK_GRID (row_grid), widget, 2, 2, 1, 1);
gtk_container_add (GTK_CONTAINER (row_box), widget);
label = gtk_label_new (_("Gateway"));
gtk_widget_set_halign (label, GTK_ALIGN_END);
gtk_grid_attach (GTK_GRID (row_grid), label, 1, 3, 1, 1);
widget = gtk_entry_new ();
gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
g_signal_connect_swapped (widget, "changed", G_CALLBACK (ce_page_changed), page);
g_signal_connect_swapped (widget, "activate", G_CALLBACK (ensure_empty_routes_row), page);
g_object_set_data (G_OBJECT (row), "gateway", widget);
gtk_entry_set_text (GTK_ENTRY (widget), gateway);
gtk_widget_set_margin_start (widget, 10);
gtk_widget_set_margin_end (widget, 10);
gtk_entry_set_width_chars (GTK_ENTRY (widget), 16);
gtk_widget_set_hexpand (widget, TRUE);
gtk_grid_attach (GTK_GRID (row_grid), widget, 2, 3, 1, 1);
gtk_container_add (GTK_CONTAINER (row_box), widget);
/* Translators: Please see https://en.wikipedia.org/wiki/Metrics_(networking) */
label = gtk_label_new (C_("network parameters", "Metric"));
gtk_widget_set_halign (label, GTK_ALIGN_END);
gtk_grid_attach (GTK_GRID (row_grid), label, 1, 4, 1, 1);
widget = gtk_entry_new ();
gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
g_signal_connect_swapped (widget, "changed", G_CALLBACK (ce_page_changed), page);
g_signal_connect_swapped (widget, "activate", G_CALLBACK (ensure_empty_routes_row), page);
g_object_set_data (G_OBJECT (row), "metric", widget);
if (metric >= 0) {
gchar *s = g_strdup_printf ("%d", metric);
gtk_entry_set_text (GTK_ENTRY (widget), s);
g_free (s);
}
gtk_widget_set_margin_start (widget, 10);
gtk_widget_set_margin_end (widget, 10);
gtk_entry_set_width_chars (GTK_ENTRY (widget), 5);
gtk_widget_set_hexpand (widget, TRUE);
gtk_grid_attach (GTK_GRID (row_grid), widget, 2, 4, 1, 1);
gtk_container_add (GTK_CONTAINER (row_box), widget);
group = GTK_SIZE_GROUP (gtk_builder_get_object (CE_PAGE (page)->builder, "routes_metric_sizegroup"));
gtk_size_group_add_widget (group, widget);
delete_button = gtk_button_new ();
gtk_style_context_add_class (gtk_widget_get_style_context (delete_button), "image-button");
g_signal_connect (delete_button, "clicked", G_CALLBACK (remove_row), page);
image = gtk_image_new_from_icon_name ("user-trash-symbolic", GTK_ICON_SIZE_MENU);
image = gtk_image_new_from_icon_name ("edit-delete-symbolic", GTK_ICON_SIZE_MENU);
atk_object_set_name (gtk_widget_get_accessible (delete_button), _("Delete Route"));
gtk_button_set_image (GTK_BUTTON (delete_button), image);
gtk_widget_set_halign (delete_button, GTK_ALIGN_CENTER);
gtk_widget_set_valign (delete_button, GTK_ALIGN_CENTER);
gtk_grid_attach (GTK_GRID (row_grid), delete_button, 3, 1, 1, 4);
gtk_container_add (GTK_CONTAINER (row_box), delete_button);
g_object_set_data (G_OBJECT (row), "delete-button", delete_button);
gtk_grid_set_row_spacing (GTK_GRID (row_grid), 10);
gtk_widget_set_margin_start (row_grid, 10);
gtk_widget_set_margin_end (row_grid, 10);
gtk_widget_set_margin_top (row_grid, 10);
gtk_widget_set_margin_bottom (row_grid, 10);
gtk_widget_set_halign (row_grid, GTK_ALIGN_FILL);
group = GTK_SIZE_GROUP (gtk_builder_get_object (CE_PAGE (page)->builder, "routes_sizegroup"));
gtk_size_group_add_widget (group, delete_button);
gtk_container_add (GTK_CONTAINER (row), row_grid);
gtk_container_add (GTK_CONTAINER (row), row_box);
gtk_widget_show_all (row);
gtk_container_add (GTK_CONTAINER (page->routes_list), row);
@ -475,33 +485,41 @@ add_route_row (CEPageIP4 *page,
}
static void
add_empty_route_row (CEPageIP4 *page)
ensure_empty_routes_row (CEPageIP4 *page)
{
add_route_row (page, "", "", "", -1);
GList *children, *l;
children = gtk_container_get_children (GTK_CONTAINER (page->routes_list));
l = children;
while (l && l->next)
l = l->next;
/* Add the last, stub row if needed*/
if (!l || validate_row (l->data))
add_route_row (page, "", "", "", -1);
g_list_free (children);
}
static void
add_routes_section (CEPageIP4 *page)
{
GtkWidget *widget;
GtkWidget *frame;
GtkWidget *list;
gint i;
widget = GTK_WIDGET (gtk_builder_get_object (CE_PAGE (page)->builder, "routes_section"));
frame = gtk_frame_new (NULL);
gtk_container_add (GTK_CONTAINER (widget), frame);
page->routes_list = list = gtk_list_box_new ();
gtk_list_box_set_selection_mode (GTK_LIST_BOX (list), GTK_SELECTION_NONE);
gtk_list_box_set_header_func (GTK_LIST_BOX (list), cc_list_box_update_header_func, NULL, NULL);
gtk_list_box_set_sort_func (GTK_LIST_BOX (list), (GtkListBoxSortFunc)sort_first_last, NULL, NULL);
gtk_container_add (GTK_CONTAINER (frame), list);
gtk_container_add (GTK_CONTAINER (widget), list);
page->auto_routes = GTK_SWITCH (gtk_builder_get_object (CE_PAGE (page)->builder, "auto_routes_switch"));
gtk_switch_set_active (page->auto_routes, !nm_setting_ip_config_get_ignore_auto_routes (page->setting));
g_signal_connect (page->auto_routes, "notify::active", G_CALLBACK (switch_toggled), page);
add_section_toolbar (page, widget, G_CALLBACK (add_empty_route_row));
for (i = 0; i < nm_setting_ip_config_get_num_routes (page->setting); i++) {
NMIPRoute *route;
@ -522,7 +540,7 @@ add_routes_section (CEPageIP4 *page)
nm_ip_route_get_metric (route));
}
if (nm_setting_ip_config_get_num_routes (page->setting) == 0)
add_empty_route_row (page);
ensure_empty_routes_row (page);
gtk_widget_show_all (widget);
}
@ -842,6 +860,9 @@ ui_to_setting (CEPageIP4 *page)
route = nm_ip_route_new (AF_INET, text_address, netmask, text_gateway, metric, NULL);
if (route)
g_ptr_array_add (routes, route);
if (!l || !l->next)
ensure_empty_routes_row (page);
}
g_list_free (children);