summary refs log tree commit diff
path: root/tui/newt/nmt-newt-utils.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2014-07-06 02:16:10 +0200
committerMichael Biebl <biebl@debian.org>2014-07-06 02:16:10 +0200
commit33491bc4279481db8ae47213e34a6d695a0e8830 (patch)
tree097d2b0fdff3fae6885381ae5e57a182cd8cbbba /tui/newt/nmt-newt-utils.c
parent59c3714a494c3b3765657c0551ad82842d98a7d2 (diff)
Imported Upstream version 0.9.10.0 upstream/0.9.10.0
Diffstat (limited to 'tui/newt/nmt-newt-utils.c')
-rw-r--r--tui/newt/nmt-newt-utils.c392
1 files changed, 392 insertions, 0 deletions
diff --git a/tui/newt/nmt-newt-utils.c b/tui/newt/nmt-newt-utils.c
new file mode 100644
index 00000000..68d8c449
--- /dev/null
+++ b/tui/newt/nmt-newt-utils.c
@@ -0,0 +1,392 @@
+/* -*- 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-utils
+ * @short_description: Utility functions
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "nmt-newt-utils.h"
+
+static void
+nmt_newt_dialog_g_log_handler (const char     *log_domain,
+                               GLogLevelFlags  log_level,
+                               const char     *message,
+                               gpointer        user_data)
+{
+	const char *level_name;
+	char *full_message;
+	int screen_width, screen_height;
+	newtComponent text, ok, form;
+	newtGrid grid;
+
+	g_assert (!(log_level & G_LOG_FLAG_RECURSION));
+
+	if (log_level & G_LOG_LEVEL_DEBUG)
+		return;
+
+	switch (log_level & G_LOG_LEVEL_MASK) {
+	case G_LOG_LEVEL_ERROR:
+		level_name = "ERROR";
+		break;
+	case G_LOG_LEVEL_CRITICAL:
+		level_name = "CRITICAL";
+		break;
+	case G_LOG_LEVEL_WARNING:
+		level_name = "WARNING";
+		break;
+	case G_LOG_LEVEL_MESSAGE:
+		level_name = "Message";
+		break;
+	default:
+		level_name = NULL;
+	}
+
+	full_message = g_strdup_printf ("%s%s%s%s%s",
+	                                log_domain ? log_domain : "",
+	                                log_domain && level_name ? " " : "",
+	                                level_name ? level_name : "",
+	                                log_domain || level_name ? ": " : "",
+	                                message);
+
+	/* newtWinMessage() wraps the window too narrowly by default, so
+	 * we don't want to use that. But we intentionally avoid using any
+	 * NmtNewt classes, to avoid possible error recursion.
+	 */
+
+	newtGetScreenSize (&screen_width, &screen_height);
+	text = newtTextboxReflowed (-1, -1, full_message, MAX (70, screen_width - 10), 0, 0, 0);
+	g_free (full_message);
+
+	ok = newtButton (-1, -1, "OK");
+
+	grid = newtCreateGrid (1, 2);
+	newtGridSetField (grid, 0, 0, NEWT_GRID_COMPONENT, text, 0, 0, 0, 0, 0, 0);
+	newtGridSetField (grid, 0, 1, NEWT_GRID_COMPONENT, ok, 0, 1, 0, 0,
+	                  NEWT_ANCHOR_RIGHT, 0);
+
+	newtGridWrappedWindow (grid, (char *) (level_name ? level_name : ""));
+	newtGridFree (grid, TRUE);
+
+	form = newtForm (NULL, NULL, 0);
+	newtFormAddComponents (form, text, ok, NULL);
+	newtRunForm (form);
+	newtFormDestroy (form);
+	newtPopWindow ();
+}
+
+static void
+nmt_newt_basic_g_log_handler (const char     *log_domain,
+                              GLogLevelFlags  log_level,
+                              const char     *message,
+                              gpointer        user_data)
+{
+	newtSuspend ();
+	g_log_default_handler (log_domain, log_level, message, NULL);
+	newtResume ();
+}
+
+static void
+nmt_newt_suspend_callback (gpointer user_data)
+{
+	newtSuspend ();
+	kill (getpid (), SIGTSTP);
+	newtResume ();
+}
+
+/**
+ * nmt_newt_init:
+ *
+ * Wrapper for newtInit() that also does some nmt-newt-internal setup.
+ * This should be called once, before any other nmt-newt functions.
+ */
+void
+nmt_newt_init (void)
+{
+	newtInit ();
+	newtCls ();
+
+	newtSetColor (NEWT_COLORSET_CHECKBOX, "black", "lightgray");
+	newtSetColor (NMT_NEWT_COLORSET_BAD_LABEL, "red", "lightgray");
+	newtSetColor (NMT_NEWT_COLORSET_PLAIN_LABEL, "black", "lightgray");
+	newtSetColor (NMT_NEWT_COLORSET_DISABLED_BUTTON, "blue", "lightgray");
+	newtSetColor (NMT_NEWT_COLORSET_TEXTBOX_WITH_BACKGROUND, "black", "white");
+
+	if (g_getenv ("NMTUI_DEBUG"))
+		g_log_set_default_handler (nmt_newt_dialog_g_log_handler, NULL);
+	else
+		g_log_set_default_handler (nmt_newt_basic_g_log_handler, NULL);
+
+	newtSetSuspendCallback (nmt_newt_suspend_callback, NULL);
+}
+
+/**
+ * nmt_newt_finished:
+ *
+ * Wrapper for newtFinished(). Should be called at the end of the program.
+ */
+void
+nmt_newt_finished (void)
+{
+	newtFinished ();
+	g_log_set_default_handler (g_log_default_handler, NULL);
+}
+
+/**
+ * nmt_newt_message_dialog:
+ * @message: a printf()-style message format
+ * @...: arguments
+ *
+ * Displays the given message in a dialog box with a single "OK"
+ * button, and returns after the user clicks "OK".
+ */
+void
+nmt_newt_message_dialog  (const char *message,
+                          ...)
+{
+	va_list ap;
+	char *msg, *msg_lc, *ok_lc;
+
+	va_start (ap, message);
+	msg = g_strdup_vprintf (message, ap);
+	va_end (ap);
+
+	msg_lc = nmt_newt_locale_from_utf8 (msg);
+	ok_lc = nmt_newt_locale_from_utf8 (_("OK"));
+	newtWinMessage (NULL, ok_lc, "%s", msg_lc);
+
+	g_free (ok_lc);
+	g_free (msg_lc);
+	g_free (msg);
+}
+
+/**
+ * nmt_newt_choice_dialog:
+ * @button1: the label for the first button
+ * @button2: the label for the second button
+ * @message: a printf()-style message format
+ * @...: arguments
+ *
+ * Displays the given message in a dialog box with two buttons with
+ * the indicated labels, and waits for the user to click one.
+ *
+ * Returns: which button was clicked: 0 for @button1 or 1 for @button2
+ */
+int
+nmt_newt_choice_dialog (const char *button1,
+                        const char *button2,
+                        const char *message,
+                        ...)
+{
+	va_list ap;
+	char *msg, *msg_lc, *button1_lc, *button2_lc;
+	int choice;
+
+	va_start (ap, message);
+	msg = g_strdup_vprintf (message, ap);
+	va_end (ap);
+
+	msg_lc = nmt_newt_locale_from_utf8 (msg);
+	button1_lc = nmt_newt_locale_from_utf8 (button1);
+	button2_lc = nmt_newt_locale_from_utf8 (button2);
+	choice = newtWinChoice (NULL, button1_lc, button2_lc, "%s", msg_lc);
+
+	g_free (button1_lc);
+	g_free (button2_lc);
+	g_free (msg_lc);
+	g_free (msg);
+
+	return choice;
+}
+
+/**
+ * nmt_newt_locale_to_utf8:
+ * @str_lc: a string in the user's locale encoding
+ *
+ * Convenience wrapper around g_locale_to_utf8().
+ *
+ * Note that libnewt works in terms of the user's locale character
+ * set, NOT UTF-8, so all strings received from libnewt must be
+ * converted back to UTF-8 before being returned to the caller or used
+ * in other APIs.
+ *
+ * Returns: @str_lc, converted to UTF-8.
+ */
+char *
+nmt_newt_locale_to_utf8 (const char *str_lc)
+{
+	char *str_utf8;
+
+	str_utf8 = g_locale_to_utf8 (str_lc, -1, NULL, NULL, NULL);
+	if (!str_utf8)
+		str_utf8 = g_strdup ("");
+	return str_utf8;
+}
+
+/**
+ * nmt_newt_locale_from_utf8:
+ * @str_utf8: a UTF-8 string
+ *
+ * Convenience wrapper around g_locale_from_utf8().
+ *
+ * Note that libnewt works in terms of the user's locale character
+ * set, NOT UTF-8, so all strings from nmt-newt must be converted to
+ * locale encoding before being passed to libnewt.
+ *
+ * Returns: @str_utf8, converted to the user's locale encoding.
+ */
+char *
+nmt_newt_locale_from_utf8 (const char *str_utf8)
+{
+	char *str_lc;
+
+	str_lc = g_locale_from_utf8 (str_utf8, -1, NULL, NULL, NULL);
+	if (!str_lc)
+		str_lc = g_strdup ("");
+	return str_lc;
+}
+
+/**
+ * nmt_newt_text_width
+ * @str: a UTF-8 string
+ *
+ * Computes the width (in terminal columns) of @str.
+ *
+ * Returns: the width of @str
+ */
+int
+nmt_newt_text_width (const char *str)
+{
+	int width;
+	gunichar ch;
+
+	for (width = 0; *str; str = g_utf8_next_char (str)) {
+		ch = g_utf8_get_char (str);
+
+		/* Based on _vte_iso2022_unichar_width */
+		if (G_LIKELY (ch < 0x80))
+			width += 1;
+		else if (G_UNLIKELY (g_unichar_iszerowidth (ch)))
+			width += 0;
+		else if (G_UNLIKELY (g_unichar_iswide (ch)))
+			width += 2;
+		else
+			width += 1;
+	}
+
+	return width;
+}
+
+/**
+ * nmt_newt_edit_string:
+ * @data: data to edit
+ *
+ * libnewt does not have a multi-line editable text component, so
+ * nmt-newt provides this function instead, which will open the user's
+ * editor to edit a file containing the given @data (ensuring that the
+ * current screen state is saved before starting the editor and
+ * restored after it returns).
+ *
+ * Returns: the edited data, or %NULL if an error occurred.
+ */
+char *
+nmt_newt_edit_string (const char *data)
+{
+	gssize len, nwrote;
+	char *filename, *argv[3];
+	GError *error = NULL;
+	int fd, status;
+	char *new_data = NULL;
+
+	fd = g_file_open_tmp ("XXXXXX.json", &filename, &error);
+	if (fd == -1) {
+		nmt_newt_message_dialog (_("Could not create temporary file: %s"), error->message);
+		g_error_free (error);
+		return NULL;
+	}
+
+	len = data ? strlen (data) : 0;
+	while (len) {
+		do
+			nwrote = write (fd, data, len);
+		while (nwrote == -1 && errno == EINTR);
+
+		len -= nwrote;
+		data += nwrote;
+	}
+	close (fd);
+
+	argv[0] = (char *) g_getenv ("VISUAL");
+	if (!argv[0])
+		argv[0] = (char *) g_getenv ("EDITOR");
+	if (!argv[0])
+		argv[0] = (char *) "vi";
+	argv[1] = filename;
+	argv[2] = NULL;
+
+	newtSuspend ();
+	g_spawn_sync (NULL, argv, NULL,
+	              G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN,
+	              NULL, NULL, NULL, NULL,
+	              &status, &error);
+	newtResume ();
+
+	if (error) {
+		nmt_newt_message_dialog (_("Could not create temporary file: %s"), error->message);
+		g_error_free (error);
+		goto done;
+	}
+
+#if GLIB_CHECK_VERSION (2, 34, 0)
+	G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+	if (!g_spawn_check_exit_status (status, &error)) {
+		nmt_newt_message_dialog (_("Editor failed: %s"), error->message);
+		g_error_free (error);
+		goto done;
+	}
+	G_GNUC_END_IGNORE_DEPRECATIONS
+#else
+	if (WIFEXITED (status)) {
+		if (WEXITSTATUS (status) != 0)
+			nmt_newt_message_dialog (_("Editor failed with status %d"), WEXITSTATUS (status));
+	} else if (WIFSIGNALED (status))
+		nmt_newt_message_dialog (_("Editor failed with signal %d"), WTERMSIG (status));
+#endif
+
+	if (!g_file_get_contents (filename, &new_data, NULL, &error)) {
+		nmt_newt_message_dialog (_("Could not re-read file: %s"), error->message);
+		g_error_free (error);
+		goto done;
+	}
+
+ done:
+	unlink (filename);
+	g_free (filename);
+
+	return new_data;
+}	
+