1 /*
2  * Copyright (C) 2001 Ximian, Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Authors:
19  *   Chema Celorio <chema@celorio.com>
20  */
21 
22 #include <config.h>
23 
24 /**
25  * SECTION:glade-utils
26  * @Title: Glade Utils
27  * @Short_Description: Welcome to the zoo.
28  *
29  * This is where all of that really usefull miscalanious stuff lands up.
30  */
31 
32 #include "glade.h"
33 #include "glade-project.h"
34 #include "glade-command.h"
35 #include "glade-debug.h"
36 #include "glade-placeholder.h"
37 #include "glade-widget.h"
38 #include "glade-widget-adaptor.h"
39 #include "glade-property.h"
40 #include "glade-property-class.h"
41 #include "glade-clipboard.h"
42 #include "glade-private.h"
43 
44 #include <string.h>
45 #include <gdk/gdkkeysyms.h>
46 #include <gmodule.h>
47 #include <glib/gi18n-lib.h>
48 #include <glib/gstdio.h>
49 #include <errno.h>
50 
51 #ifdef G_OS_WIN32
52 #define WIN32_LEAN_AND_MEAN
53 #include <windows.h>
54 #include <shellapi.h>
55 #endif
56 
57 #define GLADE_UTIL_COPY_BUFFSIZE       1024
58 
59 
60 /**
61  * glade_util_compose_get_type_func:
62  * @name:
63  *
64  * TODO: write me
65  *
66  * Returns:
67  */
68 static gchar *
glade_util_compose_get_type_func(const gchar * name)69 glade_util_compose_get_type_func (const gchar *name)
70 {
71   gchar *retval;
72   GString *tmp;
73   gint i = 1, j;
74 
75   tmp = g_string_new (name);
76 
77   while (tmp->str[i])
78     {
79       if (g_ascii_isupper (tmp->str[i]))
80         {
81           tmp = g_string_insert_c (tmp, i++, '_');
82 
83           j = 0;
84           while (g_ascii_isupper (tmp->str[i++]))
85             j++;
86 
87           if (j > 2)
88             g_string_insert_c (tmp, i - 2, '_');
89 
90           continue;
91         }
92       i++;
93     }
94 
95   tmp = g_string_append (tmp, "_get_type");
96   retval = g_ascii_strdown (tmp->str, tmp->len);
97   g_string_free (tmp, TRUE);
98 
99   return retval;
100 }
101 
102 /**
103  * glade_util_get_type_from_name:
104  * @name: the name of the #GType - like 'GtkWidget' or a "get-type" function.
105  * @have_func: function-name flag -- true if the name is a "get-type" function.
106  *
107  * Returns the type using the "get type" function name based on @name.
108  * If the @have_func flag is true,@name is used directly, otherwise the get-type
109  * function is contrived from @name then used.
110  *
111  * Returns: the new #GType
112  */
113 GType
glade_util_get_type_from_name(const gchar * name,gboolean have_func)114 glade_util_get_type_from_name (const gchar *name, gboolean have_func)
115 {
116   static GModule *allsymbols = NULL;
117   GType (*get_type) (void);
118   GType type = 0;
119   gchar *func_name = (gchar *) name;
120 
121   if ((type = g_type_from_name (name)) == 0 &&
122       (have_func ||
123        (func_name = glade_util_compose_get_type_func (name)) != NULL))
124     {
125 
126       if (!allsymbols)
127         allsymbols = g_module_open (NULL, 0);
128 
129       if (g_module_symbol (allsymbols, func_name, (gpointer) & get_type))
130         {
131           g_assert (get_type);
132           type = get_type ();
133         }
134       else
135         {
136           g_warning (_("We could not find the symbol \"%s\""), func_name);
137         }
138 
139       if (!have_func)
140 	g_free (func_name);
141     }
142 
143   if (type == 0)
144     g_warning (_("Could not get the type from \"%s\""), name);
145 
146   return type;
147 }
148 
149 /**
150  * glade_utils_get_pspec_from_funcname:
151  * @funcname: the symbol name of a function to generate a #GParamSpec
152  *
153  * Returns: A #GParamSpec created by the delagate function
154  *          specified by @funcname
155  */
156 GParamSpec *
glade_utils_get_pspec_from_funcname(const gchar * funcname)157 glade_utils_get_pspec_from_funcname (const gchar *funcname)
158 {
159   static GModule *allsymbols = NULL;
160   GParamSpec *pspec = NULL;
161   GParamSpec *(*get_pspec) (void) = NULL;
162 
163   if (!allsymbols)
164     allsymbols = g_module_open (NULL, 0);
165 
166   if (!g_module_symbol (allsymbols, funcname, (gpointer) & get_pspec))
167     {
168       g_warning (_("We could not find the symbol \"%s\""), funcname);
169       return NULL;
170     }
171 
172   g_assert (get_pspec);
173   pspec = get_pspec ();
174 
175   return pspec;
176 }
177 
178 void
_glade_util_dialog_set_hig(GtkDialog * dialog)179 _glade_util_dialog_set_hig (GtkDialog *dialog)
180 {
181   GtkWidget *vbox, *action_area;
182 
183   /* HIG spacings */
184   vbox = gtk_dialog_get_content_area (dialog);
185   gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
186   gtk_box_set_spacing (GTK_BOX (vbox), 2); /* 2 * 5 + 2 = 12 */
187 
188   action_area = gtk_dialog_get_action_area (dialog);
189   gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
190   gtk_box_set_spacing (GTK_BOX (action_area), 6);
191 }
192 
193 /**
194  * glade_util_ui_message:
195  * @parent: a #GtkWindow cast as a #GtkWidget
196  * @type:   a #GladeUIMessageType
197  * @widget: a #GtkWidget to append to the dialog vbox
198  * @format: a printf style format string
199  * @...:    args for the format.
200  *
201  * Creates a new warning dialog window as a child of @parent containing
202  * the text of @format, runs it, then destroys it on close. Depending
203  * on @type, a cancel button may apear or the icon may change.
204  *
205  * Returns: True if the @type was GLADE_UI_ARE_YOU_SURE and the user
206  *          selected "OK", True if the @type was GLADE_UI_YES_OR_NO and
207  *          the user selected "YES"; False otherwise.
208  */
209 gint
glade_util_ui_message(GtkWidget * parent,GladeUIMessageType type,GtkWidget * widget,const gchar * format,...)210 glade_util_ui_message (GtkWidget *parent,
211                        GladeUIMessageType type,
212                        GtkWidget *widget,
213                        const gchar *format,
214                        ...)
215 {
216   GtkWidget *dialog;
217   GtkMessageType message_type = GTK_MESSAGE_INFO;
218   GtkButtonsType buttons_type = GTK_BUTTONS_OK;
219   va_list args;
220   gchar *string;
221   gint response;
222 
223   va_start (args, format);
224   string = g_strdup_vprintf (format, args);
225   va_end (args);
226 
227   /* Get message_type */
228   switch (type)
229     {
230       case GLADE_UI_INFO:
231         message_type = GTK_MESSAGE_INFO;
232         break;
233       case GLADE_UI_WARN:
234       case GLADE_UI_ARE_YOU_SURE:
235         message_type = GTK_MESSAGE_WARNING;
236         break;
237       case GLADE_UI_ERROR:
238         message_type = GTK_MESSAGE_ERROR;
239         break;
240       case GLADE_UI_YES_OR_NO:
241         message_type = GTK_MESSAGE_QUESTION;
242         break;
243         break;
244       default:
245         g_critical ("Bad arg for glade_util_ui_message");
246         break;
247     }
248 
249 
250   /* Get buttons_type */
251   switch (type)
252     {
253       case GLADE_UI_INFO:
254       case GLADE_UI_WARN:
255       case GLADE_UI_ERROR:
256         buttons_type = GTK_BUTTONS_OK;
257         break;
258       case GLADE_UI_ARE_YOU_SURE:
259         buttons_type = GTK_BUTTONS_OK_CANCEL;
260         break;
261       case GLADE_UI_YES_OR_NO:
262         buttons_type = GTK_BUTTONS_YES_NO;
263         break;
264         break;
265       default:
266         g_critical ("Bad arg for glade_util_ui_message");
267         break;
268     }
269 
270   dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
271                                    GTK_DIALOG_DESTROY_WITH_PARENT,
272                                    message_type, buttons_type, NULL);
273 
274   gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), string);
275 
276   if (widget)
277     {
278       gtk_box_pack_end (GTK_BOX
279                         (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
280                         widget, TRUE, TRUE, 2);
281       gtk_widget_show (widget);
282 
283       /* If theres additional content, make it resizable */
284       gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
285     }
286 
287   response = gtk_dialog_run (GTK_DIALOG (dialog));
288 
289   gtk_widget_destroy (dialog);
290   g_free (string);
291 
292   return (response == GTK_RESPONSE_OK || response == GTK_RESPONSE_YES);
293 }
294 
295 
296 gboolean
glade_util_check_and_warn_scrollable(GladeWidget * parent,GladeWidgetAdaptor * child_adaptor,GtkWidget * parent_widget)297 glade_util_check_and_warn_scrollable (GladeWidget *parent,
298                                       GladeWidgetAdaptor *child_adaptor,
299                                       GtkWidget *parent_widget)
300 {
301   if (GTK_IS_SCROLLED_WINDOW (glade_widget_get_object (parent)) &&
302       GWA_SCROLLABLE_WIDGET (child_adaptor) == FALSE)
303     {
304       GladeWidgetAdaptor *vadaptor =
305           glade_widget_adaptor_get_by_type (GTK_TYPE_VIEWPORT);
306       GladeWidgetAdaptor *parent_adaptor = glade_widget_get_adaptor (parent);
307 
308       glade_util_ui_message (parent_widget,
309                              GLADE_UI_INFO, NULL,
310                              _("Cannot add non scrollable %s widget to a %s directly.\n"
311 			       "Add a %s first."),
312 			     glade_widget_adaptor_get_title (child_adaptor),
313                              glade_widget_adaptor_get_title (parent_adaptor),
314 			     glade_widget_adaptor_get_title (vadaptor));
315       return TRUE;
316     }
317   return FALSE;
318 }
319 
320 typedef struct
321 {
322   GtkStatusbar *statusbar;
323   guint context_id;
324   guint message_id;
325 } FlashInfo;
326 
327 static const guint flash_length = 3;
328 
329 static gboolean
remove_message_timeout(FlashInfo * fi)330 remove_message_timeout (FlashInfo *fi)
331 {
332   gtk_statusbar_remove (fi->statusbar, fi->context_id, fi->message_id);
333   g_slice_free (FlashInfo, fi);
334 
335   /* remove the timeout */
336   return FALSE;
337 }
338 
339 /**
340  * glade_utils_flash_message:
341  * @statusbar: The statusbar
342  * @context_id: The message context_id
343  * @format: The message to flash on the statusbar
344  *
345  * Flash a temporary message on the statusbar.
346  */
347 void
glade_util_flash_message(GtkWidget * statusbar,guint context_id,gchar * format,...)348 glade_util_flash_message (GtkWidget *statusbar,
349                           guint context_id,
350                           gchar *format,
351                           ...)
352 {
353   va_list args;
354   FlashInfo *fi;
355   gchar *message;
356 
357   g_return_if_fail (GTK_IS_STATUSBAR (statusbar));
358   g_return_if_fail (format != NULL);
359 
360   va_start (args, format);
361   message = g_strdup_vprintf (format, args);
362   va_end (args);
363 
364   fi = g_slice_new0 (FlashInfo);
365   fi->statusbar = GTK_STATUSBAR (statusbar);
366   fi->context_id = context_id;
367   fi->message_id = gtk_statusbar_push (fi->statusbar, fi->context_id, message);
368 
369   g_timeout_add_seconds (flash_length, (GSourceFunc) remove_message_timeout,
370                          fi);
371 
372   g_free (message);
373 }
374 
375 static gint
glade_util_compare_uline_labels(const gchar * labela,const gchar * labelb)376 glade_util_compare_uline_labels (const gchar *labela, const gchar *labelb)
377 {
378   for (;;)
379     {
380       gunichar c1, c2;
381 
382       if (*labela == '\0')
383         return (*labelb == '\0') ? 0 : -1;
384       if (*labelb == '\0')
385         return 1;
386 
387       c1 = g_utf8_get_char (labela);
388       if (c1 == '_')
389         {
390           labela = g_utf8_next_char (labela);
391           c1 = g_utf8_get_char (labela);
392         }
393 
394       c2 = g_utf8_get_char (labelb);
395       if (c2 == '_')
396         {
397           labelb = g_utf8_next_char (labelb);
398           c2 = g_utf8_get_char (labelb);
399         }
400 
401       if (c1 < c2)
402         return -1;
403       if (c1 > c2)
404         return 1;
405 
406       labela = g_utf8_next_char (labela);
407       labelb = g_utf8_next_char (labelb);
408     }
409 
410   /* Shouldn't be reached. */
411   return 0;
412 }
413 
414 /**
415  * glade_util_compare_stock_labels:
416  * @a: a #gconstpointer to a #GtkStockItem
417  * @b: a #gconstpointer to a #GtkStockItem
418  *
419  * This is a #GCompareFunc that compares the labels of two stock items,
420  * ignoring any '_' characters. It isn't particularly efficient.
421  *
422  * Returns: negative value if @a < @b; zero if @a = @b;
423  *          positive value if @a > @b
424  */
425 gint
glade_util_compare_stock_labels(gconstpointer a,gconstpointer b)426 glade_util_compare_stock_labels (gconstpointer a, gconstpointer b)
427 {
428   const gchar *stock_ida = a, *stock_idb = b;
429   GtkStockItem itema, itemb;
430   gboolean founda, foundb;
431   gint retval;
432 
433 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
434   founda = gtk_stock_lookup (stock_ida, &itema);
435   foundb = gtk_stock_lookup (stock_idb, &itemb);
436 G_GNUC_END_IGNORE_DEPRECATIONS
437 
438   if (founda)
439     {
440       if (!foundb)
441         retval = -1;
442       else
443         /* FIXME: Not ideal for UTF-8. */
444         retval = glade_util_compare_uline_labels (itema.label, itemb.label);
445     }
446   else
447     {
448       if (!foundb)
449         retval = 0;
450       else
451         retval = 1;
452     }
453 
454   return retval;
455 }
456 
457 /**
458  * glade_util_file_dialog_new:
459  * @title: dialog title
460  * @project: a #GladeProject used when saving
461  * @parent: a parent #GtkWindow for the dialog
462  * @action: a #GladeUtilFileDialogType to say if the dialog will open or save
463  *
464  * Returns: a "glade file" file chooser dialog. The caller is responsible
465  *          for showing the dialog
466  */
467 GtkWidget *
glade_util_file_dialog_new(const gchar * title,GladeProject * project,GtkWindow * parent,GladeUtilFileDialogType action)468 glade_util_file_dialog_new (const gchar *title,
469                             GladeProject *project,
470                             GtkWindow *parent,
471                             GladeUtilFileDialogType action)
472 {
473   GtkWidget *file_dialog;
474   GtkFileFilter *file_filter;
475 
476   g_return_val_if_fail ((action == GLADE_FILE_DIALOG_ACTION_OPEN ||
477                          action == GLADE_FILE_DIALOG_ACTION_SAVE), NULL);
478 
479   g_return_val_if_fail ((action != GLADE_FILE_DIALOG_ACTION_SAVE ||
480                          GLADE_IS_PROJECT (project)), NULL);
481 
482   file_dialog = gtk_file_chooser_dialog_new (title, parent, action,
483                                              _("_Cancel"), GTK_RESPONSE_CANCEL,
484                                              action ==
485                                              GLADE_FILE_DIALOG_ACTION_OPEN ?
486                                              _("_Open") : _("_Save"),
487                                              GTK_RESPONSE_OK, NULL);
488 
489   file_filter = gtk_file_filter_new ();
490   gtk_file_filter_add_pattern (file_filter, "*");
491   gtk_file_filter_set_name (file_filter, _("All Files"));
492   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_dialog), file_filter);
493 
494   file_filter = gtk_file_filter_new ();
495   gtk_file_filter_add_pattern (file_filter, "*.glade");
496   gtk_file_filter_set_name (file_filter, _("Libglade Files"));
497   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_dialog), file_filter);
498 
499   file_filter = gtk_file_filter_new ();
500   gtk_file_filter_add_pattern (file_filter, "*.ui");
501   gtk_file_filter_set_name (file_filter, _("GtkBuilder Files"));
502   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_dialog), file_filter);
503 
504   file_filter = gtk_file_filter_new ();
505   gtk_file_filter_add_pattern (file_filter, "*.ui");
506   gtk_file_filter_add_pattern (file_filter, "*.glade");
507   gtk_file_filter_set_name (file_filter, _("All Glade Files"));
508   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_dialog), file_filter);
509 
510   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (file_dialog), file_filter);
511 
512   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER
513                                                   (file_dialog), TRUE);
514   gtk_dialog_set_default_response (GTK_DIALOG (file_dialog), GTK_RESPONSE_OK);
515 
516   return file_dialog;
517 }
518 
519 /**
520  * glade_util_replace:
521  * @str: a string
522  * @a: a #gchar
523  * @b: a #gchar
524  *
525  * Replaces each occurance of the character @a in @str to @b.
526  */
527 void
glade_util_replace(gchar * str,gchar a,gchar b)528 glade_util_replace (gchar *str, gchar a, gchar b)
529 {
530   g_return_if_fail (str != NULL);
531 
532   while (*str != 0)
533     {
534       if (*str == a)
535         *str = b;
536 
537       str = g_utf8_next_char (str);
538     }
539 }
540 
541 /**
542  * _glade_util_strreplace:
543  * @str: a string
544  * @free_str: wheter to free str or not
545  * @key: the key string to search for
546  * @replacement: string to replace key
547  *
548  * Replaces each occurance of the string @key in @str to @replacement.
549  */
550 gchar *
_glade_util_strreplace(gchar * str,gboolean free_str,const gchar * key,const gchar * replacement)551 _glade_util_strreplace (gchar *str,
552                         gboolean free_str,
553                         const gchar *key,
554                         const gchar *replacement)
555 {
556   gchar *retval, **array;
557 
558   if ((array = g_strsplit (str, key, -1)) && array[0])
559     retval = g_strjoinv (replacement, array);
560   else
561     retval = g_strdup (str);
562 
563   g_strfreev (array);
564 
565   if (free_str)
566     g_free (str);
567 
568   return retval;
569 }
570 
571 /**
572  * glade_util_read_prop_name:
573  * @str: a string
574  *
575  * Return a usable version of a property identifier as found
576  * in a freshly parserd #GladeInterface
577  */
578 gchar *
glade_util_read_prop_name(const gchar * str)579 glade_util_read_prop_name (const gchar *str)
580 {
581   gchar *id;
582 
583   g_return_val_if_fail (str != NULL, NULL);
584 
585   id = g_strdup (str);
586 
587   glade_util_replace (id, '_', '-');
588 
589   return id;
590 }
591 
592 
593 /**
594  * glade_util_duplicate_underscores:
595  * @name: a string
596  *
597  * Duplicates @name, but the copy has two underscores in place of any single
598  * underscore in the original.
599  *
600  * Returns: a newly allocated string
601  */
602 gchar *
glade_util_duplicate_underscores(const gchar * name)603 glade_util_duplicate_underscores (const gchar *name)
604 {
605   const gchar *tmp;
606   const gchar *last_tmp = name;
607   gchar *underscored_name = g_malloc (strlen (name) * 2 + 1);
608   gchar *tmp_underscored = underscored_name;
609 
610   for (tmp = last_tmp; *tmp; tmp = g_utf8_next_char (tmp))
611     {
612       if (*tmp == '_')
613         {
614           memcpy (tmp_underscored, last_tmp, tmp - last_tmp + 1);
615           tmp_underscored += tmp - last_tmp + 1;
616           last_tmp = tmp + 1;
617           *tmp_underscored++ = '_';
618         }
619     }
620 
621   memcpy (tmp_underscored, last_tmp, tmp - last_tmp + 1);
622 
623   return underscored_name;
624 }
625 
626 /*
627  * taken from gtk... maybe someday we can convince them to
628  * expose gtk_container_get_all_children
629  */
630 static void
gtk_container_children_callback(GtkWidget * widget,gpointer client_data)631 gtk_container_children_callback (GtkWidget *widget, gpointer client_data)
632 {
633   GList **children;
634 
635   children = (GList **) client_data;
636   if (!g_list_find (*children, widget))
637     *children = g_list_prepend (*children, widget);
638 }
639 
640 /**
641  * glade_util_container_get_all_children:
642  * @container: a #GtkContainer
643  *
644  * Use this to itterate over all children in a GtkContainer,
645  * as it used _forall() instead of _foreach() (and the GTK+ version
646  * of this function is simply not exposed).
647  *
648  * Note that glade_widget_class_get_children() is the high-level
649  * abstraction and will usually end up calling this function.
650  *
651  * Returns: a #GList giving the contents of @container
652  */
653 GList *
glade_util_container_get_all_children(GtkContainer * container)654 glade_util_container_get_all_children (GtkContainer *container)
655 {
656   GList *children = NULL;
657 
658   g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
659 
660   gtk_container_forall (container, gtk_container_children_callback, &children);
661   gtk_container_foreach (container, gtk_container_children_callback, &children);
662 
663   /* Preserve the natural order by reversing the list */
664   return g_list_reverse (children);
665 }
666 
667 /**
668  * glade_util_count_placeholders:
669  * @parent: a #GladeWidget
670  *
671  * Returns: the amount of #GladePlaceholders parented by @parent
672  */
673 gint
glade_util_count_placeholders(GladeWidget * parent)674 glade_util_count_placeholders (GladeWidget *parent)
675 {
676   gint placeholders = 0;
677   GList *list, *children;
678 
679   /* count placeholders */
680   if ((children =
681        glade_widget_adaptor_get_children (glade_widget_get_adaptor (parent),
682 					  glade_widget_get_object (parent))) != NULL)
683     {
684       for (list = children; list && list->data; list = list->next)
685         {
686           if (GLADE_IS_PLACEHOLDER (list->data))
687             placeholders++;
688         }
689       g_list_free (children);
690     }
691 
692   return placeholders;
693 }
694 
695 static GtkTreeIter *
glade_util_find_iter(GtkTreeModel * model,GtkTreeIter * iter,GladeWidget * findme,gint column)696 glade_util_find_iter (GtkTreeModel *model,
697                       GtkTreeIter *iter,
698                       GladeWidget *findme,
699                       gint column)
700 {
701   GtkTreeIter *retval = NULL;
702   GObject *object = NULL;
703   GtkTreeIter *next;
704 
705   g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
706   g_return_val_if_fail (iter != NULL, NULL);
707 
708   next = gtk_tree_iter_copy (iter);
709   g_return_val_if_fail (next != NULL, NULL);
710 
711   while (retval == NULL)
712     {
713       GladeWidget *widget;
714 
715       gtk_tree_model_get (model, next, column, &object, -1);
716       if (object &&
717           gtk_tree_model_get_column_type (model, column) == G_TYPE_OBJECT)
718         g_object_unref (object);
719 
720       widget = glade_widget_get_from_gobject (object);
721 
722       if (widget == findme)
723         {
724           retval = gtk_tree_iter_copy (next);
725           break;
726         }
727       else if (glade_widget_is_ancestor (findme, widget))
728         {
729           if (gtk_tree_model_iter_has_child (model, next))
730             {
731               GtkTreeIter child;
732               gtk_tree_model_iter_children (model, &child, next);
733               if ((retval = glade_util_find_iter
734                    (model, &child, findme, column)) != NULL)
735                 break;
736             }
737 
738           /* Only search the branches where the searched widget
739            * is actually a child of the this row, optimize the
740            * searching this way
741            */
742           break;
743         }
744 
745       if (!gtk_tree_model_iter_next (model, next))
746         break;
747     }
748   gtk_tree_iter_free (next);
749 
750   return retval;
751 }
752 
753 /**
754  * glade_util_find_iter_by_widget:
755  * @model: a #GtkTreeModel
756  * @findme: a #GladeWidget
757  * @column: a #gint
758  *
759  * Looks through @model for the #GtkTreeIter corresponding to
760  * @findme under @column.
761  *
762  * Returns: a newly allocated #GtkTreeIter from @model corresponding
763  * to @findme which should be freed with gtk_tree_iter_free()
764  *
765  */
766 GtkTreeIter *
glade_util_find_iter_by_widget(GtkTreeModel * model,GladeWidget * findme,gint column)767 glade_util_find_iter_by_widget (GtkTreeModel *model,
768                                 GladeWidget *findme,
769                                 gint column)
770 {
771   GtkTreeIter iter;
772   if (gtk_tree_model_get_iter_first (model, &iter))
773     {
774       return glade_util_find_iter (model, &iter, findme, column);
775     }
776   return NULL;
777 }
778 
779 /**
780  * glade_util_purify_list:
781  * @list: A #GList
782  *
783  * Returns: A newly allocated version of @list with no
784  *          duplicate data entries
785  */
786 GList *
glade_util_purify_list(GList * list)787 glade_util_purify_list (GList * list)
788 {
789   GList *l, *newlist = NULL;
790 
791   for (l = list; l; l = l->next)
792     if (!g_list_find (newlist, l->data))
793       newlist = g_list_prepend (newlist, l->data);
794 
795   g_list_free (list);
796 
797   return g_list_reverse (newlist);
798 }
799 
800 /**
801  * glade_util_added_in_list:
802  * @old_list: the old #GList
803  * @new_list: the new #GList
804  *
805  * Returns: A newly allocated #GList of elements that
806  *          are in @new but not in @old
807  *
808  */
809 GList *
glade_util_added_in_list(GList * old_list,GList * new_list)810 glade_util_added_in_list (GList *old_list, GList *new_list)
811 {
812   GList *added = NULL, *list;
813 
814   for (list = new_list; list; list = list->next)
815     {
816       if (!g_list_find (old_list, list->data))
817         added = g_list_prepend (added, list->data);
818     }
819 
820   return g_list_reverse (added);
821 }
822 
823 /**
824  * glade_util_removed_from_list:
825  * @old_list: the old #GList
826  * @new_list: the new #GList
827  *
828  * Returns: A newly allocated #GList of elements that
829  *          are in @old no longer in @new
830  *
831  */
832 GList *
glade_util_removed_from_list(GList * old_list,GList * new_list)833 glade_util_removed_from_list (GList *old_list, GList *new_list)
834 {
835   GList *added = NULL, *list;
836 
837   for (list = old_list; list; list = list->next)
838     {
839       if (!g_list_find (new_list, list->data))
840         added = g_list_prepend (added, list->data);
841     }
842 
843   return g_list_reverse (added);
844 }
845 
846 
847 /**
848  * glade_util_canonical_path:
849  * @path: any path that may contain ".." or "." components
850  *
851  * Returns: an absolute path to the specified file or directory
852  *          that contains no ".." or "." components (this does
853  *          not call readlink like realpath() does).
854  *
855  * Note: on some systems; I think its possible that we dont have
856  *       permission to execute in the directory in which the glade
857  *       file resides; I decided finally to do it this way anyway
858  *       since libc's realpath() does exactly the same.
859  */
860 gchar *
glade_util_canonical_path(const gchar * path)861 glade_util_canonical_path (const gchar *path)
862 {
863   gchar *orig_dir, *dirname, *basename, *direct_dir, *direct_name = NULL;
864 
865   g_return_val_if_fail (path != NULL, NULL);
866 
867   basename = g_path_get_basename (path);
868 
869   if ((orig_dir = g_get_current_dir ()) != NULL)
870     {
871       if ((dirname = g_path_get_dirname (path)) != NULL)
872         {
873           if (g_chdir (dirname) == 0)
874             {
875               if ((direct_dir = g_get_current_dir ()) != NULL)
876 		{
877 		  direct_name = g_build_filename (direct_dir, basename, NULL);
878 		  g_free (direct_dir);
879 		}
880               else
881                 g_warning ("g_path");
882 
883               if (g_chdir (orig_dir) != 0)
884                 g_warning ("Unable to chdir back to %s directory (%s)",
885                            orig_dir, g_strerror (errno));
886 
887             }
888           else
889             g_warning ("Unable to chdir to %s directory (%s)",
890                        dirname, g_strerror (errno));
891 
892           g_free (dirname);
893         }
894       else
895         g_warning ("Unable to get directory component of %s\n", path);
896       g_free (orig_dir);
897     }
898 
899   if (basename)
900     g_free (basename);
901 
902   return direct_name;
903 }
904 
905 static GModule *
try_load_library(const gchar * library_path,const gchar * library_name)906 try_load_library (const gchar *library_path, const gchar *library_name)
907 {
908   gchar *path = g_module_build_path (library_path, library_name);
909   GModule *module = NULL;
910 
911   if (!library_path || g_file_test (path, G_FILE_TEST_EXISTS))
912     {
913       if (!(module = g_module_open (path, G_MODULE_BIND_LAZY)))
914         g_warning ("Failed to load %s: %s", path, g_module_error ());
915     }
916 
917   g_free (path);
918 
919   return module;
920 }
921 
922 /**
923  * glade_util_load_library:
924  * @library_name: name of the library
925  *
926  * Loads the named library from the Glade modules and lib directory or failing that
927  * from the standard platform specific directories. (Including /usr/local/lib for unices)
928  *
929  * The @library_name should not include any platform specifix prefix or suffix,
930  * those are automatically added, if needed, by g_module_build_path()
931  *
932  * Returns: a #GModule on success, or %NULL on failure.
933  */
934 GModule *
glade_util_load_library(const gchar * library_name)935 glade_util_load_library (const gchar *library_name)
936 {
937   GModule *module = NULL;
938   const gchar *search_path;
939   gint i;
940 
941   if ((search_path = g_getenv (GLADE_ENV_MODULE_PATH)) != NULL)
942     {
943       gchar **split;
944 
945       if ((split = g_strsplit (search_path, ":", 0)) != NULL)
946         {
947           for (i = 0; split[i] != NULL; i++)
948             if ((module = try_load_library (split[i], library_name)) != NULL)
949               break;
950 
951           g_strfreev (split);
952         }
953     }
954 
955   if (g_getenv (GLADE_ENV_TESTING) == NULL && !module)
956     {
957       const gchar *paths[] = { glade_app_get_modules_dir (),
958                                glade_app_get_lib_dir (),
959 #ifndef G_OS_WIN32 /* Try local lib dir on Unices */
960                                "/usr/local/lib",
961 #endif
962                                NULL}; /* Use default system paths */
963 
964 
965       for (i = 0; i < G_N_ELEMENTS (paths); i++)
966         if ((module = try_load_library (paths[i], library_name)) != NULL)
967           break;
968     }
969 
970   return module;
971 }
972 
973 /**
974  * glade_util_file_is_writeable:
975  * @path:  the path to the file
976  *
977  * Checks whether the file at @path is writeable
978  *
979  * Returns: TRUE if file is writeable
980  */
981 gboolean
glade_util_file_is_writeable(const gchar * path)982 glade_util_file_is_writeable (const gchar *path)
983 {
984   GIOChannel *channel;
985   g_return_val_if_fail (path != NULL, FALSE);
986 
987   /* The only way to really know if the file is writable */
988   if ((channel = g_io_channel_new_file (path, "a+", NULL)) != NULL)
989     {
990       g_io_channel_unref (channel);
991       return TRUE;
992     }
993   return FALSE;
994 }
995 
996 /**
997  * glade_util_have_devhelp:
998  *
999  * Returns: whether the devhelp module is loaded
1000  */
1001 gboolean
glade_util_have_devhelp(void)1002 glade_util_have_devhelp (void)
1003 {
1004   static gint have_devhelp = -1;
1005   gchar *ptr;
1006   gint cnt, ret, major, minor;
1007   GError *error = NULL;
1008 
1009 #define DEVHELP_OLD_MESSAGE  \
1010     "The DevHelp installed on your system is too old, " \
1011     "devhelp feature will be disabled."
1012 
1013 #define DEVHELP_MISSING_MESSAGE  \
1014     "No DevHelp installed on your system, " \
1015     "devhelp feature will be disabled."
1016 
1017   if (have_devhelp >= 0)
1018     return have_devhelp;
1019 
1020   have_devhelp = 0;
1021 
1022   if ((ptr = g_find_program_in_path ("devhelp")) != NULL)
1023     {
1024       g_free (ptr);
1025 
1026       if (g_spawn_command_line_sync ("devhelp --version",
1027                                      &ptr, NULL, &ret, &error))
1028         {
1029           /* If we have a successfull return code.. parse the output.
1030            */
1031           if (ret == 0)
1032             {
1033               gchar name[16];
1034               if ((cnt = sscanf (ptr, "%15s %d.%d\n",
1035                                  name, &major, &minor)) == 3)
1036                 {
1037                   /* Devhelp 0.12 required.
1038                    */
1039                   if (major >= 2 || (major >= 0 && minor >= 12))
1040                     have_devhelp = 1;
1041                   else
1042                     g_message (DEVHELP_OLD_MESSAGE);
1043                 }
1044               else
1045 
1046                 {
1047                   if (ptr != NULL || strlen (ptr) > 0)
1048                     g_warning ("devhelp had unparsable output: "
1049                                "'%s' (parsed %d elements)", ptr, cnt);
1050                   else
1051                     g_message (DEVHELP_OLD_MESSAGE);
1052                 }
1053             }
1054           else
1055             g_warning ("devhelp had bad return code: '%d'", ret);
1056         }
1057       else
1058         {
1059           g_warning ("Error trying to launch devhelp: %s", error->message);
1060           g_error_free (error);
1061         }
1062     }
1063   else
1064     g_message (DEVHELP_MISSING_MESSAGE);
1065 
1066   return have_devhelp;
1067 }
1068 
1069 /**
1070  * glade_util_get_devhelp_icon:
1071  * @size: the preferred icon size
1072  *
1073  * Creates an image displaying the devhelp icon.
1074  *
1075  * Returns: a #GtkImage
1076  */
1077 GtkWidget *
glade_util_get_devhelp_icon(GtkIconSize size)1078 glade_util_get_devhelp_icon (GtkIconSize size)
1079 {
1080   GtkIconTheme *icon_theme;
1081   GdkScreen *screen;
1082   GtkWidget *image;
1083   gchar *path;
1084 
1085   image = gtk_image_new ();
1086   screen = gtk_widget_get_screen (GTK_WIDGET (image));
1087   icon_theme = gtk_icon_theme_get_for_screen (screen);
1088 
1089   if (gtk_icon_theme_has_icon (icon_theme, GLADE_DEVHELP_ICON_NAME))
1090     {
1091       gtk_image_set_from_icon_name (GTK_IMAGE (image), GLADE_DEVHELP_ICON_NAME,
1092                                     size);
1093     }
1094   else
1095     {
1096       path =
1097           g_build_filename (glade_app_get_pixmaps_dir (),
1098                             GLADE_DEVHELP_FALLBACK_ICON_FILE, NULL);
1099 
1100       gtk_image_set_from_file (GTK_IMAGE (image), path);
1101 
1102       g_free (path);
1103     }
1104 
1105   return image;
1106 }
1107 
1108 /**
1109  * glade_util_search_devhep:
1110  * @devhelp: the devhelp widget created by the devhelp module.
1111  * @book: the devhelp book (or %NULL)
1112  * @page: the page in the book (or %NULL)
1113  * @search: the search string (or %NULL)
1114  *
1115  * Envokes devhelp with the appropriate search string
1116  *
1117  */
1118 void
glade_util_search_devhelp(const gchar * book,const gchar * page,const gchar * search)1119 glade_util_search_devhelp (const gchar *book,
1120                            const gchar *page,
1121                            const gchar *search)
1122 {
1123   GError *error = NULL;
1124   gchar *book_comm = NULL, *page_comm = NULL, *search_comm = NULL;
1125   gchar *string;
1126 
1127   g_return_if_fail (glade_util_have_devhelp ());
1128 
1129   if (book)
1130     book_comm = g_strdup_printf ("book:%s", book);
1131   if (page)
1132     page_comm = g_strdup_printf (" page:%s", page);
1133   if (search)
1134     search_comm = g_strdup_printf (" %s", search);
1135 
1136   string = g_strdup_printf ("devhelp -s \"%s%s%s\"",
1137                             book_comm ? book_comm : "",
1138                             page_comm ? page_comm : "",
1139                             search_comm ? search_comm : "");
1140 
1141   if (g_spawn_command_line_async (string, &error) == FALSE)
1142     {
1143       g_warning ("Error envoking devhelp: %s", error->message);
1144       g_error_free (error);
1145     }
1146 
1147   g_free (string);
1148   if (book_comm)
1149     g_free (book_comm);
1150   if (page_comm)
1151     g_free (page_comm);
1152   if (search_comm)
1153     g_free (search_comm);
1154 }
1155 
1156 GtkWidget *
glade_util_get_placeholder_from_pointer(GtkContainer * container)1157 glade_util_get_placeholder_from_pointer (GtkContainer *container)
1158 {
1159   GdkDeviceManager *manager;
1160   GdkDisplay *display;
1161   GdkDevice *device;
1162   GdkWindow *window;
1163 
1164   if (((display = gtk_widget_get_display (GTK_WIDGET (container))) ||
1165        (display = gdk_display_get_default ())) &&
1166       (manager = gdk_display_get_device_manager (display)) &&
1167       (device = gdk_device_manager_get_client_pointer (manager)) &&
1168       (window = gdk_device_get_window_at_position (device, NULL, NULL)))
1169     {
1170       gpointer widget;
1171       gdk_window_get_user_data (window, &widget);
1172 
1173       return GLADE_IS_PLACEHOLDER (widget) ? GTK_WIDGET (widget) : NULL;
1174     }
1175 
1176   return NULL;
1177 }
1178 
1179 /**
1180  * glade_util_object_is_loading:
1181  * @object: A #GObject
1182  *
1183  * Returns: Whether the object's project is being loaded or not.
1184  *
1185  */
1186 gboolean
glade_util_object_is_loading(GObject * object)1187 glade_util_object_is_loading (GObject *object)
1188 {
1189   GladeProject *project;
1190   GladeWidget *widget;
1191 
1192   g_return_val_if_fail (G_IS_OBJECT (object), FALSE);
1193 
1194   widget = glade_widget_get_from_gobject (object);
1195   g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE);
1196 
1197   project = glade_widget_get_project (widget);
1198 
1199   return project && glade_project_is_loading (project);
1200 }
1201 
1202 /**
1203  * glade_util_url_show:
1204  * @url: An URL to display
1205  *
1206  * Portable function for showing an URL @url in a web browser.
1207  *
1208  * Returns: TRUE if a web browser was successfully launched, or FALSE
1209  *
1210  */
1211 gboolean
glade_util_url_show(const gchar * url)1212 glade_util_url_show (const gchar *url)
1213 {
1214   GtkWidget *widget;
1215   GError *error = NULL;
1216   gboolean ret;
1217 
1218   g_return_val_if_fail (url != NULL, FALSE);
1219 
1220   widget = glade_app_get_window ();
1221 
1222   ret = gtk_show_uri (gtk_widget_get_screen (widget),
1223                       url, gtk_get_current_event_time (), &error);
1224   if (error != NULL)
1225     {
1226       GtkWidget *dialog_widget;
1227 
1228       dialog_widget = gtk_message_dialog_new (GTK_WINDOW (widget),
1229                                               GTK_DIALOG_DESTROY_WITH_PARENT,
1230                                               GTK_MESSAGE_ERROR,
1231                                               GTK_BUTTONS_CLOSE,
1232                                               "%s", _("Could not show link:"));
1233       gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG
1234                                                 (dialog_widget), "%s",
1235                                                 error->message);
1236       g_error_free (error);
1237 
1238       g_signal_connect (dialog_widget, "response",
1239                         G_CALLBACK (gtk_widget_destroy), NULL);
1240 
1241       gtk_window_present (GTK_WINDOW (dialog_widget));
1242     }
1243 
1244   return ret;
1245 }
1246 
1247 /**
1248  * glade_util_get_file_mtime:
1249  * @filename: A filename
1250  * @error: return location for errors
1251  *
1252  * Gets the UTC modification time of file @filename.
1253  *
1254  * Returns: The mtime of the file, or %0 if the file attributes
1255  *          could not be read.
1256  */
1257 time_t
glade_util_get_file_mtime(const gchar * filename,GError ** error)1258 glade_util_get_file_mtime (const gchar *filename, GError **error)
1259 {
1260   struct stat info;
1261   gint retval;
1262 
1263   retval = g_stat (filename, &info);
1264 
1265   if (retval != 0)
1266     {
1267       g_set_error (error,
1268                    G_FILE_ERROR,
1269                    g_file_error_from_errno (errno),
1270                    "could not stat file '%s': %s", filename,
1271                    g_strerror (errno));
1272       return (time_t) 0;
1273     }
1274   else
1275     {
1276       return info.st_mtime;
1277     }
1278 }
1279 
1280 gchar *
glade_util_filename_to_icon_name(const gchar * value)1281 glade_util_filename_to_icon_name (const gchar *value)
1282 {
1283   gchar *icon_name, *p;
1284   g_return_val_if_fail (value && value[0], NULL);
1285 
1286   icon_name = g_strdup_printf ("glade-generated-%s", value);
1287 
1288   if ((p = strrchr (icon_name, '.')) != NULL)
1289     *p = '-';
1290 
1291   return icon_name;
1292 }
1293 
1294 gchar *
glade_util_icon_name_to_filename(const gchar * value)1295 glade_util_icon_name_to_filename (const gchar *value)
1296 {
1297   /* sscanf makes us allocate a buffer */
1298   gchar filename[FILENAME_MAX], *p;
1299   g_return_val_if_fail (value && value[0], NULL);
1300 
1301   sscanf (value, "glade-generated-%s", filename);
1302 
1303   /* XXX: Filenames without an extention will evidently
1304    * break here
1305    */
1306   if ((p = strrchr (filename, '-')) != NULL)
1307     *p = '.';
1308 
1309   return g_strdup (filename);
1310 }
1311 
1312 gint
glade_utils_enum_value_from_string(GType enum_type,const gchar * strval)1313 glade_utils_enum_value_from_string (GType enum_type, const gchar *strval)
1314 {
1315   gint value = 0;
1316   const gchar *displayable;
1317   GValue *gvalue;
1318 
1319   g_return_val_if_fail (strval && strval[0], 0);
1320 
1321   if (((displayable =
1322         glade_get_value_from_displayable (enum_type, strval)) != NULL &&
1323        (gvalue =
1324         glade_utils_value_from_string (enum_type, displayable, NULL)) != NULL) ||
1325       (gvalue =
1326        glade_utils_value_from_string (enum_type, strval, NULL)) != NULL)
1327     {
1328       value = g_value_get_enum (gvalue);
1329       g_value_unset (gvalue);
1330       g_free (gvalue);
1331     }
1332   return value;
1333 }
1334 
1335 static gchar *
glade_utils_enum_string_from_value_real(GType enum_type,gint value,gboolean displayable)1336 glade_utils_enum_string_from_value_real (GType enum_type,
1337                                          gint value,
1338                                          gboolean displayable)
1339 {
1340   GValue gvalue = { 0, };
1341   gchar *string;
1342 
1343   g_value_init (&gvalue, enum_type);
1344   g_value_set_enum (&gvalue, value);
1345 
1346   string = glade_utils_string_from_value (&gvalue);
1347   g_value_unset (&gvalue);
1348 
1349   if (displayable && string)
1350     {
1351       const gchar *dstring = glade_get_displayable_value (enum_type, string);
1352       if (dstring)
1353         {
1354           g_free (string);
1355           return g_strdup (dstring);
1356         }
1357     }
1358 
1359   return string;
1360 }
1361 
1362 gchar *
glade_utils_enum_string_from_value(GType enum_type,gint value)1363 glade_utils_enum_string_from_value (GType enum_type, gint value)
1364 {
1365   return glade_utils_enum_string_from_value_real (enum_type, value, FALSE);
1366 }
1367 
1368 gchar *
glade_utils_enum_string_from_value_displayable(GType enum_type,gint value)1369 glade_utils_enum_string_from_value_displayable (GType enum_type, gint value)
1370 {
1371   return glade_utils_enum_string_from_value_real (enum_type, value, TRUE);
1372 }
1373 
1374 
1375 gint
glade_utils_flags_value_from_string(GType flags_type,const gchar * strval)1376 glade_utils_flags_value_from_string (GType flags_type, const gchar *strval)
1377 {
1378   gint value = 0;
1379   const gchar *displayable;
1380   GValue *gvalue;
1381 
1382   g_return_val_if_fail (strval && strval[0], 0);
1383 
1384   if (((displayable =
1385         glade_get_value_from_displayable (flags_type, strval)) != NULL &&
1386        (gvalue =
1387         glade_utils_value_from_string (flags_type, displayable, NULL)) != NULL) ||
1388       (gvalue =
1389        glade_utils_value_from_string (flags_type, strval, NULL)) != NULL)
1390     {
1391       value = g_value_get_flags (gvalue);
1392       g_value_unset (gvalue);
1393       g_free (gvalue);
1394     }
1395   return value;
1396 }
1397 
1398 static gchar *
glade_utils_flags_string_from_value_real(GType flags_type,gint value,gboolean displayable)1399 glade_utils_flags_string_from_value_real (GType flags_type,
1400                                           gint value,
1401                                           gboolean displayable)
1402 {
1403   GValue gvalue = { 0, };
1404   gchar *string;
1405 
1406   g_value_init (&gvalue, flags_type);
1407   g_value_set_flags (&gvalue, value);
1408 
1409   string = glade_utils_string_from_value (&gvalue);
1410   g_value_unset (&gvalue);
1411 
1412   if (displayable && string)
1413     {
1414       const gchar *dstring = glade_get_displayable_value (flags_type, string);
1415       if (dstring)
1416         {
1417           g_free (string);
1418           return g_strdup (dstring);
1419         }
1420     }
1421 
1422   return string;
1423 }
1424 
1425 gchar *
glade_utils_flags_string_from_value(GType flags_type,gint value)1426 glade_utils_flags_string_from_value (GType flags_type, gint value)
1427 {
1428   return glade_utils_flags_string_from_value_real (flags_type, value, FALSE);
1429 
1430 }
1431 
1432 
1433 gchar *
glade_utils_flags_string_from_value_displayable(GType flags_type,gint value)1434 glade_utils_flags_string_from_value_displayable (GType flags_type, gint value)
1435 {
1436   return glade_utils_flags_string_from_value_real (flags_type, value, TRUE);
1437 }
1438 
1439 
1440 /* A hash table of generically created property classes for
1441  * fundamental types, so we can easily use glade's conversion
1442  * system without using properties (only GTypes)
1443  */
1444 static GHashTable *generic_property_classes = NULL;
1445 
1446 
1447 static gboolean
utils_gtype_equal(gconstpointer v1,gconstpointer v2)1448 utils_gtype_equal (gconstpointer v1, gconstpointer v2)
1449 {
1450   return *((const GType *) v1) == *((const GType *) v2);
1451 }
1452 
1453 static guint
utils_gtype_hash(gconstpointer v)1454 utils_gtype_hash (gconstpointer v)
1455 {
1456   return *(const GType *) v;
1457 }
1458 
1459 
1460 static GladePropertyClass *
pclass_from_gtype(GType type)1461 pclass_from_gtype (GType type)
1462 {
1463   GladePropertyClass *property_class = NULL;
1464   GParamSpec *pspec = NULL;
1465 
1466   if (!generic_property_classes)
1467     generic_property_classes =
1468         g_hash_table_new_full (utils_gtype_hash, utils_gtype_equal, g_free,
1469                                (GDestroyNotify) glade_property_class_free);
1470 
1471   property_class = g_hash_table_lookup (generic_property_classes, &type);
1472 
1473   if (!property_class)
1474     {
1475       /* Support enum and flag types, and a hardcoded list of fundamental types */
1476       if (type == G_TYPE_CHAR)
1477         pspec = g_param_spec_char ("dummy", "dummy", "dummy",
1478                                    G_MININT8, G_MAXINT8, 0,
1479                                    G_PARAM_READABLE | G_PARAM_WRITABLE);
1480       else if (type == G_TYPE_UCHAR)
1481         pspec = g_param_spec_char ("dummy", "dummy", "dummy",
1482                                    0, G_MAXUINT8, 0,
1483                                    G_PARAM_READABLE | G_PARAM_WRITABLE);
1484       else if (type == G_TYPE_BOOLEAN)
1485         pspec = g_param_spec_boolean ("dummy", "dummy", "dummy",
1486                                       FALSE,
1487                                       G_PARAM_READABLE | G_PARAM_WRITABLE);
1488       else if (type == G_TYPE_INT)
1489         pspec = g_param_spec_int ("dummy", "dummy", "dummy",
1490                                   G_MININT, G_MAXINT, 0,
1491                                   G_PARAM_READABLE | G_PARAM_WRITABLE);
1492       else if (type == G_TYPE_UINT)
1493         pspec = g_param_spec_uint ("dummy", "dummy", "dummy",
1494                                    0, G_MAXUINT, 0,
1495                                    G_PARAM_READABLE | G_PARAM_WRITABLE);
1496       else if (type == G_TYPE_LONG)
1497         pspec = g_param_spec_long ("dummy", "dummy", "dummy",
1498                                    G_MINLONG, G_MAXLONG, 0,
1499                                    G_PARAM_READABLE | G_PARAM_WRITABLE);
1500       else if (type == G_TYPE_ULONG)
1501         pspec = g_param_spec_ulong ("dummy", "dummy", "dummy",
1502                                     0, G_MAXULONG, 0,
1503                                     G_PARAM_READABLE | G_PARAM_WRITABLE);
1504       else if (type == G_TYPE_INT64)
1505         pspec = g_param_spec_int64 ("dummy", "dummy", "dummy",
1506                                     G_MININT64, G_MAXINT64, 0,
1507                                     G_PARAM_READABLE | G_PARAM_WRITABLE);
1508       else if (type == G_TYPE_UINT64)
1509         pspec = g_param_spec_uint64 ("dummy", "dummy", "dummy",
1510                                      0, G_MAXUINT64, 0,
1511                                      G_PARAM_READABLE | G_PARAM_WRITABLE);
1512       else if (type == G_TYPE_FLOAT)
1513         pspec = g_param_spec_float ("dummy", "dummy", "dummy",
1514                                     G_MINFLOAT, G_MAXFLOAT, 1.0F,
1515                                     G_PARAM_READABLE | G_PARAM_WRITABLE);
1516       else if (type == G_TYPE_DOUBLE)
1517         pspec = g_param_spec_double ("dummy", "dummy", "dummy",
1518                                      G_MINDOUBLE, G_MAXDOUBLE, 1.0F,
1519                                      G_PARAM_READABLE | G_PARAM_WRITABLE);
1520       else if (type == G_TYPE_STRING)
1521         pspec = g_param_spec_string ("dummy", "dummy", "dummy",
1522                                      NULL, G_PARAM_READABLE | G_PARAM_WRITABLE);
1523       else if (type == G_TYPE_OBJECT || g_type_is_a (type, G_TYPE_OBJECT))
1524         pspec = g_param_spec_object ("dummy", "dummy", "dummy",
1525                                      type, G_PARAM_READABLE | G_PARAM_WRITABLE);
1526       else if (G_TYPE_IS_ENUM (type))
1527         {
1528           GEnumClass *eclass = g_type_class_ref (type);
1529           pspec = g_param_spec_enum ("dummy", "dummy", "dummy",
1530                                      type, eclass->minimum,
1531                                      G_PARAM_READABLE | G_PARAM_WRITABLE);
1532           g_type_class_unref (eclass);
1533         }
1534       else if (G_TYPE_IS_FLAGS (type))
1535         pspec = g_param_spec_flags ("dummy", "dummy", "dummy",
1536                                     type, 0,
1537                                     G_PARAM_READABLE | G_PARAM_WRITABLE);
1538 
1539       if (pspec)
1540         {
1541           if ((property_class =
1542                glade_property_class_new_from_spec_full (NULL, pspec,
1543                                                         FALSE)) != NULL)
1544             {
1545               /* XXX If we ever free the hash table, property classes wont touch
1546                * the allocated pspecs, so they would theoretically be leaked.
1547                */
1548               g_hash_table_insert (generic_property_classes,
1549                                    g_memdup (&type, sizeof (GType)),
1550                                    property_class);
1551             }
1552           else
1553             g_warning ("Unable to create property class for type %s",
1554                        g_type_name (type));
1555         }
1556       else
1557         g_warning ("No generic conversion support for type %s",
1558                    g_type_name (type));
1559     }
1560   return property_class;
1561 }
1562 
1563 /**
1564  * glade_utils_value_from_string:
1565  * @type: a #GType to convert with
1566  * @string: the string to convert
1567  * @project: the #GladeProject to look for formats of object names when needed
1568  * @widget: if the value is a gobject, this #GladeWidget will be used to look
1569  *          for an object in the same widget tree.
1570  *
1571  * Allocates and sets a #GValue of type @type
1572  * set to @string (using glade conversion routines)
1573  *
1574  * Returns: A newly allocated and set #GValue
1575  */
1576 GValue *
glade_utils_value_from_string(GType type,const gchar * string,GladeProject * project)1577 glade_utils_value_from_string (GType type,
1578                                const gchar *string,
1579                                GladeProject *project)
1580 {
1581   GladePropertyClass *pclass;
1582 
1583   g_return_val_if_fail (type != G_TYPE_INVALID, NULL);
1584   g_return_val_if_fail (string != NULL, NULL);
1585 
1586   if ((pclass = pclass_from_gtype (type)) != NULL)
1587     return glade_property_class_make_gvalue_from_string (pclass, string, project);
1588 
1589   return NULL;
1590 }
1591 
1592 /**
1593  * glade_utils_boolean_from_string:
1594  * @string: the string to convert
1595  * @value: return location
1596  *
1597  * Parse a boolean value
1598  *
1599  * Returns: True if there was an error on the conversion.
1600  */
1601 gboolean
glade_utils_boolean_from_string(const gchar * string,gboolean * value)1602 glade_utils_boolean_from_string (const gchar *string, gboolean *value)
1603 {
1604   if (string)
1605     {
1606       const gchar *c = string;
1607 
1608       /* Skip white spaces */
1609       while (g_ascii_isspace (*c))
1610         c++;
1611 
1612       /* We only need the first char */
1613       switch (*c)
1614         {
1615           case '1':
1616           case 't':
1617           case 'T':
1618           case 'y':
1619           case 'Y':
1620             if (value)
1621               *value = TRUE;
1622             return FALSE;
1623           break;
1624 
1625           case '0':
1626           case 'f':
1627           case 'F':
1628           case 'n':
1629           case 'N':
1630             if (value)
1631               *value = FALSE;
1632             return FALSE;
1633           break;
1634         }
1635     }
1636 
1637   return TRUE;
1638 }
1639 
1640 /**
1641  * glade_utils_string_from_value:
1642  * @value: a #GValue to convert
1643  *
1644  * Serializes #GValue into a string
1645  * (using glade conversion routines)
1646  *
1647  * Returns: A newly allocated string
1648  */
1649 gchar *
glade_utils_string_from_value(const GValue * value)1650 glade_utils_string_from_value (const GValue *value)
1651 {
1652   GladePropertyClass *pclass;
1653 
1654   g_return_val_if_fail (value != NULL, NULL);
1655 
1656   if ((pclass = pclass_from_gtype (G_VALUE_TYPE (value))) != NULL)
1657     return glade_property_class_make_string_from_gvalue (pclass, value);
1658 
1659   return NULL;
1660 }
1661 
1662 
1663 /**
1664  * glade_utils_liststore_from_enum_type:
1665  * @enum_type: A #GType
1666  * @include_empty: wheather to prepend an "Unset" slot
1667  *
1668  * Creates a liststore suitable for comboboxes and such to
1669  * chose from a variety of types.
1670  *
1671  * Returns: A new #GtkListStore
1672  */
1673 GtkListStore *
glade_utils_liststore_from_enum_type(GType enum_type,gboolean include_empty)1674 glade_utils_liststore_from_enum_type (GType enum_type, gboolean include_empty)
1675 {
1676   GtkListStore *store;
1677   GtkTreeIter iter;
1678   GEnumClass *eclass;
1679   guint i;
1680 
1681   eclass = g_type_class_ref (enum_type);
1682 
1683   store = gtk_list_store_new (1, G_TYPE_STRING);
1684 
1685   if (include_empty)
1686     {
1687       gtk_list_store_append (store, &iter);
1688       gtk_list_store_set (store, &iter, 0, _("None"), -1);
1689     }
1690 
1691   for (i = 0; i < eclass->n_values; i++)
1692     {
1693       const gchar *displayable =
1694           glade_get_displayable_value (enum_type, eclass->values[i].value_nick);
1695 
1696       gtk_list_store_append (store, &iter);
1697       gtk_list_store_set (store, &iter,
1698                           0,
1699                           displayable ? displayable : eclass->values[i].
1700                           value_nick, -1);
1701     }
1702 
1703   g_type_class_unref (eclass);
1704 
1705   return store;
1706 }
1707 
1708 
1709 
1710 /**
1711  * glade_utils_hijack_key_press:
1712  * @win: a #GtkWindow
1713  * event: the GdkEventKey
1714  * user_data: unused
1715  *
1716  * This function is meant to be attached to key-press-event of a toplevel,
1717  * it simply allows the window contents to treat key events /before/
1718  * accelerator keys come into play (this way widgets dont get deleted
1719  * when cutting text in an entry etc.).
1720  * Creates a liststore suitable for comboboxes and such to
1721  * chose from a variety of types.
1722  *
1723  * Returns: whether the event was handled
1724  */
1725 gint
glade_utils_hijack_key_press(GtkWindow * win,GdkEventKey * event,gpointer user_data)1726 glade_utils_hijack_key_press (GtkWindow *win,
1727                               GdkEventKey *event,
1728                               gpointer user_data)
1729 {
1730   GtkWidget *focus_widget;
1731 
1732   focus_widget = gtk_window_get_focus (win);
1733   if (focus_widget && (event->keyval == GDK_KEY_Delete ||       /* Filter Delete from accelerator keys */
1734                        ((event->state & GDK_CONTROL_MASK) &&    /* CNTL keys... */
1735                         ((event->keyval == GDK_KEY_c || event->keyval == GDK_KEY_C) ||  /* CNTL-C (copy)  */
1736                          (event->keyval == GDK_KEY_x || event->keyval == GDK_KEY_X) ||  /* CNTL-X (cut)   */
1737                          (event->keyval == GDK_KEY_v || event->keyval == GDK_KEY_V) ||  /* CNTL-V (paste) */
1738                          (event->keyval == GDK_KEY_n || event->keyval == GDK_KEY_N))))) /* CNTL-N (new project) */
1739     {
1740       return gtk_widget_event (focus_widget, (GdkEvent *) event);
1741     }
1742   return FALSE;
1743 }
1744 
1745 
1746 void
glade_utils_cairo_draw_line(cairo_t * cr,GdkColor * color,gint x1,gint y1,gint x2,gint y2)1747 glade_utils_cairo_draw_line (cairo_t *cr,
1748                              GdkColor *color,
1749                              gint x1, gint y1,
1750                              gint x2, gint y2)
1751 {
1752   cairo_save (cr);
1753 
1754 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1755   gdk_cairo_set_source_color (cr, color);
1756 G_GNUC_END_IGNORE_DEPRECATIONS
1757   cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
1758 
1759   cairo_move_to (cr, x1 + 0.5, y1 + 0.5);
1760   cairo_line_to (cr, x2 + 0.5, y2 + 0.5);
1761   cairo_stroke (cr);
1762 
1763   cairo_restore (cr);
1764 }
1765 
1766 
1767 void
glade_utils_cairo_draw_rectangle(cairo_t * cr,GdkColor * color,gboolean filled,gint x,gint y,gint width,gint height)1768 glade_utils_cairo_draw_rectangle (cairo_t *cr,
1769                                   GdkColor *color,
1770                                   gboolean filled,
1771                                   gint x, gint y,
1772                                   gint width, gint height)
1773 {
1774 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
1775   gdk_cairo_set_source_color (cr, color);
1776 G_GNUC_END_IGNORE_DEPRECATIONS
1777 
1778   if (filled)
1779     {
1780       cairo_rectangle (cr, x, y, width, height);
1781       cairo_fill (cr);
1782     }
1783   else
1784     {
1785       cairo_rectangle (cr, x + 0.5, y + 0.5, width, height);
1786       cairo_stroke (cr);
1787     }
1788 }
1789 
1790 
1791 /* copied from gedit */
1792 gchar *
glade_utils_replace_home_dir_with_tilde(const gchar * path)1793 glade_utils_replace_home_dir_with_tilde (const gchar *path)
1794 {
1795 #ifdef G_OS_UNIX
1796   gchar *tmp;
1797   gchar *home;
1798 
1799   g_return_val_if_fail (path != NULL, NULL);
1800 
1801   /* Note that g_get_home_dir returns a const string */
1802   tmp = (gchar *) g_get_home_dir ();
1803 
1804   if (tmp == NULL)
1805     return g_strdup (path);
1806 
1807   home = g_filename_to_utf8 (tmp, -1, NULL, NULL, NULL);
1808   if (home == NULL)
1809     return g_strdup (path);
1810 
1811   if (strcmp (path, home) == 0)
1812     {
1813       g_free (home);
1814 
1815       return g_strdup ("~");
1816     }
1817 
1818   tmp = home;
1819   home = g_strdup_printf ("%s/", tmp);
1820   g_free (tmp);
1821 
1822   if (g_str_has_prefix (path, home))
1823     {
1824       gchar *res;
1825 
1826       res = g_strdup_printf ("~/%s", path + strlen (home));
1827 
1828       g_free (home);
1829 
1830       return res;
1831     }
1832 
1833   g_free (home);
1834 
1835   return g_strdup (path);
1836 #else
1837   return g_strdup (path);
1838 #endif
1839 }
1840 
1841 static void
draw_tip(cairo_t * cr)1842 draw_tip (cairo_t *cr)
1843 {
1844   cairo_line_to (cr, 2, 8);
1845   cairo_line_to (cr, 2, 4);
1846   cairo_line_to (cr, 0, 4);
1847   cairo_line_to (cr, 0, 3);
1848   cairo_line_to (cr, 3, 0);
1849   cairo_line_to (cr, 6, 3);
1850   cairo_line_to (cr, 6, 4);
1851   cairo_line_to (cr, 4, 4);
1852 
1853   cairo_translate (cr, 12, 6);
1854   cairo_rotate (cr, G_PI_2);
1855 }
1856 
1857 static void
draw_tips(cairo_t * cr)1858 draw_tips (cairo_t *cr)
1859 {
1860   cairo_move_to (cr, 2, 8);
1861   draw_tip (cr); draw_tip (cr); draw_tip (cr); draw_tip (cr);
1862   cairo_close_path (cr);
1863 }
1864 
1865 static void
draw_pointer(cairo_t * cr)1866 draw_pointer (cairo_t *cr)
1867 {
1868   cairo_line_to (cr, 8, 3);
1869   cairo_line_to (cr, 19, 14);
1870   cairo_line_to (cr, 13.75, 14);
1871   cairo_line_to (cr, 16.5, 19);
1872   cairo_line_to (cr, 14, 21);
1873   cairo_line_to (cr, 11, 16);
1874   cairo_line_to (cr, 7, 19);
1875   cairo_line_to (cr, 7, 3);
1876   cairo_line_to (cr, 8, 3);
1877 }
1878 
1879 /* Needed for private draw functions! */
1880 #include "glade-design-private.h"
1881 
1882 /**
1883  * glade_utils_pointer_mode_render_icon:
1884  * @mode: the #GladePointerMode to render as icon
1885  * @size: icon size
1886  *
1887  * Render an icon representing the pointer mode.
1888  * Best view with sizes bigger than GTK_ICON_SIZE_LARGE_TOOLBAR.
1889  */
1890 GdkPixbuf *
glade_utils_pointer_mode_render_icon(GladePointerMode mode,GtkIconSize size)1891 glade_utils_pointer_mode_render_icon (GladePointerMode mode, GtkIconSize size)
1892 {
1893   GdkRGBA c1, c2, fg, bg;
1894   cairo_surface_t *surface;
1895   gint width, height;
1896   GdkPixbuf *pix;
1897   cairo_t *cr;
1898 
1899   if (gtk_icon_size_lookup (size, &width, &height) == FALSE) return NULL;
1900 
1901   surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
1902   cr = cairo_create (surface);
1903   cairo_scale (cr, width/24.0, height/24.0);
1904 
1905   /* Now get colors */
1906   _glade_design_layout_get_colors (&bg, &fg, &c1, &c2);
1907 
1908   /* Clear surface */
1909   cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
1910   cairo_fill(cr);
1911   cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
1912 
1913   switch (mode)
1914     {
1915       case GLADE_POINTER_SELECT:
1916       case GLADE_POINTER_ADD_WIDGET:
1917         cairo_set_line_width (cr, 1);
1918         cairo_translate (cr, 1.5, 1.5);
1919         draw_pointer (cr);
1920         fg.alpha = .16;
1921         gdk_cairo_set_source_rgba (cr, &fg);
1922         cairo_stroke (cr);
1923 
1924         cairo_translate (cr, -1, -1);
1925         draw_pointer (cr);
1926         gdk_cairo_set_source_rgba (cr, &c2);
1927         cairo_fill_preserve (cr);
1928 
1929         fg.alpha = .64;
1930         gdk_cairo_set_source_rgba (cr, &fg);
1931         cairo_stroke (cr);
1932       break;
1933       case GLADE_POINTER_DRAG_RESIZE:
1934         cairo_set_line_width (cr, 1);
1935         cairo_translate (cr, 10.5, 3.5);
1936 
1937         draw_tips (cr);
1938 
1939         fg.alpha = .16;
1940         gdk_cairo_set_source_rgba (cr, &fg);
1941         cairo_stroke (cr);
1942 
1943         cairo_translate (cr, -1, -1);
1944         draw_tips (cr);
1945 
1946         gdk_cairo_set_source_rgba (cr, &c2);
1947         cairo_fill_preserve (cr);
1948 
1949         c1.red = MAX (0, c1.red - .1);
1950         c1.green = MAX (0, c1.green - .1);
1951         c1.blue = MAX (0, c1.blue - .1);
1952         gdk_cairo_set_source_rgba (cr, &c1);
1953         cairo_stroke (cr);
1954       break;
1955       case GLADE_POINTER_MARGIN_EDIT:
1956         {
1957           gdk_cairo_set_source_rgba (cr, &bg);
1958           cairo_rectangle (cr, 4, 4, 18, 18);
1959           cairo_fill (cr);
1960 
1961           c1.alpha = .1;
1962           gdk_cairo_set_source_rgba (cr, &c1);
1963           cairo_rectangle (cr, 6, 6, 16, 16);
1964           cairo_fill (cr);
1965 
1966           cairo_set_line_width (cr, 1);
1967           fg.alpha = .32;
1968           gdk_cairo_set_source_rgba (cr, &fg);
1969           cairo_move_to (cr, 16.5, 22);
1970           cairo_line_to (cr, 16.5, 16.5);
1971           cairo_line_to (cr, 22, 16.5);
1972           cairo_stroke (cr);
1973 
1974           c1.alpha = .16;
1975           gdk_cairo_set_source_rgba (cr, &c1);
1976           cairo_rectangle (cr, 16, 16, 6, 6);
1977           cairo_fill (cr);
1978 
1979           cairo_set_line_width (cr, 2);
1980           c1.alpha = .75;
1981           gdk_cairo_set_source_rgba (cr, &c1);
1982           cairo_move_to (cr, 6, 22);
1983           cairo_line_to (cr, 6, 6);
1984           cairo_line_to (cr, 22, 6);
1985           cairo_stroke (cr);
1986 
1987           c1.alpha = 1;
1988           cairo_scale (cr, .75, .75);
1989           cairo_set_line_width (cr, 4);
1990           _glade_design_layout_draw_node (cr, 16*1.25, 6*1.25, &c1, &c2);
1991           _glade_design_layout_draw_node (cr, 6*1.25, 16*1.25, &c1, &c2);
1992         }
1993       break;
1994       case GLADE_POINTER_ALIGN_EDIT:
1995         cairo_scale (cr, 1.5, 1.5);
1996         cairo_rotate (cr, 45*(G_PI/180));
1997         cairo_translate (cr, 11, 2);
1998         _glade_design_layout_draw_pushpin (cr, 2.5, &c1, &c2, &c2, &fg);
1999       break;
2000       default:
2001       break;
2002     }
2003 
2004   pix = gdk_pixbuf_get_from_surface (surface, 0, 0,
2005                                      cairo_image_surface_get_width (surface),
2006                                      cairo_image_surface_get_height (surface));
2007 
2008   cairo_surface_destroy (surface);
2009   cairo_destroy (cr);
2010 
2011   return pix;
2012 }
2013 
2014 /**
2015  * glade_utils_get_pointer:
2016  * @widget: The widget to get the mouse position relative for
2017  * @window: The window of the current event, or %NULL
2018  * @device: The device, if not specified, the current event will be expected to have a @device.
2019  * @x: The location to store the mouse pointer X position
2020  * @y: The location to store the mouse pointer Y position
2021  *
2022  * Get's the pointer position relative to @widget, while @window and @device
2023  * are not absolutely needed, they should be passed wherever possible.
2024  *
2025  */
2026 void
glade_utils_get_pointer(GtkWidget * widget,GdkWindow * window,GdkDevice * device,gint * x,gint * y)2027 glade_utils_get_pointer (GtkWidget *widget,
2028 			 GdkWindow *window,
2029 			 GdkDevice *device,
2030 			 gint      *x,
2031 			 gint      *y)
2032 {
2033   gint device_x = 0, device_y = 0;
2034   gint final_x = 0, final_y = 0;
2035   GtkWidget *event_widget = NULL;
2036 
2037   g_return_if_fail (GTK_IS_WIDGET (widget));
2038 
2039   if (!device)
2040     {
2041       GdkEvent *event = gtk_get_current_event ();
2042 
2043       device = gdk_event_get_device (event);
2044       gdk_event_free (event);
2045     }
2046 
2047   g_return_if_fail (GDK_IS_DEVICE (device));
2048 
2049   if (!window)
2050     window = gtk_widget_get_window (widget);
2051 
2052   g_return_if_fail (GDK_IS_WINDOW (window));
2053 
2054   gdk_window_get_device_position (window, device, &device_x, &device_y, NULL);
2055   gdk_window_get_user_data (window, (gpointer)&event_widget);
2056 
2057   if (event_widget != widget)
2058     {
2059       gtk_widget_translate_coordinates (event_widget,
2060                                         widget,
2061                                         device_x, device_y,
2062 					&final_x, &final_y);
2063     }
2064   else
2065     {
2066       final_x = device_x;
2067       final_y = device_y;
2068     }
2069 
2070   if (x)
2071     *x = final_x;
2072   if (y)
2073     *y = final_y;
2074 }
2075 
2076 /* Use this to disable scroll events on property editors,
2077  * we dont want them handling scroll because they are inside
2078  * a scrolled window and interrupt workflow causing unexpected
2079  * results when scrolled.
2080  */
2081 static gint
abort_scroll_events(GtkWidget * widget,GdkEvent * event,gpointer user_data)2082 abort_scroll_events (GtkWidget *widget,
2083 		     GdkEvent  *event,
2084 		     gpointer   user_data)
2085 {
2086   GtkWidget *parent = gtk_widget_get_parent (widget);
2087 
2088   /* Removing the events from the mask doesnt work for
2089    * stubborn combo boxes which call gtk_widget_add_events()
2090    * in it's gtk_combo_box_init() - so handle the event and propagate
2091    * it up the tree so the scrollwindow still handles the scroll event.
2092    */
2093   gtk_propagate_event (parent, event);
2094 
2095   return TRUE;
2096 }
2097 
2098 void
glade_util_remove_scroll_events(GtkWidget * widget)2099 glade_util_remove_scroll_events (GtkWidget *widget)
2100 {
2101   gint events = gtk_widget_get_events (widget);
2102 
2103   events &= ~(GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK);
2104   gtk_widget_set_events (widget, events);
2105 
2106   g_signal_connect (G_OBJECT (widget), "scroll-event",
2107 		    G_CALLBACK (abort_scroll_events), NULL);
2108 }
2109 
2110 /**
2111  * _glade_util_file_get_relative_path:
2112  * @target: input GFile
2113  * @source: input GFile
2114  *
2115  * Gets the path for @source relative to @target even if @source is not a
2116  * descendant of @target.
2117  *
2118  */
2119 gchar *
_glade_util_file_get_relative_path(GFile * target,GFile * source)2120 _glade_util_file_get_relative_path (GFile *target, GFile *source)
2121 {
2122   gchar *relative_path;
2123 
2124   if ((relative_path = g_file_get_relative_path (target, source)) == NULL)
2125     {
2126       GString *relpath = g_string_new ("");
2127 
2128       g_object_ref (target);
2129 
2130       while (relative_path == NULL)
2131         {
2132           GFile *old_target = target;
2133           target = g_file_get_parent (target);
2134 
2135           relative_path = g_file_get_relative_path (target, source);
2136 
2137           g_string_append (relpath, "..");
2138           g_string_append_c (relpath, G_DIR_SEPARATOR);
2139 
2140           g_object_unref (old_target);
2141         }
2142 
2143       g_string_append (relpath, relative_path);
2144       g_free (relative_path);
2145       relative_path = g_string_free (relpath, FALSE);
2146     }
2147 
2148   return relative_path;
2149 }
2150