Files
NetworkManager/tui/newt/nmt-newt-section.c
Dan Winship dd2e751b07 tui: fix Wi-Fi section of "nmtui connect" list in non-UTF-8 locales
In locales where the Wi-Fi signal-strength characters couldn't be
represented (eg, LANG=C), the entire Wi-Fi SSID + signal strength
string would fail to convert, causing the Wi-Fi section of the
connection list to show up as a series of blank lines.

Fix this by testing beforehand whether the characters can convert, and
falling back to plain ASCII if not. (And also, fix the similar code in
nmt-newt-section.c, which got broken when nmt_newt_locale_from_utf8()
was changed to never return NULL.)

Also, for paranoia, represent the signal-strength strings via \nnn
escapes rather than actual UTF-8 data, to guarantee that they get
compiled to the expected values even if the source files get
re-encoded.

https://bugzilla.gnome.org/show_bug.cgi?id=733007
2014-07-16 12:05:44 -04:00

410 lines
12 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/*
* This program 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.
*
* This program 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, see <http://www.gnu.org/licenses/>.
*
* Copyright 2013 Red Hat, Inc.
*/
/**
* SECTION:nmt-newt-section
* @short_description: A collapsible section
*
* #NmtNewtSection is a container with two children; the header and
* the body. The header is always visible, but the body is only
* visible when the container is #NmtNewtSection:open.
*
* Note that there is no default way to open and close an
* #NmtNewtSection. You need to implement this yourself. (Eg, by
* binding the #NmtToggleButton:active property of an #NmtToggleButton
* in the section's header to the section's #NmtNewtSection:open
* property.)
*
* In addition to the header and body, the #NmtNewtSection also draws
* a border along the left side, indicating the extent of the section.
*/
#include "config.h"
#include <string.h>
#include "nmt-newt-section.h"
#include "nmt-newt-grid.h"
#include "nmt-newt-label.h"
#include "nmt-newt-utils.h"
G_DEFINE_TYPE (NmtNewtSection, nmt_newt_section, NMT_TYPE_NEWT_CONTAINER)
#define NMT_NEWT_SECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_SECTION, NmtNewtSectionPrivate))
typedef struct {
NmtNewtWidget *header;
int hheight_req, hwidth_req;
NmtNewtWidget *body;
int bheight_req, bwidth_req;
NmtNewtWidget *border_grid;
NmtNewtWidget *border_open_label;
NmtNewtWidget *border_closed_label;
NmtNewtWidget *border_end_label;
GPtrArray *border_line_labels;
gboolean open;
} NmtNewtSectionPrivate;
static char *closed_glyph, *open_glyph, *line_glyph, *end_glyph;
enum {
PROP_0,
PROP_OPEN,
LAST_PROP
};
/**
* nmt_newt_section_new:
*
* Creates a new #NmtNewtSection
*
* Returns: a new #NmtNewtSection
*/
NmtNewtWidget *
nmt_newt_section_new (void)
{
return g_object_new (NMT_TYPE_NEWT_SECTION,
NULL);
}
static void
nmt_newt_section_init (NmtNewtSection *section)
{
NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section);
NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_section_parent_class);
priv->border_grid = nmt_newt_grid_new ();
parent_class->add (NMT_NEWT_CONTAINER (section), priv->border_grid);
priv->border_open_label = nmt_newt_label_new (open_glyph);
nmt_newt_widget_set_visible (priv->border_open_label, FALSE);
nmt_newt_grid_add (NMT_NEWT_GRID (priv->border_grid), priv->border_open_label, 0, 0);
priv->border_closed_label = nmt_newt_label_new (closed_glyph);
nmt_newt_grid_add (NMT_NEWT_GRID (priv->border_grid), priv->border_closed_label, 0, 0);
priv->border_end_label = nmt_newt_label_new (end_glyph);
nmt_newt_widget_set_visible (priv->border_open_label, FALSE);
nmt_newt_grid_add (NMT_NEWT_GRID (priv->border_grid), priv->border_end_label, 0, 1);
priv->border_line_labels = g_ptr_array_new ();
}
static void
nmt_newt_section_finalize (GObject *object)
{
NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (object);
g_ptr_array_unref (priv->border_line_labels);
G_OBJECT_CLASS (nmt_newt_section_parent_class)->finalize (object);
}
/**
* nmt_newt_section_set_header:
* @section: an #NmtNewtSection
* @header: the header widget
*
* Sets @section's header widget.
*/
void
nmt_newt_section_set_header (NmtNewtSection *section,
NmtNewtWidget *header)
{
NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section);
NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_section_parent_class);
NmtNewtContainer *container = NMT_NEWT_CONTAINER (section);
if (priv->header)
parent_class->remove (container, priv->header);
priv->header = header;
parent_class->add (container, header);
}
/**
* nmt_newt_section_get_header:
* @section: an #NmtNewtSection
*
* Gets @section's header widget.
*
* Returns: @section's header widget.
*/
NmtNewtWidget *
nmt_newt_section_get_header (NmtNewtSection *section)
{
NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section);
return priv->header;
}
/**
* nmt_newt_section_set_body:
* @section: an #NmtNewtSection
* @body: the body widget
*
* Sets @section's body widget.
*/
void
nmt_newt_section_set_body (NmtNewtSection *section,
NmtNewtWidget *body)
{
NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section);
NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_section_parent_class);
NmtNewtContainer *container = NMT_NEWT_CONTAINER (section);
if (priv->body)
parent_class->remove (container, priv->body);
priv->body = body;
parent_class->add (container, body);
}
/**
* nmt_newt_section_get_body:
* @section: an #NmtNewtSection
*
* Gets @section's body widget.
*
* Returns: @section's body widget.
*/
NmtNewtWidget *
nmt_newt_section_get_body (NmtNewtSection *section)
{
NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section);
return priv->body;
}
static void
nmt_newt_section_remove (NmtNewtContainer *container,
NmtNewtWidget *widget)
{
NmtNewtSection *section = NMT_NEWT_SECTION (container);
NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section);
NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_section_parent_class);
if (widget == priv->header)
priv->header = NULL;
else if (widget == priv->body)
priv->body = NULL;
else if (widget == priv->border_grid)
priv->border_grid = NULL;
parent_class->remove (container, widget);
}
static newtComponent *
nmt_newt_section_get_components (NmtNewtWidget *widget)
{
NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (widget);
newtComponent *child_cos;
GPtrArray *cos;
int i;
g_return_val_if_fail (priv->header != NULL && priv->body != NULL, NULL);
cos = g_ptr_array_new ();
child_cos = nmt_newt_widget_get_components (priv->border_grid);
for (i = 0; child_cos[i]; i++)
g_ptr_array_add (cos, child_cos[i]);
g_free (child_cos);
child_cos = nmt_newt_widget_get_components (priv->header);
for (i = 0; child_cos[i]; i++)
g_ptr_array_add (cos, child_cos[i]);
g_free (child_cos);
if (priv->open) {
child_cos = nmt_newt_widget_get_components (priv->body);
for (i = 0; child_cos[i]; i++)
g_ptr_array_add (cos, child_cos[i]);
g_free (child_cos);
}
g_ptr_array_add (cos, NULL);
return (newtComponent *) g_ptr_array_free (cos, FALSE);
}
static void
nmt_newt_section_size_request (NmtNewtWidget *widget,
int *width,
int *height)
{
NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (widget);
int border_width, border_height;
g_return_if_fail (priv->header != NULL && priv->body != NULL);
nmt_newt_widget_size_request (priv->border_grid, &border_width, &border_height);
nmt_newt_widget_size_request (priv->header, &priv->hwidth_req, &priv->hheight_req);
nmt_newt_widget_size_request (priv->body, &priv->bwidth_req, &priv->bheight_req);
*width = MAX (priv->hwidth_req, priv->bwidth_req) + 2;
*height = priv->open ? priv->hheight_req + priv->bheight_req + 1 : priv->hheight_req;
}
static void
adjust_border_for_allocation (NmtNewtSectionPrivate *priv,
int height)
{
int i;
/* We have to use a series of one-line labels rather than a multi-line
* textbox, because newt will hide any component that's partially offscreen,
* but we want the on-screen portion of the border to show even if part of
* it is offscreen.
*/
if (height == 1) {
nmt_newt_widget_set_visible (priv->border_closed_label, TRUE);
nmt_newt_widget_set_visible (priv->border_open_label, FALSE);
for (i = 0; i < priv->border_line_labels->len; i++)
nmt_newt_widget_set_visible (priv->border_line_labels->pdata[i], FALSE);
nmt_newt_widget_set_visible (priv->border_end_label, FALSE);
} else {
nmt_newt_widget_set_visible (priv->border_closed_label, FALSE);
nmt_newt_widget_set_visible (priv->border_open_label, TRUE);
for (i = 0; i < height - 2; i++) {
if (i >= priv->border_line_labels->len) {
NmtNewtWidget *label;
label = nmt_newt_label_new (line_glyph);
g_ptr_array_add (priv->border_line_labels, label);
nmt_newt_grid_add (NMT_NEWT_GRID (priv->border_grid), label, 0, i + 1);
} else
nmt_newt_widget_set_visible (priv->border_line_labels->pdata[i], TRUE);
}
nmt_newt_widget_set_visible (priv->border_end_label, TRUE);
nmt_newt_grid_move (NMT_NEWT_GRID (priv->border_grid), priv->border_end_label, 0, height - 1);
}
}
static void
nmt_newt_section_size_allocate (NmtNewtWidget *widget,
int x,
int y,
int width,
int height)
{
NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (widget);
adjust_border_for_allocation (priv, height);
nmt_newt_widget_size_allocate (priv->border_grid, x, y, 1, height);
nmt_newt_widget_size_allocate (priv->header, x + 2, y, width, priv->hheight_req);
if (priv->open) {
nmt_newt_widget_size_allocate (priv->body, x + 2, y + priv->hheight_req,
width, height - priv->hheight_req);
}
}
static void
nmt_newt_section_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (object);
switch (prop_id) {
case PROP_OPEN:
priv->open = g_value_get_boolean (value);
nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (object));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
nmt_newt_section_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (object);
switch (prop_id) {
case PROP_OPEN:
g_value_set_boolean (value, priv->open);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
nmt_newt_section_class_init (NmtNewtSectionClass *section_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (section_class);
NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (section_class);
NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (section_class);
g_type_class_add_private (section_class, sizeof (NmtNewtSectionPrivate));
/* virtual methods */
object_class->set_property = nmt_newt_section_set_property;
object_class->get_property = nmt_newt_section_get_property;
object_class->finalize = nmt_newt_section_finalize;
widget_class->get_components = nmt_newt_section_get_components;
widget_class->size_request = nmt_newt_section_size_request;
widget_class->size_allocate = nmt_newt_section_size_allocate;
container_class->remove = nmt_newt_section_remove;
/* properties */
/**
* NmtNewtSection:open:
*
* %TRUE if the section is open (ie, its body is visible), %FALSE
* if not.
*/
g_object_class_install_property
(object_class, PROP_OPEN,
g_param_spec_boolean ("open", "", "",
FALSE,
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
/* globals */
closed_glyph = nmt_newt_locale_from_utf8 ("\342\225\220"); /* ═ */
open_glyph = nmt_newt_locale_from_utf8 ("\342\225\244"); /* ╤ */
line_glyph = nmt_newt_locale_from_utf8 ("\342\224\202"); /* │ */
end_glyph = nmt_newt_locale_from_utf8 ("\342\224\224"); /* └ */
if (!*closed_glyph || !*open_glyph || !*line_glyph || !*end_glyph) {
g_free (closed_glyph);
g_free (open_glyph);
g_free (line_glyph);
g_free (end_glyph);
closed_glyph = g_strdup ("-");
open_glyph = g_strdup ("+");
line_glyph = g_strdup ("|");
end_glyph = g_strdup ("\\");
}
}