diff options
| author | Michael Biebl <biebl@debian.org> | 2014-07-06 02:16:10 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2014-07-06 02:16:10 +0200 |
| commit | 33491bc4279481db8ae47213e34a6d695a0e8830 (patch) | |
| tree | 097d2b0fdff3fae6885381ae5e57a182cd8cbbba /tui/newt/nmt-newt-utils.c | |
| parent | 59c3714a494c3b3765657c0551ad82842d98a7d2 (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.c | 392 |
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; +} + |