1 /*
2  * glade-clipboard.c - An object for handling Cut/Copy/Paste.
3  *
4  * Copyright (C) 2005 The GNOME Foundation.
5  *
6  * Author(s):
7  *      Tristan Van Berkom <tvb@gnome.org>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
22  * USA.
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 
29 #include <glib-object.h>
30 #include <glib/gi18n-lib.h>
31 #include <string.h>
32 #include "glade-builtins.h"
33 #include "glade-displayable-values.h"
34 
35 
36 struct _GladeParamSpecObjects
37 {
38   GParamSpec parent_instance;
39 
40   GType type;                   /* Object or interface type accepted
41                                  * in this object list.
42                                  */
43 };
44 
45 typedef struct _GladeStockItem
46 {
47   gchar *value_name;
48   gchar *value_nick;
49   gchar *clean_name;
50   gint value;
51 } GladeStockItem;
52 
53 
54 /************************************************************
55  *      Auto-generate the enum type for stock properties    *
56  ************************************************************/
57 
58 /* Hard-coded list of stock images (and displayable translations) from gtk+ that are not stock "items" */
59 static const gchar *builtin_stock_images[] = {
60   "gtk-dialog-authentication",  /* GTK_STOCK_DIALOG_AUTHENTICATION */
61   "gtk-dnd",                    /* GTK_STOCK_DND */
62   "gtk-dnd-multiple",           /* GTK_STOCK_DND_MULTIPLE */
63   "gtk-color-picker",           /* GTK_STOCK_COLOR_PICKER */
64   "gtk-directory",              /* GTK_STOCK_DIRECTORY */
65   "gtk-file",                   /* GTK_STOCK_FILE */
66   "gtk-missing-image"           /* GTK_STOCK_MISSING_IMAGE */
67 };
68 
69 static const gchar *builtin_stock_displayables[] = {
70   /* GTK_STOCK_DIALOG_AUTHENTICATION */
71   N_("Authentication"),
72   /* GTK_STOCK_DND */
73   N_("Drag and Drop"),
74   /* GTK_STOCK_DND_MULTIPLE */
75   N_("Drag and Drop Multiple"),
76   /* GTK_STOCK_COLOR_PICKER */
77   N_("Color Picker"),
78   /* GTK_STOCK_DIRECTORY */
79   N_("Directory"),
80   /* GTK_STOCK_FILE */
81   N_("File"),
82   /* GTK_STOCK_MISSING_IMAGE */
83   N_("Missing Image")
84 };
85 
86 static GSList *stock_prefixs = NULL;
87 static gboolean stock_prefixs_done = FALSE;
88 
89 /* FIXME: func needs documentation
90  */
91 void
glade_standard_stock_append_prefix(const gchar * prefix)92 glade_standard_stock_append_prefix (const gchar * prefix)
93 {
94   if (stock_prefixs_done)
95     {
96       g_warning
97           ("glade_standard_stock_append_prefix should be used in catalog init-function");
98       return;
99     }
100 
101   stock_prefixs = g_slist_append (stock_prefixs, g_strdup (prefix));
102 }
103 
104 static GladeStockItem *
new_from_values(const gchar * name,const gchar * nick,gint value)105 new_from_values (const gchar * name, const gchar * nick, gint value)
106 {
107   GladeStockItem *new_gsi = NULL;
108   gchar *clean_name;
109   size_t len = 0;
110   guint i = 0;
111   guint j = 0;
112 
113   new_gsi = (GladeStockItem *) g_malloc0 (sizeof (GladeStockItem));
114 
115   new_gsi->value_name = g_strdup (name);
116   new_gsi->value_nick = g_strdup (nick);
117   new_gsi->value = value;
118 
119 
120   clean_name = g_strdup (name);
121   len = strlen (clean_name);
122 
123   while (i + j <= len)
124     {
125       if (clean_name[i + j] == '_')
126         j++;
127 
128       clean_name[i] = clean_name[i + j];
129       i++;
130     }
131 
132   new_gsi->clean_name = g_utf8_collate_key (clean_name, i - j);
133 
134   g_free (clean_name);
135 
136   return new_gsi;
137 }
138 
139 
140 static gint
compare_two_gsi(gconstpointer a,gconstpointer b)141 compare_two_gsi (gconstpointer a, gconstpointer b)
142 {
143   GladeStockItem *gsi1 = (GladeStockItem *) a;
144   GladeStockItem *gsi2 = (GladeStockItem *) b;
145 
146   return strcmp (gsi1->clean_name, gsi2->clean_name);
147 }
148 
149 static GArray *
list_stock_items(gboolean include_images)150 list_stock_items (gboolean include_images)
151 {
152   GtkStockItem item;
153   GSList *l = NULL, *stock_list = NULL, *p = NULL;
154   gchar *stock_id = NULL, *prefix = NULL;
155   gint stock_enum = 0, i = 0;
156   GEnumValue value;
157   GArray *values = NULL;
158   GladeStockItem *gsi;
159   GSList *gsi_list = NULL;
160   GSList *gsi_list_list = NULL;
161 
162   if (gdk_display_get_default () == NULL)
163     {
164       values = g_array_sized_new (TRUE, TRUE, sizeof (GEnumValue), 1);
165 
166       value.value = 0;
167       value.value_name = "dummy";
168       value.value_nick = "Dummy";
169       g_array_append_val (values, value);
170 
171       return values;
172     }
173 
174 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
175   stock_list = g_slist_reverse (gtk_stock_list_ids ());
176 G_GNUC_END_IGNORE_DEPRECATIONS
177 
178   values = g_array_sized_new (TRUE, TRUE, sizeof (GEnumValue),
179                               g_slist_length (stock_list));
180 
181   /* We want gtk+ stock items to appear first */
182   if ((stock_prefixs && strcmp (stock_prefixs->data, "gtk-")) ||
183       stock_prefixs == NULL)
184     stock_prefixs = g_slist_prepend (stock_prefixs, g_strdup ("gtk-"));
185 
186   for (p = stock_prefixs; p; p = g_slist_next (p))
187     {
188       prefix = p->data;
189 
190       for (l = stock_list; l; l = g_slist_next (l))
191         {
192           stock_id = l->data;
193 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
194           if (g_str_has_prefix (stock_id, prefix) == FALSE ||
195               gtk_stock_lookup (stock_id, &item) == FALSE)
196             continue;
197 G_GNUC_END_IGNORE_DEPRECATIONS
198 
199           gsi = new_from_values (item.label, stock_id, stock_enum++);
200           gsi_list =
201               g_slist_insert_sorted (gsi_list, gsi,
202                                      (GCompareFunc) compare_two_gsi);
203         }
204 
205       gsi_list_list = g_slist_append (gsi_list_list, gsi_list);
206       gsi_list = NULL;
207 
208       /* Images are appended after the gtk+ group of items */
209       if (include_images && !strcmp (prefix, "gtk-"))
210         {
211           for (i = 0; i < G_N_ELEMENTS (builtin_stock_images); i++)
212             {
213               gsi =
214                   new_from_values (builtin_stock_images[i],
215                                    builtin_stock_images[i], stock_enum++);
216               gsi_list =
217                   g_slist_insert_sorted (gsi_list, gsi,
218                                          (GCompareFunc) compare_two_gsi);
219             }
220           gsi_list_list = g_slist_append (gsi_list_list, gsi_list);
221           gsi_list = NULL;
222         }
223     }
224 
225   for (p = gsi_list_list; p; p = g_slist_next (p))
226     {
227 
228       for (l = (GSList *) p->data; l; l = g_slist_next (l))
229         {
230           gsi = (GladeStockItem *) l->data;
231           value.value = gsi->value;
232           value.value_name = g_strdup (gsi->value_name);
233           value.value_nick = g_strdup (gsi->value_nick);
234           values = g_array_append_val (values, value);
235 
236           g_free (gsi->value_nick);
237           g_free (gsi->value_name);
238           g_free (gsi->clean_name);
239           g_free (gsi);
240         }
241       g_slist_free ((GSList *) p->data);
242     }
243 
244   g_slist_free (gsi_list_list);
245 
246   stock_prefixs_done = TRUE;
247   g_slist_free (stock_list);
248 
249   return values;
250 }
251 
252 static gchar *
clean_stock_name(const gchar * name)253 clean_stock_name (const gchar * name)
254 {
255   gchar *clean_name, *str;
256   size_t len = 0;
257   guint i = 0;
258   guint j = 0;
259 
260   g_assert (name && name[0]);
261 
262   str = g_strdup (name);
263   len = strlen (str);
264 
265   while (i + j <= len)
266     {
267       if (str[i + j] == '_')
268         j++;
269 
270       str[i] = str[i + j];
271       i++;
272     }
273   clean_name = g_strndup (str, i - j);
274   g_free (str);
275 
276   return clean_name;
277 }
278 
279 GType
glade_standard_stock_get_type(void)280 glade_standard_stock_get_type (void)
281 {
282   static GType etype = 0;
283 
284   if (etype == 0)
285     {
286       GArray *values = list_stock_items (FALSE);
287       gint i, n_values = values->len;
288       GEnumValue *enum_values = (GEnumValue *) values->data;
289       GtkStockItem item;
290 
291       etype = g_enum_register_static ("GladeStock",
292                                       (GEnumValue *) g_array_free (values,
293                                                                    FALSE));
294 
295       /* Register displayable by GType, i.e. after the types been created. */
296       for (i = 0; i < n_values; i++)
297         {
298 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
299           gboolean valid_item = gtk_stock_lookup (enum_values[i].value_nick, &item);
300 G_GNUC_END_IGNORE_DEPRECATIONS
301 
302           if (valid_item)
303             {
304               gchar *clean_name = clean_stock_name (item.label);
305 
306 	      if (!glade_get_displayable_value (etype, enum_values[i].value_nick))
307 		glade_register_translated_value (etype, enum_values[i].value_nick, clean_name);
308               g_free (clean_name);
309             }
310         }
311     }
312   return etype;
313 }
314 
315 
316 GType
glade_standard_stock_image_get_type(void)317 glade_standard_stock_image_get_type (void)
318 {
319   static GType etype = 0;
320 
321   if (etype == 0)
322     {
323       GArray *values = list_stock_items (TRUE);
324       gint i, n_values = values->len;
325       GEnumValue *enum_values = (GEnumValue *) values->data;
326       GtkStockItem item;
327 
328       etype = g_enum_register_static ("GladeStockImage",
329                                       (GEnumValue *) g_array_free (values,
330                                                                    FALSE));
331 
332       /* Register displayable by GType, i.e. after the types been created. */
333       for (i = 0; i < n_values; i++)
334         {
335 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
336           gboolean valid_item = gtk_stock_lookup (enum_values[i].value_nick, &item);
337 G_GNUC_END_IGNORE_DEPRECATIONS
338           if (valid_item)
339             {
340               gchar *clean_name = clean_stock_name (item.label);
341 
342               /* These are translated, we just cut out the mnemonic underscores */
343 	      if (!glade_get_displayable_value (etype, enum_values[i].value_nick))
344 		glade_register_translated_value (etype, enum_values[i].value_nick, clean_name);
345               g_free (clean_name);
346             }
347         }
348 
349       for (i = 0; i < G_N_ELEMENTS (builtin_stock_images); i++)
350         {
351           /* these ones are translated from glade */
352 	  if (!glade_get_displayable_value (etype, builtin_stock_images[i]))
353 	    glade_register_displayable_value (etype,
354 					      builtin_stock_images[i],
355 					      GETTEXT_PACKAGE,
356 					      builtin_stock_displayables[i]);
357         }
358     }
359   return etype;
360 }
361 
362 GParamSpec *
glade_standard_stock_spec(void)363 glade_standard_stock_spec (void)
364 {
365   return g_param_spec_enum ("stock", _("Stock"),
366                             _("A builtin stock item"),
367                             GLADE_TYPE_STOCK, 0, G_PARAM_READWRITE);
368 }
369 
370 GParamSpec *
glade_standard_stock_image_spec(void)371 glade_standard_stock_image_spec (void)
372 {
373   return g_param_spec_enum ("stock-image", _("Stock Image"),
374                             _("A builtin stock image"),
375                             GLADE_TYPE_STOCK_IMAGE, 0, G_PARAM_READWRITE);
376 }
377 
378 /****************************************************************
379  *  A GList boxed type used by GladeParamSpecObjects and        *
380  *  GladeParamSpecAccel (which is now in the glade-gtk backend) *
381  ****************************************************************/
382 GType
glade_glist_get_type(void)383 glade_glist_get_type (void)
384 {
385   static GType type_id = 0;
386 
387   if (!type_id)
388     type_id = g_boxed_type_register_static
389         ("GladeGList",
390          (GBoxedCopyFunc) g_list_copy, (GBoxedFreeFunc) g_list_free);
391   return type_id;
392 }
393 
394 /****************************************************************
395  *  Built-in GladeParamSpecObjects for object list properties   *
396  *  (Used as a pspec to desctibe an AtkRelationSet, but can     *
397  *  for any object list property)                               *
398  ****************************************************************/
399 static void
param_objects_init(GParamSpec * pspec)400 param_objects_init (GParamSpec * pspec)
401 {
402   GladeParamSpecObjects *ospec = GLADE_PARAM_SPEC_OBJECTS (pspec);
403   ospec->type = G_TYPE_OBJECT;
404 }
405 
406 static void
param_objects_set_default(GParamSpec * pspec,GValue * value)407 param_objects_set_default (GParamSpec * pspec, GValue * value)
408 {
409   if (value->data[0].v_pointer != NULL)
410     {
411       g_free (value->data[0].v_pointer);
412     }
413   value->data[0].v_pointer = NULL;
414 }
415 
416 static gboolean
param_objects_validate(GParamSpec * pspec,GValue * value)417 param_objects_validate (GParamSpec * pspec, GValue * value)
418 {
419   GladeParamSpecObjects *ospec = GLADE_PARAM_SPEC_OBJECTS (pspec);
420   GList *objects, *list, *toremove = NULL;
421   GObject *object;
422 
423   objects = value->data[0].v_pointer;
424 
425   for (list = objects; list; list = list->next)
426     {
427       object = list->data;
428 
429       if (!(G_OBJECT_TYPE (object) == ospec->type ||
430 	    g_type_is_a (G_OBJECT_TYPE (object), ospec->type)))
431         toremove = g_list_prepend (toremove, object);
432     }
433 
434   for (list = toremove; list; list = list->next)
435     {
436       object = list->data;
437       objects = g_list_remove (objects, object);
438     }
439   if (toremove)
440     g_list_free (toremove);
441 
442   value->data[0].v_pointer = objects;
443 
444   return toremove != NULL;
445 }
446 
447 static gint
param_objects_values_cmp(GParamSpec * pspec,const GValue * value1,const GValue * value2)448 param_objects_values_cmp (GParamSpec * pspec,
449                           const GValue * value1, const GValue * value2)
450 {
451   guint8 *p1 = value1->data[0].v_pointer;
452   guint8 *p2 = value2->data[0].v_pointer;
453 
454   /* not much to compare here, try to at least provide stable lesser/greater result */
455 
456   return p1 < p2 ? -1 : p1 > p2;
457 }
458 
459 GType
glade_param_objects_get_type(void)460 glade_param_objects_get_type (void)
461 {
462   static GType objects_type = 0;
463 
464   if (objects_type == 0)
465     {
466       static /* const */ GParamSpecTypeInfo pspec_info = {
467         sizeof (GladeParamSpecObjects), /* instance_size */
468         16,                     /* n_preallocs */
469         param_objects_init,     /* instance_init */
470         0xdeadbeef,             /* value_type, assigned further down */
471         NULL,                   /* finalize */
472         param_objects_set_default,      /* value_set_default */
473         param_objects_validate, /* value_validate */
474         param_objects_values_cmp,       /* values_cmp */
475       };
476       pspec_info.value_type = GLADE_TYPE_GLIST;
477 
478       objects_type = g_param_type_register_static
479           ("GladeParamObjects", &pspec_info);
480     }
481   return objects_type;
482 }
483 
484 GParamSpec *
glade_param_spec_objects(const gchar * name,const gchar * nick,const gchar * blurb,GType accepted_type,GParamFlags flags)485 glade_param_spec_objects (const gchar * name,
486                           const gchar * nick,
487                           const gchar * blurb,
488                           GType accepted_type, GParamFlags flags)
489 {
490   GladeParamSpecObjects *pspec;
491 
492   pspec = g_param_spec_internal (GLADE_TYPE_PARAM_OBJECTS,
493                                  name, nick, blurb, flags);
494 
495   pspec->type = accepted_type;
496   return G_PARAM_SPEC (pspec);
497 }
498 
499 void
glade_param_spec_objects_set_type(GladeParamSpecObjects * pspec,GType type)500 glade_param_spec_objects_set_type (GladeParamSpecObjects * pspec, GType type)
501 {
502   pspec->type = type;
503 }
504 
505 GType
glade_param_spec_objects_get_type(GladeParamSpecObjects * pspec)506 glade_param_spec_objects_get_type (GladeParamSpecObjects * pspec)
507 {
508   return pspec->type;
509 }
510 
511 /* This was developed for the purpose of holding a list
512  * of 'targets' in an AtkRelation (we are setting it up
513  * as a property)
514  */
515 GParamSpec *
glade_standard_objects_spec(void)516 glade_standard_objects_spec (void)
517 {
518   return glade_param_spec_objects ("objects", _("Objects"),
519                                    _("A list of objects"),
520                                    G_TYPE_OBJECT, G_PARAM_READWRITE);
521 }
522 
523 /* Pixbuf Type */
524 GParamSpec *
glade_standard_pixbuf_spec(void)525 glade_standard_pixbuf_spec (void)
526 {
527   return g_param_spec_object ("pixbuf", _("Image File Name"),
528                               _("Enter a filename, relative path or full path to "
529 				"load the image"), GDK_TYPE_PIXBUF,
530                               G_PARAM_READWRITE);
531 }
532 
533 /* GdkColor */
534 GParamSpec *
glade_standard_gdkcolor_spec(void)535 glade_standard_gdkcolor_spec (void)
536 {
537   return g_param_spec_boxed ("gdkcolor", _("Color"),
538                              _("A GDK color value"), GDK_TYPE_COLOR,
539                              G_PARAM_READWRITE);
540 }
541 
542 /****************************************************************
543  *                    Basic types follow                        *
544  ****************************************************************/
545 GParamSpec *
glade_standard_int_spec(void)546 glade_standard_int_spec (void)
547 {
548   return g_param_spec_int ("int", "Integer",
549                            "An integer value",
550                            G_MININT, G_MAXINT, 0, G_PARAM_READWRITE);
551 }
552 
553 GParamSpec *
glade_standard_uint_spec(void)554 glade_standard_uint_spec (void)
555 {
556   return g_param_spec_uint ("uint", "Unsigned Integer",
557                             "An unsigned integer value",
558                             0, G_MAXUINT, 0, G_PARAM_READWRITE);
559 }
560 
561 GParamSpec *
glade_standard_string_spec(void)562 glade_standard_string_spec (void)
563 {
564   return g_param_spec_string ("string", _("String"),
565                               _("An entry"), "", G_PARAM_READWRITE);
566 }
567 
568 GParamSpec *
glade_standard_strv_spec(void)569 glade_standard_strv_spec (void)
570 {
571   return g_param_spec_boxed ("strv", "Strv",
572                              "String array", G_TYPE_STRV, G_PARAM_READWRITE);
573 }
574 
575 GParamSpec *
glade_standard_float_spec(void)576 glade_standard_float_spec (void)
577 {
578   return g_param_spec_float ("float", "Float",
579                              "A floating point entry",
580                              0.0F, G_MAXFLOAT, 0.0F, G_PARAM_READWRITE);
581 }
582 
583 GParamSpec *
glade_standard_boolean_spec(void)584 glade_standard_boolean_spec (void)
585 {
586   return g_param_spec_boolean ("boolean", "Boolean",
587                                "A boolean value", FALSE, G_PARAM_READWRITE);
588 }
589