/* vim: colorcolumn=80 ts=4 sw=4 */ /* * util.c * * Copyright © 2002 Sun Microsystems, Inc. * © 1999, 2000 Red Hat Inc. * © 1998 James Henstridge * © 1995-2002 Free Software Foundation * © 2021-2023 Logan Rathbone * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * * Authors: Glynn Foster * Havoc Pennington * James Henstridge * Tom Tromey */ #include "util.h" #include "zenity.h" #include #include #include #include #include #include #include "config.h" #define ZENITY_OK_DEFAULT 0 #define ZENITY_CANCEL_DEFAULT 1 #define ZENITY_ESC_DEFAULT 1 #define ZENITY_ERROR_DEFAULT -1 #define ZENITY_EXTRA_DEFAULT 127 #define ZENITY_UI_RESOURCE_PATH RESOURCE_BASE_PATH "/zenity.ui" GIcon * zenity_util_gicon_from_string (const char *str) { GIcon *icon = NULL; g_autoptr(GFile) icon_file = NULL; if (str) { icon_file = g_file_new_for_path (str); if (g_file_query_exists (icon_file, NULL)) { icon = g_file_icon_new (icon_file); } else { g_debug (_("Icon filename %s not found; trying theme icon."), str); icon = g_themed_icon_new_with_default_fallbacks (str); } } return icon; } GtkBuilder * zenity_util_load_ui_file (const char *root_widget, ...) { va_list args; char *arg = NULL; GPtrArray *ptrarray; GtkBuilder *builder = gtk_builder_new (); g_autoptr(GError) error = NULL; char **objects; gboolean result = FALSE; gtk_builder_set_translation_domain (builder, GETTEXT_PACKAGE); /* We have at least the root_widget and a NULL */ ptrarray = g_ptr_array_sized_new (2); g_ptr_array_add (ptrarray, g_strdup (root_widget)); va_start (args, root_widget); arg = va_arg (args, char *); while (arg) { g_ptr_array_add (ptrarray, g_strdup (arg)); arg = va_arg (args, char *); } va_end (args); /* Enforce terminating NULL */ g_ptr_array_add (ptrarray, NULL); objects = (char **)g_ptr_array_free (ptrarray, FALSE); /* Make sure this custom widget type is registered before this initial * GtkBuilder method call. */ g_type_ensure (ZENITY_TREE_TYPE_COLUMN_VIEW); result = gtk_builder_add_objects_from_resource (builder, ZENITY_UI_RESOURCE_PATH, (const char **)objects, &error); g_strfreev (objects); if (! result) { g_error ("Could not load ui resource %s: %s", ZENITY_UI_RESOURCE_PATH, error->message); } /* This should never happen, but if an unexpected error is logged, print * it for debugging purposes. */ if (error) { g_debug ("%s: Error generated: %s", __func__, error->message); } return builder; } char * zenity_util_strip_newline (char *string) { gsize len; g_return_val_if_fail (string != NULL, NULL); len = strlen (string); while (len--) { if (string[len] == '\n') string[len] = '\0'; else break; } return string; } gboolean zenity_util_fill_file_buffer (GtkTextBuffer *buffer, const char *filename) { GtkTextIter iter, end; FILE *f; char buf[2048]; int remaining = 0; if (filename == NULL) return FALSE; f = fopen (filename, "r"); if (f == NULL) { g_warning ("Cannot open file '%s': %s", filename, g_strerror (errno)); return FALSE; } gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0); while (!feof (f)) { int count; const char *leftover; int to_read = 2047 - remaining; count = fread (buf + remaining, 1, to_read, f); buf[count + remaining] = '\0'; g_utf8_validate (buf, count + remaining, &leftover); g_assert (g_utf8_validate (buf, leftover - buf, NULL)); gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf); remaining = (buf + remaining + count) - leftover; memmove(buf, leftover, remaining); if (remaining > 6 || count < to_read) break; } if (remaining) { g_warning ( "Invalid UTF-8 data encountered reading file '%s'", filename); return FALSE; } /* We had a newline in the buffer to begin with. (The buffer always contains * a newline, so we delete to the end of the buffer to clean up. */ gtk_text_buffer_get_end_iter (buffer, &end); gtk_text_buffer_delete (buffer, &iter, &end); gtk_text_buffer_set_modified (buffer, FALSE); return TRUE; } void zenity_util_show_help (GError **error) { g_autofree char *tmp = g_find_program_in_path ("yelp"); if (tmp) g_spawn_command_line_async ("yelp help:zenity", error); } int zenity_util_return_exit_code (ZenityExitCode value) { const char *env_var = NULL; int retval; switch (value) { case ZENITY_OK: env_var = g_getenv ("ZENITY_OK"); if (!env_var) env_var = g_getenv ("DIALOG_OK"); if (!env_var) retval = ZENITY_OK_DEFAULT; break; case ZENITY_CANCEL: env_var = g_getenv ("ZENITY_CANCEL"); if (!env_var) env_var = g_getenv ("DIALOG_CANCEL"); if (!env_var) retval = ZENITY_CANCEL_DEFAULT; break; case ZENITY_ESC: env_var = g_getenv ("ZENITY_ESC"); if (!env_var) env_var = g_getenv ("DIALOG_ESC"); if (!env_var) retval = ZENITY_ESC_DEFAULT; break; case ZENITY_EXTRA: env_var = g_getenv ("ZENITY_EXTRA"); if (!env_var) env_var = g_getenv ("DIALOG_EXTRA"); if (!env_var) retval = ZENITY_EXTRA_DEFAULT; break; case ZENITY_ERROR: env_var = g_getenv ("ZENITY_ERROR"); if (!env_var) env_var = g_getenv ("DIALOG_ERROR"); if (!env_var) retval = ZENITY_ERROR_DEFAULT; break; case ZENITY_TIMEOUT: env_var = g_getenv ("ZENITY_TIMEOUT"); if (!env_var) env_var = g_getenv ("DIALOG_TIMEOUT"); if (!env_var) retval = ZENITY_TIMEOUT; break; default: retval = 1; } if (env_var) retval = atoi (env_var); return retval; } void zenity_util_exit_code_with_data (ZenityExitCode value, ZenityData *zen_data) { zen_data->exit_code = zenity_util_return_exit_code (value); } /* This function was written by Matthias Clasen and is included somewhere in * the GTK source tree.. I believe it is also included in libdazzle, but I * didn't want to include a whole dependency just for one function. LGPL, but * credit where credit is due! */ char * zenity_util_pango_font_description_to_css (PangoFontDescription *desc) { GString *s; PangoFontMask set; s = g_string_new ("* { "); set = pango_font_description_get_set_fields (desc); if (set & PANGO_FONT_MASK_FAMILY) { g_string_append (s, "font-family: "); g_string_append (s, pango_font_description_get_family (desc)); g_string_append (s, "; "); } if (set & PANGO_FONT_MASK_STYLE) { switch (pango_font_description_get_style (desc)) { case PANGO_STYLE_NORMAL: g_string_append (s, "font-style: normal; "); break; case PANGO_STYLE_OBLIQUE: g_string_append (s, "font-style: oblique; "); break; case PANGO_STYLE_ITALIC: g_string_append (s, "font-style: italic; "); break; } } if (set & PANGO_FONT_MASK_VARIANT) { switch (pango_font_description_get_variant (desc)) { case PANGO_VARIANT_NORMAL: g_string_append (s, "font-variant: normal; "); break; case PANGO_VARIANT_SMALL_CAPS: g_string_append (s, "font-variant: small-caps; "); break; default: break; } } if (set & PANGO_FONT_MASK_WEIGHT) { switch (pango_font_description_get_weight (desc)) { case PANGO_WEIGHT_THIN: g_string_append (s, "font-weight: 100; "); break; case PANGO_WEIGHT_ULTRALIGHT: g_string_append (s, "font-weight: 200; "); break; case PANGO_WEIGHT_LIGHT: case PANGO_WEIGHT_SEMILIGHT: g_string_append (s, "font-weight: 300; "); break; case PANGO_WEIGHT_BOOK: case PANGO_WEIGHT_NORMAL: g_string_append (s, "font-weight: 400; "); break; case PANGO_WEIGHT_MEDIUM: g_string_append (s, "font-weight: 500; "); break; case PANGO_WEIGHT_SEMIBOLD: g_string_append (s, "font-weight: 600; "); break; case PANGO_WEIGHT_BOLD: g_string_append (s, "font-weight: 700; "); break; case PANGO_WEIGHT_ULTRABOLD: g_string_append (s, "font-weight: 800; "); break; case PANGO_WEIGHT_HEAVY: case PANGO_WEIGHT_ULTRAHEAVY: g_string_append (s, "font-weight: 900; "); break; } } if (set & PANGO_FONT_MASK_STRETCH) { switch (pango_font_description_get_stretch (desc)) { case PANGO_STRETCH_ULTRA_CONDENSED: g_string_append (s, "font-stretch: ultra-condensed; "); break; case PANGO_STRETCH_EXTRA_CONDENSED: g_string_append (s, "font-stretch: extra-condensed; "); break; case PANGO_STRETCH_CONDENSED: g_string_append (s, "font-stretch: condensed; "); break; case PANGO_STRETCH_SEMI_CONDENSED: g_string_append (s, "font-stretch: semi-condensed; "); break; case PANGO_STRETCH_NORMAL: g_string_append (s, "font-stretch: normal; "); break; case PANGO_STRETCH_SEMI_EXPANDED: g_string_append (s, "font-stretch: semi-expanded; "); break; case PANGO_STRETCH_EXPANDED: g_string_append (s, "font-stretch: expanded; "); break; case PANGO_STRETCH_EXTRA_EXPANDED: g_string_append (s, "font-stretch: extra-expanded; "); break; case PANGO_STRETCH_ULTRA_EXPANDED: g_string_append (s, "font-stretch: ultra-expanded; "); break; } } if (set & PANGO_FONT_MASK_SIZE) { g_string_append_printf (s, "font-size: %dpt; ", pango_font_description_get_size (desc) / PANGO_SCALE); } g_string_append (s, "}"); return g_string_free (s, FALSE); } void zenity_util_show_dialog (GtkWidget *dialog) { gtk_widget_show (dialog); } gboolean zenity_util_timeout_handle (AdwMessageDialog *dialog) { if (dialog) { adw_message_dialog_response (dialog, "timeout"); } else { exit (ZENITY_TIMEOUT); } return FALSE; } void zenity_util_gapp_main (GtkWindow *window) { GApplication *app = g_application_get_default (); if (window) { /* As this behaves quite differently if a window is provided vs. not, * let's ensure any window passed is valid. */ g_assert (GTK_IS_WINDOW (window)); gtk_application_add_window (GTK_APPLICATION(app), window); } else { g_application_hold (g_application_get_default ()); } } void zenity_util_gapp_quit (GtkWindow *window, ZenityData *data) { static gboolean quit_requested = FALSE; if (quit_requested) { g_debug ("%s: Quit request already pending. Ignoring superfluous quit request.", __func__); return; } /* This is a bit hack-ish, but GApplication doesn't really allow for * customized exit statuses within that API. */ if (data->exit_code != 0) exit (data->exit_code); if (window) gtk_window_destroy (window); else g_application_release (g_application_get_default ()); quit_requested = TRUE; } int zenity_util_parse_dialog_response (const char *response) { if (g_strcmp0 (response, "ok") == 0 || g_strcmp0 (response, "yes") == 0) { return ZENITY_OK; } else if (g_strcmp0 (response, "cancel") == 0 || g_strcmp0 (response, "no") == 0) { return ZENITY_CANCEL; } else if (g_strcmp0 (response, "timeout") == 0) { return ZENITY_TIMEOUT; } else if (response[0] >= '0' && response[0] <= '9') { /* FIXME - atoi returns 0 on error, so this *could* mean the function * failed - but that would be a programmer error, so we'll leave this * in place. */ return atoi (response); } else { return ZENITY_ESC; } } GtkWidget * zenity_util_add_button (AdwMessageDialog *dialog, const char *button_text, ZenityExitCode response_id) { GtkWidget *w = GTK_WIDGET(dialog); g_autofree char *response_str = g_strdup_printf ("%d", response_id); adw_message_dialog_add_response (dialog, response_str, button_text); return w; }