/*================================================================== * util.c - Utility functions * * Swami * Copyright (C) 1999-2003 Josh Green * * 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA or point your web browser to http://www.gnu.org. * * To contact the author of this program: * Email: Josh Green * Swami homepage: http://swami.sourceforge.net *==================================================================*/ #include #include #include #include #include #include #include #include "GObjSup.h" #include "util.h" #include "pixmap.h" #include "i18n.h" #include "widgets/popdog.h" #if 0 /* time interval (in milliseconds) to check if log view should be popped */ #define LOG_POPUP_CHECK_INTERVAL 200 gboolean log_viewactive = FALSE; /* log window active? */ gint log_poplevel = LogBad; static gboolean log_popview = FALSE; /* log view popup scheduled? */ static GtkWidget *log_view_widg = NULL; /* currently active error view widg */ #endif /* unique dialog system data */ typedef struct { GtkWidget *dialog; gchar *strkey; int key2; } UniqueDialogKey; gboolean unique_dialog_inited = FALSE; GArray *unique_dialog_array; static void swamiui_util_cb_quick_popup_btn_clicked (GtkWidget *btn, gpointer userdata); static void swamiui_util_cb_waitfor_widget_destroyed (GtkWidget *widg, gpointer data); // static gboolean log_check_popup (gpointer data); // static void log_view_cb_destroy (void); static void dataset_foreach_data_elem (GQuark key_id, gpointer data, gpointer user_data); /* initialize various utility services (unique dialog, log view timer, etc) */ void swamiui_util_init (void) { unique_dialog_array = g_array_new (FALSE, FALSE, sizeof (UniqueDialogKey)); unique_dialog_inited = TRUE; // g_timeout_add (LOG_POPUP_CHECK_INTERVAL, (GSourceFunc)log_check_popup, NULL); } /* pops up a dialog for user input, for simple questions The arguments should be: msg, btn1label, btn1func (UtilQuickFunc *), btn1data, btn2label, btn2func... if btn1label = NULL then a single "OK" button is created, otherwise a button is created for each label that is provided with a callback to btnNfunc, if btnNfunc is NULL then gtk_widget_destroy is used */ GtkWidget * swamiui_util_quick_popup (gchar * msg, gchar * btn1, ...) { va_list args; GtkWidget *popdog; GtkWidget *lbl; GtkWidget *btn; gchar *s; UtilQuickFunc func; gpointer userdata; gint index = 1; popdog = gtk_popdog_new (NULL); gtk_window_set_modal (GTK_WINDOW (popdog), TRUE); lbl = gtk_label_new (msg); gtk_widget_show (lbl); gtk_box_pack_start (GTK_BOX (GTK_POPDOG (popdog)->vbox), lbl, FALSE, FALSE, 0); va_start (args, btn1); if (!btn1) { btn = gtk_button_new_with_label (_("OK")); gtk_signal_connect_object (GTK_OBJECT (btn), "clicked", (GtkSignalFunc) gtk_widget_destroy, GTK_OBJECT (popdog)); gtk_widget_show (btn); gtk_box_pack_start (GTK_BOX (GTK_POPDOG (popdog)->action_area), btn, FALSE, FALSE, 0); } else { s = btn1; do { btn = gtk_button_new_with_label (s); func = va_arg (args, UtilQuickFunc); userdata = va_arg (args, gpointer); if (!func) /* use destroy (popdog) as callback if !func */ { func = (UtilQuickFunc)gtk_widget_destroy; userdata = popdog; } gtk_object_set_data (GTK_OBJECT (btn), "parent", popdog); gtk_object_set_data (GTK_OBJECT (btn), "func", func); gtk_object_set_data (GTK_OBJECT (btn), "index", GINT_TO_POINTER (index++)); gtk_signal_connect (GTK_OBJECT (btn), "clicked", swamiui_util_cb_quick_popup_btn_clicked, userdata); gtk_widget_show (btn); gtk_box_pack_start (GTK_BOX (GTK_POPDOG (popdog)->action_area), btn, FALSE, FALSE, 0); } while ((s = va_arg (args, gchar *))); } va_end (args); gtk_widget_show (popdog); return (popdog); } static void swamiui_util_cb_quick_popup_btn_clicked (GtkWidget *btn, gpointer userdata) { GtkWidget *parent; UtilQuickFunc func; gint index; index = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (btn), "index")); swamiui_util_widget_action (btn, GINT_TO_POINTER (index)); func = gtk_object_get_data (GTK_OBJECT (btn), "func"); parent = gtk_object_get_data (GTK_OBJECT (btn), "parent"); (*func) (userdata, parent); } /* Unique dialog system is for allowing unique non-modal dialogs for resources identified by a string key and an optional additional integer key, attempting to open up a second dialog for the same resource will cause the first dialog to be brought to the front of view and no additional dialog will be created */ /* looks up a unique dialog widget by its keys, returns the widget or NULL */ GtkWidget * swamiui_util_lookup_unique_dialog (gchar *strkey, gint key2) { UniqueDialogKey *udkeyp; gint i; for (i = unique_dialog_array->len - 1; i >= 0; i--) { udkeyp = &g_array_index (unique_dialog_array, UniqueDialogKey, i); if ((udkeyp->strkey == strkey || strcmp (udkeyp->strkey, strkey) == 0) && udkeyp->key2 == key2) return (udkeyp->dialog); } return (NULL); } /* register a unique dialog, if a dialog already exists with the same keys, then activate the existing dialog and return FALSE, otherwise register the new dialog and return TRUE */ gboolean swamiui_util_register_unique_dialog (GtkWidget *dialog, gchar *strkey, gint key2) { UniqueDialogKey udkey; GtkWidget *widg; if ((widg = swamiui_util_lookup_unique_dialog (strkey, key2))) { gtk_widget_activate (widg); return (FALSE); } /* tell window manager to remember dialog state (position etc) */ gtk_window_set_wmclass (GTK_WINDOW (dialog), strkey, "Swami"); udkey.dialog = dialog; udkey.strkey = strkey; udkey.key2 = key2; g_array_append_val (unique_dialog_array, udkey); gtk_signal_connect (GTK_OBJECT (dialog), "destroy", (GtkSignalFunc)swamiui_util_unregister_unique_dialog, NULL); return (TRUE); } void swamiui_util_unregister_unique_dialog (GtkWidget *dialog) { UniqueDialogKey *udkeyp; gint i; for (i = unique_dialog_array->len - 1; i >= 0; i--) { udkeyp = &g_array_index (unique_dialog_array, UniqueDialogKey, i); if (udkeyp->dialog == dialog) break; } if (i >= 0) g_array_remove_index (unique_dialog_array, i); } /* activate (or raise) a unique dialog into view */ gboolean swamiui_util_activate_unique_dialog (gchar *strkey, gint key2) { GtkWidget *dialog; if ((dialog = swamiui_util_lookup_unique_dialog (strkey, key2))) { gdk_window_raise (GTK_WIDGET (dialog)->window); return (TRUE); } return (FALSE); } /* run gtk_main loop until the GtkObject data property "action" is != NULL, mates nicely with swamiui_util_quick_popup, returns value of "action". Useful for complex routines that require a lot of user dialog interaction. Rather than having to save state info and exit and return to a routine, a call to this routine can be made which will wait for the user's choice and return the index of the button (or other user specified value), -1 if the widget was destroyed or -2 if gtk_main_quit was called */ gpointer swamiui_util_waitfor_widget_action (GtkWidget *widg) { GQuark quark; gpointer val = NULL; gboolean destroyed = FALSE; guint sigid; /* initialize the action variable to NULL */ quark = gtk_object_data_force_id ("action"); gtk_object_set_data_by_id (GTK_OBJECT (widg), quark, NULL); /* already passing one variable to destroy signal handler, so bind this one as a GtkObject data item, will notify us if widget was destroyed */ gtk_object_set_data (GTK_OBJECT (widg), "_destroyed", &destroyed); /* val is set to "action" by swamiui_util_cb_waitfor_widget_destroyed if the widget we are waiting for gets killed */ sigid = gtk_signal_connect (GTK_OBJECT (widg), "destroy", GTK_SIGNAL_FUNC (swamiui_util_cb_waitfor_widget_destroyed), &val); do { if (gtk_main_iteration ()) /* run the gtk main loop, wait if no events */ val = GINT_TO_POINTER (-2); /* gtk_main_quit was called, return -2 */ else if (val == NULL) /* check the "action" data property */ val = gtk_object_get_data_by_id (GTK_OBJECT (widg), quark); } while (val == NULL); /* loop until "action" is set */ if (!destroyed) gtk_signal_disconnect (GTK_OBJECT (widg), sigid); return (val); } static void swamiui_util_cb_waitfor_widget_destroyed (GtkWidget *widg, gpointer data) { gpointer *val = data; gpointer action; gboolean *destroyed; action = gtk_object_get_data (GTK_OBJECT (widg), "action"); destroyed = gtk_object_get_data (GTK_OBJECT (widg), "_destroyed"); *destroyed = TRUE; if (action) *val = action; else *val = GINT_TO_POINTER (-1); } /* a callback for widgets (buttons, etc) within a "parent" widget used by swamiui_util_waitfor_widget_action, sets "action" to the specified "value" */ void swamiui_util_widget_action (GtkWidget *cbwidg, gpointer value) { GtkWidget *parent; parent = gtk_object_get_data (GTK_OBJECT (cbwidg), "parent"); gtk_object_set_data (GTK_OBJECT (parent), "action", value); } /** * A function to hack around glade's dialog based widgets * @widg Dialog widget to extract guts from */ GtkWidget * swamiui_util_rip_guts (GtkWidget *widg, char *glade_name) { GtkWidget *child; GList *children; children = gtk_container_children (GTK_CONTAINER (widg)); if (!children) return (NULL); /* remove objects data reference to itself */ gtk_object_set_data (GTK_OBJECT (widg), glade_name, NULL); /* get first born child :) */ child = (GtkWidget *)(children->data); g_list_free (children); /* duplicate the toplevel widgets data elements and place them in child */ g_datalist_foreach (>K_OBJECT (widg)->object_data, dataset_foreach_data_elem, child); /* glade object identifier (used in widget lookups) */ gtk_object_set_data (GTK_OBJECT (child), "__gladeobj", GINT_TO_POINTER (1)); /* un-parent child */ gtk_container_remove (GTK_CONTAINER (widg), child); return (child); } /** * Find a child of a Glade widget via any other child. * @widget A Glade widget ripped with swamiui_util_rip_guts or any child * there of. Will also work with regular Glade generated widgets. * @widget_name Name of child to get. * * Inspired by the Glade based lookup_widget but modified for ripped widgets. */ GtkWidget * swamiui_util_lookup_widget (GtkWidget *widget, const char *child_name) { GtkWidget *parent; /* locate glade widget by its dummy __gladeobj data element, or the toplevel widget */ while (widget) { if (gtk_object_get_data (GTK_OBJECT (widget), "__gladeobj")) break; if (GTK_IS_MENU (widget)) parent = gtk_menu_get_attach_widget (GTK_MENU (widget)); else parent = widget->parent; if (parent == NULL) break; widget = parent; } /* get the widget with the given name */ return (gtk_object_get_data (GTK_OBJECT (widget), child_name)); } /* a foreach function to copy all object data elements to new child object */ static void dataset_foreach_data_elem (GQuark key_id, gpointer data, gpointer user_data) { GtkWidget *child = (GtkWidget *)user_data; /* increment the referenced object's reference count for this data element if not the new child */ if (data && GTK_IS_WIDGET (data) && data != child) gtk_widget_ref (GTK_WIDGET (data)); /* copy the data element over to the new child */ gtk_object_set_data_by_id_full (GTK_OBJECT (child), key_id, data, (GtkDestroyNotify) gtk_widget_unref); } GtkWidget * swamiui_util_create_pixmap (gchar ** xpmdata) { GdkPixmap *pixmap; GdkBitmap *mask; pixmap_get (xpmdata, &pixmap, &mask); return (gtk_pixmap_new (pixmap, mask)); } int swamiui_util_option_menu_index (GtkWidget *opmenu) { GtkWidget *menu, *actv; menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (opmenu)); actv = gtk_menu_get_active (GTK_MENU (menu)); return (g_list_index (GTK_MENU_SHELL (menu)->children, actv)); } #if 0 /* a callback for a glib timeout that periodically checks if the log view should be popped by the GTK thread (see swamiui_util_init) */ static gboolean log_check_popup (gpointer data) { if (log_popview) { log_popview = FALSE; log_view (NULL); } return (TRUE); } /* log_view - Display log view window */ void log_view (gchar * title) { GtkWidget *popdog; GtkWidget *hbox; GtkWidget *msgarea; GtkAdjustment *adj; GtkWidget *vscrollbar; GtkWidget *btn; static GStaticMutex mutex = G_STATIC_MUTEX_INIT; /* need to lock test and set of log_viewactive */ g_static_mutex_lock (&mutex); if (log_viewactive) { g_static_mutex_unlock (&mutex); return; } log_viewactive = TRUE; g_static_mutex_unlock (&mutex); if (title) popdog = gtk_popdog_new (title); else popdog = gtk_popdog_new (_("Swami log")); hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (GTK_POPDOG (popdog)->vbox), hbox, TRUE, TRUE, 0); gtk_widget_show (hbox); msgarea = gtk_text_new (NULL, NULL); gtk_widget_set_usize (msgarea, 400, 100); gtk_text_set_editable (GTK_TEXT (msgarea), FALSE); gtk_text_set_word_wrap (GTK_TEXT (msgarea), FALSE); /* have to lock on log read, another thread might be writing to it */ g_mutex_lock (log_mutex); gtk_text_insert (GTK_TEXT (msgarea), NULL, NULL, NULL, log_buf->str, -1); g_mutex_unlock (log_mutex); gtk_box_pack_start (GTK_BOX (hbox), msgarea, TRUE, TRUE, 0); gtk_widget_show (msgarea); adj = GTK_TEXT (msgarea)->vadj; /* get the message area's vert adj */ vscrollbar = gtk_vscrollbar_new (adj); gtk_box_pack_start (GTK_BOX (hbox), vscrollbar, FALSE, FALSE, 0); gtk_widget_show (vscrollbar); btn = gtk_button_new_with_label (_("OK")); gtk_signal_connect_object (GTK_OBJECT (btn), "clicked", (GtkSignalFunc) gtk_widget_destroy, GTK_OBJECT (popdog)); gtk_box_pack_start (GTK_BOX (GTK_POPDOG (popdog)->action_area), btn, FALSE, FALSE, 0); gtk_widget_show (btn); gtk_signal_connect_object (GTK_OBJECT (popdog), "destroy", GTK_SIGNAL_FUNC (log_view_cb_destroy), NULL); gtk_widget_show (popdog); log_view_widg = NULL; } /* reset dialog active variables */ static void log_view_cb_destroy (void) { log_viewactive = FALSE; log_view_widg = NULL; } #endif /** * Convert all dos newlines ("\r\n") to unix newlines "\n" in a string * @str String to convert * Returns: New string with converted newlines, should be freed when done with */ char * swamiui_util_str_crlf2lf (char *str) { char *newstr, *s; newstr = g_new (char, strlen (str) + 1); s = newstr; while (*str != '\0') { if (*str != '\r' || *(str + 1) != '\n') *(s++) = *str; str++; } *s = '\0'; return (newstr); } /** * Convert all unix newlines "\n" to dos newlines ("\r\n") in a string * @str String to convert * Returns: New string with converted newlines, should be freed when done with */ char * swamiui_util_str_lf2crlf (char *str) { GString *gs; char *s; gs = g_string_sized_new (sizeof (str)); while (*str != '\0') { if (*str != '\n') gs = g_string_append_c (gs, *str); else gs = g_string_append (gs, "\r\n"); str++; } s = gs->str; g_string_free (gs, FALSE); /* character segment is not free'd */ return (s); } /** * Search for a sub string in a string * @sub Partial string to search for in str * @str String to search for sub string in * Returns: TRUE if "sub" is found in "str", FALSE otherwise */ int swamiui_util_substrcmp (char *sub, char *str) { char *s, *s2; if (!*sub) return (TRUE); /* null string, matches */ while (*str) { if (tolower (*str) == tolower (*sub)) { s = sub + 1; s2 = str + 1; while (*s && *s2) { if (tolower (*s) != tolower (*s2)) break; s++; s2++; } if (!*s) return (TRUE); } str++; } return (FALSE); }