diff options
author | Lubomir Rintel <lkundrak@v3.sk> | 2018-11-08 16:32:42 +0100 |
---|---|---|
committer | Lubomir Rintel <lkundrak@v3.sk> | 2019-05-06 12:12:43 +0200 |
commit | 6a8dd8653ce122909c24323044aa7a6cc2a51b85 (patch) | |
tree | 3287d4e1e2f1f31d7c06455bc459b9374a2aeb60 | |
parent | 442d9b844a90d3a55ea2e6e760d5b0ef96b8893d (diff) | |
download | network-manager-applet-6a8dd8653ce122909c24323044aa7a6cc2a51b85.tar.gz |
libnma/nma-bar-code-widget: add QR code widget
Draws a QR code exposing the connection details for a NMConnection. Can
print a business-card with the code too.
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | src/libnma/libnma.ver | 4 | ||||
-rw-r--r-- | src/libnma/meson.build | 1 | ||||
-rw-r--r-- | src/libnma/nma-bar-code-widget.c | 466 | ||||
-rw-r--r-- | src/libnma/nma-bar-code-widget.h | 47 | ||||
-rw-r--r-- | src/libnma/nma-bar-code-widget.ui | 45 | ||||
-rw-r--r-- | src/libnma/nma.gresource.xml | 1 |
7 files changed, 566 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index 2846db37..edd04651 100644 --- a/Makefile.am +++ b/Makefile.am @@ -512,6 +512,7 @@ libnma_h_pub = \ src/libnma/nma-vpn-password-dialog.h \ src/libnma/nma-ui-utils.h \ src/libnma/nma-cert-chooser.h \ + src/libnma/nma-bar-code-widget.h \ src/libnma/nma-bar-code.h libnma_c_real = \ @@ -523,6 +524,7 @@ libnma_c_real = \ src/libnma/init.c \ src/libnma/nma-cert-chooser.c \ src/libnma/nma-file-cert-chooser.c \ + src/libnma/nma-bar-code-widget.c \ src/libnma/nma-bar-code.c EXTRA_DIST += \ @@ -725,6 +727,7 @@ EXTRA_DIST += \ src/libnma/libnma.ver \ src/libnma/wifi.ui \ src/libnma/nma-mobile-wizard.ui \ + src/libnma/nma-bar-code-widget.ui \ src/libnma/nma-pkcs11-token-login-dialog.ui \ src/libnma/nma-pkcs11-cert-chooser-dialog.ui \ src/libnma/nma-vpn-password-dialog.ui \ diff --git a/src/libnma/libnma.ver b/src/libnma/libnma.ver index 833ec8f0..9b263d9c 100644 --- a/src/libnma/libnma.ver +++ b/src/libnma/libnma.ver @@ -109,4 +109,6 @@ libnma_1_8_22 { nma_bar_code_get_size; nma_bar_code_get_type; nma_bar_code_new; -} libnma_1_8_20; + nma_bar_code_widget_get_type; + nma_bar_code_widget_new; +} libnma_1_8_12; diff --git a/src/libnma/meson.build b/src/libnma/meson.build index 4c93884d..d959ec5f 100644 --- a/src/libnma/meson.build +++ b/src/libnma/meson.build @@ -36,6 +36,7 @@ install_headers( gir_sources = [version_header] + cert_chooser_sources + files( 'init.c', + 'nma-bar-code-widget.c', 'nma-bar-code.c', 'nma-mobile-providers.c', 'nma-mobile-wizard.c', diff --git a/src/libnma/nma-bar-code-widget.c b/src/libnma/nma-bar-code-widget.c new file mode 100644 index 00000000..a34d4c61 --- /dev/null +++ b/src/libnma/nma-bar-code-widget.c @@ -0,0 +1,466 @@ +/* nma-bar-code-widget.h - Renderer of a "QR" code + * + * Lubomir Rintel <lkundrak@v3.sk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the ree 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 + * Library 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright 2018, 2019 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include <stdint.h> + +#include "nma-bar-code.h" +#include "nma-bar-code-widget.h" + +#define CARD_WIDTH_PT 252 +#define CARD_HEIGHT_PT 144 + +struct _NMABarCodeWidget { + GtkBox parent; +}; + +struct _NMABarCodeWidgetClass { + GtkBoxClass parent_class; +}; + +typedef struct { + NMConnection *connection; + GtkWidget *qr_code; + NMABarCode *qr; +} NMABarCodeWidgetPrivate; + +/** + * SECTION:nma-bar-code-widget + * @title: NMABarCodeWidget + * + * This is a widget that displays a QR code for a connection suitable for + * optical recognition, e.g. scanning on a phone to connect to a hotspot. + */ + +G_DEFINE_TYPE_WITH_CODE (NMABarCodeWidget, nma_bar_code_widget, GTK_TYPE_BOX, + G_ADD_PRIVATE (NMABarCodeWidget)) + +enum { + PROP_0, + PROP_CONNECTION, + + LAST_PROP +}; + +#define NMA_BAR_CODE_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + NMA_TYPE_BAR_CODE_WIDGET, \ + NMABarCodeWidgetPrivate)) + +static void +do_qr_code_draw (GtkDrawingArea *area, cairo_t *cr, int width, int height, + gpointer user_data) +{ + NMABarCodeWidgetPrivate *priv = NMA_BAR_CODE_WIDGET_GET_PRIVATE (user_data); + int size = nma_bar_code_get_size (priv->qr); + + gtk_widget_set_size_request (priv->qr_code, (size + 2) * 3, (size + 2) * 3); + + cairo_set_source_rgba (cr, 1, 1, 1, 1); + cairo_fill (cr); + cairo_paint (cr); + cairo_set_source_rgba (cr, 0, 0, 0, 1); + cairo_scale (cr, (float)width / (size + 2), (float)height / (size + 2)); + cairo_translate (cr, 1, 1); + cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); + nma_bar_code_draw (priv->qr, cr); +} + +static gboolean +qr_code_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data) +{ + do_qr_code_draw (GTK_DRAWING_AREA (widget), cr, + gtk_widget_get_allocated_width (widget), + gtk_widget_get_allocated_height (widget), + user_data); + + return TRUE; +} + +static char * +shell_escape (const char *to_escape) +{ + GString *string = g_string_sized_new (32); + gboolean quote = *to_escape == '\0'; + const char *c; + + for (c = to_escape; *c; c++) { + if (strchr ("$\\\"", *c)) + g_string_append_c (string, '\\'); + else if (!g_ascii_isalnum(*c) && !strchr ("@%^+-_[]:", *c)) + quote = TRUE; + g_string_append_c (string, *c); + } + if (quote) { + g_string_append_c (string, '"'); + g_string_prepend_c (string, '"'); + } + return g_string_free (string, FALSE); +} + +static void +draw_one (NMABarCodeWidget *self, cairo_t *cr, + const char *psk, const char *ssid, + const char *nmcli_line1, const char *nmcli_line2) +{ + NMABarCodeWidgetPrivate *priv = NMA_BAR_CODE_WIDGET_GET_PRIVATE (self); + int size = nma_bar_code_get_size (priv->qr); + + cairo_save (cr); + + cairo_set_line_width (cr, 0.01); + cairo_rectangle (cr, 0, 0, CARD_WIDTH_PT, CARD_HEIGHT_PT); + cairo_stroke (cr); + cairo_translate (cr, 12, 12); + + cairo_save (cr); + cairo_scale (cr, (float)84/(float)size, (float)84/(float)size); + cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); + nma_bar_code_draw (priv->qr, cr); + cairo_restore (cr); + + cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + + cairo_move_to (cr, 96, 12); + cairo_set_font_size (cr, 12); + cairo_show_text(cr, _("Network")); + + cairo_move_to (cr, 96, 30); + cairo_set_font_size (cr, 16); + cairo_show_text(cr, ssid); + + cairo_select_font_face(cr, "Monospace", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + + cairo_move_to (cr, 0, 108); + cairo_set_font_size (cr, 10); + cairo_show_text(cr, nmcli_line1); + + if (psk) { + cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + + cairo_move_to (cr, 96, 60); + cairo_set_font_size (cr, 12); + cairo_show_text(cr, _("Password")); + + cairo_move_to (cr, 96, 78); + cairo_set_font_size (cr, 16); + cairo_show_text(cr, psk); + + cairo_select_font_face(cr, "Monospace", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + + cairo_move_to (cr, 24, 120); + cairo_set_font_size (cr, 10); + cairo_show_text(cr, nmcli_line2); + } + + cairo_restore (cr); +} + +static void +draw_page (GtkPrintOperation *operation, GtkPrintContext *context, int page_nr, gpointer user_data) +{ + NMABarCodeWidget *self = NMA_BAR_CODE_WIDGET (user_data); + NMABarCodeWidgetPrivate *priv = NMA_BAR_CODE_WIDGET_GET_PRIVATE (self); + cairo_t *cr = gtk_print_context_get_cairo_context (context); + double width = gtk_print_context_get_width (context); + double height = gtk_print_context_get_height (context); + int count_x = width / CARD_WIDTH_PT; + int count_y = height / CARD_HEIGHT_PT; + double spacing_x = (width - (count_x * CARD_WIDTH_PT)) / (count_x + 1); + double spacing_y = (height - (count_y * CARD_HEIGHT_PT)) / (count_y + 1); + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wsec; + gs_free char *nmcli_line1 = NULL; + gs_free char *nmcli_line2 = NULL; + const char *psk = NULL; + GBytes *ssid_bytes; + char *ssid; + char *tmp; + int x, y; + + s_wireless = nm_connection_get_setting_wireless (priv->connection); + if (!s_wireless) { + nma_bar_code_set_text (priv->qr, NULL); + gtk_widget_queue_draw (priv->qr_code); + return; + } + + ssid_bytes = nm_setting_wireless_get_ssid (s_wireless); + g_return_if_fail (ssid_bytes); + ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid_bytes, NULL), + g_bytes_get_size (ssid_bytes)); + g_return_if_fail (ssid); + + s_wsec = nm_connection_get_setting_wireless_security (priv->connection); + if (s_wsec) + psk = nm_setting_wireless_security_get_psk (s_wsec); + + tmp = shell_escape (ssid); + nmcli_line1 = g_strdup_printf ("$ nmcli d wifi con %s%s", tmp, psk ? " \\" : ""); + g_free (tmp); + + if (psk) { + tmp = shell_escape (psk); + nmcli_line2 = g_strdup_printf ("password %s", tmp); + g_free (tmp); + } + + for (y = 0; y < count_y; y++) { + cairo_save (cr); + + cairo_translate (cr, spacing_x, spacing_y); + + for (x = 0; x < count_x; x++) { + draw_one (self, cr, psk, ssid, nmcli_line1, nmcli_line2); + cairo_translate (cr, CARD_WIDTH_PT + spacing_x, 0); + } + + cairo_restore (cr); + cairo_translate (cr, 0, CARD_HEIGHT_PT + spacing_y); + } +} + +static gboolean +link_activated (GtkLabel *label, char *uri, gpointer user_data) +{ + NMABarCodeWidget *self = NMA_BAR_CODE_WIDGET (user_data); + GtkPrintOperation *print = gtk_print_operation_new (); + GtkWidget *window; + GError *error = NULL; + + g_return_val_if_fail (strcmp (uri, "nma:print") == 0, FALSE); + + window = gtk_widget_get_ancestor (GTK_WIDGET (self), GTK_TYPE_WINDOW); + + gtk_print_operation_set_n_pages (print, 1); + gtk_print_operation_set_use_full_page (print, TRUE); + gtk_print_operation_set_unit (print, GTK_UNIT_POINTS); + g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), self); + + if (!gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, + window ? GTK_WINDOW (window) : NULL, &error)) { + g_printerr ("%s", error->message); + g_error_free (error); + } + + g_object_unref (print); + + return FALSE; +} + +static void +string_append_mecard (GString *string, const char *tag, const char *text) +{ + const char *p; + bool is_hex = TRUE; + int start; + + if (!text) + return; + + g_string_append (string, tag); + start = string->len; + + for (p = text; *p; p++) { + if (!g_ascii_isxdigit (*p)) + is_hex = FALSE; + if (strchr ("\\\":;,", *p)) + g_string_append_c (string, '\\'); + g_string_append_c (string, *p); + } + + if (is_hex) { + g_string_insert_c (string, start, '\"'); + g_string_append_c (string, '\"'); + } + g_string_append_c (string, ';'); +} + +static void +update_qr_code (NMABarCodeWidget *self) +{ + NMABarCodeWidgetPrivate *priv = NMA_BAR_CODE_WIDGET_GET_PRIVATE (self); + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wsec; + const char *key_mgmt = NULL; + const char *psk = NULL; + const char *type = NULL; + GBytes *ssid_bytes; + char *ssid; + GString *string; + + if (!priv->qr) + return; + + s_wireless = nm_connection_get_setting_wireless (priv->connection); + if (!s_wireless) { + nma_bar_code_set_text (priv->qr, NULL); + gtk_widget_queue_draw (priv->qr_code); + return; + } + + ssid_bytes = nm_setting_wireless_get_ssid (s_wireless); + g_return_if_fail (ssid_bytes); + ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid_bytes, NULL), + g_bytes_get_size (ssid_bytes)); + g_return_if_fail (ssid); + + string = g_string_sized_new (64); + g_string_append (string, "WIFI:"); + + s_wsec = nm_connection_get_setting_wireless_security (priv->connection); + if (s_wsec) { + key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + psk = nm_setting_wireless_security_get_psk (s_wsec); + } + + if (key_mgmt == NULL) { + type = "nopass"; + } else if ( strcmp (key_mgmt, "none") == 0 + || strcmp (key_mgmt, "ieee8021x") == 0) { + type = "WEP"; + } else if ( strcmp (key_mgmt, "wpa-none") == 0 + || strcmp (key_mgmt, "wpa-psk") == 0) { + type = "WPA"; + } + + string_append_mecard(string, "T:", type); + string_append_mecard(string, "S:", ssid); + string_append_mecard(string, "P:", psk); + + if (nm_setting_wireless_get_hidden (s_wireless)) + g_string_append (string, "H:true;"); + + g_string_append_c (string, ';'); + nma_bar_code_set_text (priv->qr, string->str); + gtk_widget_queue_draw (priv->qr_code); + g_string_free (string, TRUE); +} + +static void +set_connection (NMABarCodeWidget *self, NMConnection *connection) +{ + NMABarCodeWidgetPrivate *priv = NMA_BAR_CODE_WIDGET_GET_PRIVATE (self); + + if (priv->connection) { + g_signal_handlers_disconnect_by_data (priv->connection, self); + g_clear_object (&priv->connection); + } + + if (connection) { + priv->connection = connection; + g_signal_connect_swapped (connection, "changed", G_CALLBACK (update_qr_code), self); + g_signal_connect_swapped (connection, "secrets-updated", G_CALLBACK (update_qr_code), self); + } +} + +static void +get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NMABarCodeWidgetPrivate *priv = NMA_BAR_CODE_WIDGET_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_CONNECTION: + g_value_set_object (value, priv->connection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NMABarCodeWidget *self = NMA_BAR_CODE_WIDGET (object); + + switch (prop_id) { + case PROP_CONNECTION: + set_connection (self, g_value_dup_object (value)); + update_qr_code (self); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nma_bar_code_widget_init (NMABarCodeWidget *self) +{ + NMABarCodeWidgetPrivate *priv = NMA_BAR_CODE_WIDGET_GET_PRIVATE (self); + + gtk_widget_init_template (GTK_WIDGET (self)); + + priv->qr = nma_bar_code_new (NULL); + g_signal_connect (priv->qr_code, "draw", G_CALLBACK (qr_code_draw), self); +} + +/** + * nma_bar_code_widget_new: + * @connection: connection to get network details from + * + * Returns: (transfer full): the bar code widget instance + * + * Since: 1.8.22 + */ +GtkWidget * +nma_bar_code_widget_new (NMConnection *connection) +{ + return g_object_new (NMA_TYPE_BAR_CODE_WIDGET, + NMA_BAR_CODE_WIDGET_CONNECTION, connection, + NULL); +} + +static void +finalize (GObject *object) +{ + NMABarCodeWidget *self = NMA_BAR_CODE_WIDGET (object); + NMABarCodeWidgetPrivate *priv = NMA_BAR_CODE_WIDGET_GET_PRIVATE (self); + + g_clear_object (&priv->qr); + set_connection (self, NULL); + + G_OBJECT_CLASS (nma_bar_code_widget_parent_class)->finalize (object); +} + +static void +nma_bar_code_widget_class_init (NMABarCodeWidgetClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + + g_object_class_install_property + (object_class, PROP_CONNECTION, + g_param_spec_object (NMA_BAR_CODE_WIDGET_CONNECTION, "", "", + NM_TYPE_CONNECTION, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + + gtk_widget_class_set_template_from_resource (widget_class, + "/org/freedesktop/network-manager-applet/nma-bar-code-widget.ui"); + + gtk_widget_class_bind_template_child_private (widget_class, NMABarCodeWidget, qr_code); + gtk_widget_class_bind_template_callback (widget_class, link_activated); +} diff --git a/src/libnma/nma-bar-code-widget.h b/src/libnma/nma-bar-code-widget.h new file mode 100644 index 00000000..9bd452a9 --- /dev/null +++ b/src/libnma/nma-bar-code-widget.h @@ -0,0 +1,47 @@ +/* nma-bar-code.h - Widget that renders a "QR" code + * + * Lubomir Rintel <lkundrak@v3.sk> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the ree 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 + * Library 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (C) 2018, 2019 Red Hat, Inc. + */ + +#ifndef __NMA_BAR_CODE_WIDGET_H__ +#define __NMA_BAR_CODE_WIDGET_H__ + +#include <glib-object.h> + +#include "nma-version.h" + +#define NMA_TYPE_BAR_CODE_WIDGET (nma_bar_code_widget_get_type ()) +#define NMA_BAR_CODE_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMA_TYPE_BAR_CODE_WIDGET, NMABarCodeWidget)) +#define NMA_BAR_CODE_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMA_TYPE_BAR_CODE_WIDGET, NMABarCodeWidgetClass)) +#define NMA_IS_BAR_CODE_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMA_TYPE_BAR_CODE_WIDGET)) +#define NMA_IS_BAR_CODE_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMA_TYPE_BAR_CODE_WIDGET)) +#define NMA_BAR_CODE_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMA_TYPE_BAR_CODE_WIDGET, NMABarCodeWidgetClass)) + +#define NMA_BAR_CODE_WIDGET_CONNECTION "connection" + +typedef struct _NMABarCodeWidget NMABarCodeWidget; +typedef struct _NMABarCodeWidgetClass NMABarCodeWidgetClass; + +NMA_AVAILABLE_IN_1_8_22 +GType nma_bar_code_widget_get_type (void) G_GNUC_CONST; + +NMA_AVAILABLE_IN_1_8_22 +GtkWidget *nma_bar_code_widget_new (NMConnection *connection); + +#endif /* __NMA_BAR_CODE_WIDGET_H__ */ diff --git a/src/libnma/nma-bar-code-widget.ui b/src/libnma/nma-bar-code-widget.ui new file mode 100644 index 00000000..d88f17c6 --- /dev/null +++ b/src/libnma/nma-bar-code-widget.ui @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.22.1 --> +<interface domain="nm-applet"> + <requires lib="gtk+" version="3.20"/> + <template class="NMABarCodeWidget" parent="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="margin_start">6</property> + <property name="margin_end">6</property> + <property name="margin_top">6</property> + <property name="margin_bottom">6</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkAspectFrame"> + <property name="expand">True</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label_xalign">0.5</property> + <child> + <object class="GtkDrawingArea" id="qr_code"> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + </child> + </object> + <packing> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="expand">False</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Scan with your phone or <a href="nma:print">Print</a></property> + <property name="use_markup">True</property> + <signal name="activate-link" handler="link_activated" swapped="no"/> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + </template> +</interface> diff --git a/src/libnma/nma.gresource.xml b/src/libnma/nma.gresource.xml index e5f803e7..ee1fcb20 100644 --- a/src/libnma/nma.gresource.xml +++ b/src/libnma/nma.gresource.xml @@ -6,5 +6,6 @@ <file preprocess="xml-stripblanks">nma-pkcs11-cert-chooser-dialog.ui</file> <file preprocess="xml-stripblanks">nma-mobile-wizard.ui</file> <file preprocess="xml-stripblanks">nma-vpn-password-dialog.ui</file> + <file preprocess="xml-stripblanks">nma-bar-code-widget.ui</file> </gresource> </gresources> |