2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
17 * Chris Lahey <clahey@ximian.com>
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
25 * @include: e-util/e-util.h
46 #include <glib/gi18n.h>
47 #include <glib/gstdio.h>
53 #include <camel/camel.h>
54 #include <libedataserver/e-data-server-util.h>
55 #include <libedataserver/e-categories.h>
56 #include <libedataserver/e-source-list.h>
58 #include "filter/e-filter-option.h"
61 #include "e-util-private.h"
64 * e_get_accels_filename:
66 * Returns the name of the user data file containing custom keyboard
67 * accelerator specifications.
69 * Returns: filename for accelerator specifications
72 e_get_accels_filename (void)
74 static gchar
*filename
= NULL
;
76 if (G_UNLIKELY (filename
== NULL
)) {
77 const gchar
*config_dir
= e_get_user_config_dir ();
78 filename
= g_build_filename (config_dir
, "accels", NULL
);
86 * @parent: a parent #GtkWindow or %NULL
87 * @uri: the URI to show
89 * Launches the default application to show the given URI. The URI must
90 * be of a form understood by GIO. If the URI cannot be shown, it presents
91 * a dialog describing the error. The dialog is set as transient to @parent
92 * if @parent is non-%NULL.
95 e_show_uri (GtkWindow
*parent
,
99 GdkScreen
*screen
= NULL
;
100 GError
*error
= NULL
;
103 g_return_if_fail (uri
!= NULL
);
105 timestamp
= gtk_get_current_event_time ();
108 screen
= gtk_widget_get_screen (GTK_WIDGET (parent
));
110 if (gtk_show_uri (screen
, uri
, timestamp
, &error
))
113 dialog
= gtk_message_dialog_new_with_markup (
114 parent
, GTK_DIALOG_DESTROY_WITH_PARENT
,
115 GTK_MESSAGE_ERROR
, GTK_BUTTONS_OK
,
116 "<big><b>%s</b></big>",
117 _("Could not open the link."));
119 gtk_message_dialog_format_secondary_text (
120 GTK_MESSAGE_DIALOG (dialog
), "%s", error
->message
);
122 gtk_dialog_run (GTK_DIALOG (dialog
));
124 gtk_widget_destroy (dialog
);
125 g_error_free (error
);
130 * @parent: a parent #GtkWindow or %NULL
131 * @link_id: help section to present or %NULL
133 * Opens the user documentation to the section given by @link_id, or to the
134 * table of contents if @link_id is %NULL. If the user documentation cannot
135 * be opened, it presents a dialog describing the error. The dialog is set
136 * as transient to @parent if @parent is non-%NULL.
139 e_display_help (GtkWindow
*parent
,
140 const gchar
*link_id
)
144 GdkScreen
*screen
= NULL
;
145 GError
*error
= NULL
;
148 uri
= g_string_new ("ghelp:" PACKAGE
);
149 timestamp
= gtk_get_current_event_time ();
152 screen
= gtk_widget_get_screen (GTK_WIDGET (parent
));
155 g_string_append_printf (uri
, "?%s", link_id
);
157 if (gtk_show_uri (screen
, uri
->str
, timestamp
, &error
))
160 dialog
= gtk_message_dialog_new_with_markup (
161 parent
, GTK_DIALOG_DESTROY_WITH_PARENT
,
162 GTK_MESSAGE_ERROR
, GTK_BUTTONS_OK
,
163 "<big><b>%s</b></big>",
164 _("Could not display help for Evolution."));
166 gtk_message_dialog_format_secondary_text (
167 GTK_MESSAGE_DIALOG (dialog
), "%s", error
->message
);
169 gtk_dialog_run (GTK_DIALOG (dialog
));
171 gtk_widget_destroy (dialog
);
172 g_error_free (error
);
175 g_string_free (uri
, TRUE
);
180 * @ui_manager: a #GtkUIManager
181 * @action_name: the name of an action
183 * Returns the first #GtkAction named @action_name by traversing the
184 * list of action groups in @ui_manager. If no such action exists, the
185 * function emits a critical warning before returning %NULL, since this
186 * probably indicates a programming error and most code is not prepared
187 * to deal with lookup failures.
189 * Returns: the first #GtkAction named @action_name
192 e_lookup_action (GtkUIManager
*ui_manager
,
193 const gchar
*action_name
)
195 GtkAction
*action
= NULL
;
198 g_return_val_if_fail (GTK_IS_UI_MANAGER (ui_manager
), NULL
);
199 g_return_val_if_fail (action_name
!= NULL
, NULL
);
201 iter
= gtk_ui_manager_get_action_groups (ui_manager
);
203 while (iter
!= NULL
) {
204 GtkActionGroup
*action_group
= iter
->data
;
206 action
= gtk_action_group_get_action (
207 action_group
, action_name
);
211 iter
= g_list_next (iter
);
214 g_critical ("%s: action '%s' not found", G_STRFUNC
, action_name
);
220 * e_lookup_action_group:
221 * @ui_manager: a #GtkUIManager
222 * @group_name: the name of an action group
224 * Returns the #GtkActionGroup in @ui_manager named @group_name. If no
225 * such action group exists, the function emits a critical warnings before
226 * returning %NULL, since this probably indicates a programming error and
227 * most code is not prepared to deal with lookup failures.
229 * Returns: the #GtkActionGroup named @group_name
232 e_lookup_action_group (GtkUIManager
*ui_manager
,
233 const gchar
*group_name
)
237 g_return_val_if_fail (GTK_IS_UI_MANAGER (ui_manager
), NULL
);
238 g_return_val_if_fail (group_name
!= NULL
, NULL
);
240 iter
= gtk_ui_manager_get_action_groups (ui_manager
);
242 while (iter
!= NULL
) {
243 GtkActionGroup
*action_group
= iter
->data
;
246 name
= gtk_action_group_get_name (action_group
);
247 if (strcmp (name
, group_name
) == 0)
250 iter
= g_list_next (iter
);
253 g_critical ("%s: action group '%s' not found", G_STRFUNC
, group_name
);
259 * e_builder_get_widget:
260 * @builder: a #GtkBuilder
261 * @widget_name: name of a widget in @builder
263 * Gets the widget named @widget_name. Note that this function does not
264 * increment the reference count of the returned widget. If @widget_name
265 * could not be found in the @builder<!-- -->'s object tree, a run-time
266 * warning is emitted since this usually indicates a programming error.
268 * This is a convenience function to work around the awkwardness of
269 * #GtkBuilder returning #GObject pointers, when the vast majority of
270 * the time you want a #GtkWidget pointer.
272 * If you need something from @builder other than a #GtkWidget, or you
273 * want to test for the existence of some widget name without incurring
274 * a run-time warning, use gtk_builder_get_object().
276 * Returns: the widget named @widget_name, or %NULL
279 e_builder_get_widget (GtkBuilder
*builder
,
280 const gchar
*widget_name
)
284 g_return_val_if_fail (GTK_IS_BUILDER (builder
), NULL
);
285 g_return_val_if_fail (widget_name
!= NULL
, NULL
);
287 object
= gtk_builder_get_object (builder
, widget_name
);
288 if (object
== NULL
) {
289 g_warning ("Could not find widget '%s'", widget_name
);
293 return GTK_WIDGET (object
);
297 * e_load_ui_builder_definition:
298 * @builder: a #GtkBuilder
299 * @basename: basename of the UI definition file
301 * Loads a UI definition into @builder from Evolution's UI directory.
302 * Failure here is fatal, since the application can't function without
303 * its UI definitions.
306 e_load_ui_builder_definition (GtkBuilder
*builder
,
307 const gchar
*basename
)
310 GError
*error
= NULL
;
312 g_return_if_fail (GTK_IS_BUILDER (builder
));
313 g_return_if_fail (basename
!= NULL
);
315 filename
= g_build_filename (EVOLUTION_UIDIR
, basename
, NULL
);
316 gtk_builder_add_from_file (builder
, filename
, &error
);
320 g_error ("%s: %s", basename
, error
->message
);
321 g_assert_not_reached ();
326 * e_action_compare_by_label:
327 * @action1: a #GtkAction
328 * @action2: a #GtkAction
330 * Compares the labels for @action1 and @action2 using g_utf8_collate().
332 * Returns: < 0 if @action1 compares before @action2, 0 if they
333 * compare equal, > 0 if @action1 compares after @action2
336 e_action_compare_by_label (GtkAction
*action1
,
343 /* XXX This is horribly inefficient but will generally only be
344 * used on short lists of actions during UI construction. */
346 if (action1
== action2
)
349 g_object_get (action1
, "label", &label1
, NULL
);
350 g_object_get (action2
, "label", &label2
, NULL
);
352 result
= g_utf8_collate (label1
, label2
);
361 * e_action_group_remove_all_actions:
362 * @action_group: a #GtkActionGroup
364 * Removes all actions from the action group.
367 e_action_group_remove_all_actions (GtkActionGroup
*action_group
)
371 /* XXX I've proposed this function for inclusion in GTK+.
372 * GtkActionGroup stores actions in an internal hash
373 * table and can do this more efficiently by calling
374 * g_hash_table_remove_all().
376 * http://bugzilla.gnome.org/show_bug.cgi?id=550485 */
378 g_return_if_fail (GTK_IS_ACTION_GROUP (action_group
));
380 list
= gtk_action_group_list_actions (action_group
);
381 for (iter
= list
; iter
!= NULL
; iter
= iter
->next
)
382 gtk_action_group_remove_action (action_group
, iter
->data
);
387 * e_radio_action_get_current_action:
388 * @radio_action: a #GtkRadioAction
390 * Returns the currently active member of the group to which @radio_action
393 * Returns: the currently active group member
396 e_radio_action_get_current_action (GtkRadioAction
*radio_action
)
401 g_return_val_if_fail (GTK_IS_RADIO_ACTION (radio_action
), NULL
);
403 group
= gtk_radio_action_get_group (radio_action
);
404 current_value
= gtk_radio_action_get_current_value (radio_action
);
406 while (group
!= NULL
) {
409 radio_action
= GTK_RADIO_ACTION (group
->data
);
410 g_object_get (radio_action
, "value", &value
, NULL
);
412 if (value
== current_value
)
415 group
= g_slist_next (group
);
421 /* Helper for e_categories_add_change_hook() */
423 categories_changed_cb (GObject
*useless_opaque_object
,
424 GHookList
*hook_list
)
426 /* e_categories_register_change_listener() is broken because
427 * it requires callbacks to allow for some opaque GObject as
428 * the first argument (not does it document this). */
429 g_hook_list_invoke (hook_list
, FALSE
);
432 /* Helper for e_categories_add_change_hook() */
434 categories_weak_notify_cb (GHookList
*hook_list
,
435 gpointer where_the_object_was
)
439 /* This should not happen, but if we fail to find the hook for
440 * some reason, g_hook_destroy_link() will warn about the NULL
441 * pointer, which is all we would do anyway so no need to test
442 * for it ourselves. */
443 hook
= g_hook_find_data (hook_list
, TRUE
, where_the_object_was
);
444 g_hook_destroy_link (hook_list
, hook
);
448 * e_categories_add_change_hook:
449 * @func: a hook function
450 * @object: a #GObject to be passed to @func, or %NULL
452 * A saner alternative to e_categories_register_change_listener().
454 * Adds a hook function to be called when a category is added, removed or
455 * modified. If @object is not %NULL, the hook function is automatically
456 * removed when @object is finalized.
459 e_categories_add_change_hook (GHookFunc func
,
462 static gboolean initialized
= FALSE
;
463 static GHookList hook_list
;
466 g_return_if_fail (func
!= NULL
);
469 g_return_if_fail (G_IS_OBJECT (object
));
472 g_hook_list_init (&hook_list
, sizeof (GHook
));
473 e_categories_register_change_listener (
474 G_CALLBACK (categories_changed_cb
), &hook_list
);
478 hook
= g_hook_alloc (&hook_list
);
485 G_OBJECT (object
), (GWeakNotify
)
486 categories_weak_notify_cb
, &hook_list
);
488 g_hook_append (&hook_list
, hook
);
493 * @parent_type: the root #GType to traverse from
494 * @func: the function to call for each visited #GType
495 * @user_data: user data to pass to the function
497 * Calls @func for all instantiable subtypes of @parent_type.
499 * This is often useful for extending functionality by way of #EModule.
500 * A module may register a subtype of @parent_type in its e_module_load()
501 * function. Then later on the application will call e_type_traverse()
502 * to instantiate all registered subtypes of @parent_type.
505 e_type_traverse (GType parent_type
,
510 guint n_children
, ii
;
512 g_return_if_fail (func
!= NULL
);
514 children
= g_type_children (parent_type
, &n_children
);
516 for (ii
= 0; ii
< n_children
; ii
++) {
517 GType type
= children
[ii
];
519 /* Recurse over the child's children. */
520 e_type_traverse (type
, func
, user_data
);
522 /* Skip abstract types. */
523 if (G_TYPE_IS_ABSTRACT (type
))
526 func (type
, user_data
);
533 * e_str_without_underscores:
534 * @string: the string to strip underscores from
536 * Strips underscores from a string in the same way
537 * @gtk_label_new_with_mnemonics does. The returned string should be freed
540 * Returns: a newly-allocated string without underscores
543 e_str_without_underscores (const gchar
*string
)
549 new_string
= g_malloc (strlen (string
) + 1);
552 for (sp
= string
; *sp
!= '\0'; sp
++) {
556 } else if (sp
[1] == '_') {
557 /* Translate "__" in "_". */
569 e_str_compare (gconstpointer x
, gconstpointer y
)
571 if (x
== NULL
|| y
== NULL
) {
578 return strcmp (x
, y
);
582 e_str_case_compare (gconstpointer x
, gconstpointer y
)
587 if (x
== NULL
|| y
== NULL
) {
594 cx
= g_utf8_casefold (x
, -1);
595 cy
= g_utf8_casefold (y
, -1);
597 res
= g_utf8_collate (cx
, cy
);
606 e_collate_compare (gconstpointer x
, gconstpointer y
)
608 if (x
== NULL
|| y
== NULL
) {
615 return g_utf8_collate (x
, y
);
619 e_int_compare (gconstpointer x
, gconstpointer y
)
621 gint nx
= GPOINTER_TO_INT (x
);
622 gint ny
= GPOINTER_TO_INT (y
);
624 return (nx
== ny
) ? 0 : (nx
< ny
) ? -1 : 1;
629 * @color: a #GdkColor
631 * Converts a #GdkColor to a 24-bit RGB color value.
633 * Returns: a 24-bit color value
636 e_color_to_value (GdkColor
*color
)
642 g_return_val_if_fail (color
!= NULL
, 0);
644 red
= color
->red
>> 8;
645 green
= color
->green
>> 8;
646 blue
= color
->blue
>> 8;
648 return (guint32
) (((red
<< 16) | (green
<< 8) | blue
) & 0xffffff);
663 e_format_number (gint number
)
665 GList
*iterator
, *list
= NULL
;
666 struct lconv
*locality
;
667 gint char_length
= 0;
668 gint group_count
= 0;
673 gchar
*value_iterator
;
675 locality
= localeconv ();
676 grouping
= locality
->grouping
;
681 last_count
= *grouping
;
684 divider
= epow10 (last_count
);
685 if (number
>= divider
) {
686 group
= g_strdup_printf (
687 "%0*d", last_count
, number
% divider
);
689 group
= g_strdup_printf (
690 "%d", number
% divider
);
695 group
= g_strdup_printf("%d", number
);
699 char_length
+= strlen (group
);
700 list
= g_list_prepend (list
, group
);
706 gchar
, 1 + char_length
+ (group_count
- 1) *
707 strlen (locality
->thousands_sep
));
710 value_iterator
= value
;
712 strcpy (value_iterator
, iterator
->data
);
713 value_iterator
+= strlen (iterator
->data
);
714 for (iterator
= iterator
->next
; iterator
; iterator
= iterator
->next
) {
715 strcpy (value_iterator
, locality
->thousands_sep
);
716 value_iterator
+= strlen (locality
->thousands_sep
);
718 strcpy (value_iterator
, iterator
->data
);
719 value_iterator
+= strlen (iterator
->data
);
721 g_list_foreach (list
, (GFunc
) g_free
, NULL
);
725 return g_strdup("0");
729 /* Perform a binary search for key in base which has nmemb elements
730 of size bytes each. The comparisons are done by (*compare)(). */
732 e_bsearch (gconstpointer key
,
736 ESortCompareFunc compare
,
751 p
= (((const gchar
*) base
) + (idx
* size
));
752 comparison
= (*compare
) (key
, p
, closure
);
755 else if (comparison
> 0)
764 p
= (((const gchar
*) base
) + (idx
* size
));
765 comparison
= (*compare
) (key
, p
, closure
);
779 p
= (((const gchar
*) base
) + (idx
* size
));
780 comparison
= (*compare
) (key
, p
, closure
);
798 /* Function to do a last minute fixup of the AM/PM stuff if the locale
799 * and gettext haven't done it right. Most English speaking countries
800 * except the USA use the 24 hour clock (UK, Australia etc). However
801 * since they are English nobody bothers to write a language
802 * translation (gettext) file. So the locale turns off the AM/PM, but
803 * gettext does not turn on the 24 hour clock. Leaving a mess.
805 * This routine checks if AM/PM are defined in the locale, if not it
806 * forces the use of the 24 hour clock.
808 * The function itself is a front end on strftime and takes exactly
809 * the same arguments.
811 * TODO: Actually remove the '%p' from the fixed up string so that
812 * there isn't a stray space.
816 e_strftime_fix_am_pm (gchar
*str
, gsize max
, const gchar
*fmt
,
824 if (strstr(fmt
, "%p")==NULL
&& strstr(fmt
, "%P")==NULL
) {
825 /* No AM/PM involved - can use the fmt string directly */
826 ret
=e_strftime (str
, max
, fmt
, tm
);
828 /* Get the AM/PM symbol from the locale */
829 e_strftime (buf
, 10, "%p", tm
);
832 /* AM/PM have been defined in the locale
833 * so we can use the fmt string directly. */
834 ret
=e_strftime (str
, max
, fmt
, tm
);
836 /* No AM/PM defined by locale
837 * must change to 24 hour clock. */
839 for (sp
=ffmt
; (sp
=strstr(sp
, "%l")); sp
++) {
840 /* Maybe this should be 'k', but I have never
841 * seen a 24 clock actually use that format. */
844 for (sp
=ffmt
; (sp
=strstr(sp
, "%I")); sp
++) {
847 ret
=e_strftime (str
, max
, ffmt
, tm
);
856 e_utf8_strftime_fix_am_pm (gchar
*str
, gsize max
, const gchar
*fmt
,
860 gchar
*locale_fmt
, *buf
;
862 locale_fmt
= g_locale_from_utf8 (fmt
, -1, NULL
, &sz
, NULL
);
866 ret
= e_strftime_fix_am_pm (str
, max
, locale_fmt
, tm
);
872 buf
= g_locale_to_utf8 (str
, ret
, NULL
, &sz
, NULL
);
879 gchar
*tmp
= buf
+ max
- 1;
880 tmp
= g_utf8_find_prev_char (buf
, tmp
);
886 memcpy (str
, buf
, sz
);
895 * @month: month index
896 * @abbreviated: if %TRUE, abbreviate the month name
898 * Returns the localized name for @month. If @abbreviated is %TRUE,
899 * returns the locale's abbreviated month name.
901 * Returns: localized month name
904 e_get_month_name (GDateMonth month
,
905 gboolean abbreviated
)
907 /* Make the indices correspond to the enum values. */
908 static const gchar
*abbr_names
[G_DATE_DECEMBER
+ 1];
909 static const gchar
*full_names
[G_DATE_DECEMBER
+ 1];
910 static gboolean first_time
= TRUE
;
912 g_return_val_if_fail (month
>= G_DATE_JANUARY
, NULL
);
913 g_return_val_if_fail (month
<= G_DATE_DECEMBER
, NULL
);
915 if (G_UNLIKELY (first_time
)) {
920 memset (abbr_names
, 0, sizeof (abbr_names
));
921 memset (full_names
, 0, sizeof (full_names
));
923 /* First Julian day was in January. */
924 g_date_set_julian (&date
, 1);
926 for (ii
= G_DATE_JANUARY
; ii
<= G_DATE_DECEMBER
; ii
++) {
927 g_date_strftime (buffer
, sizeof (buffer
), "%b", &date
);
928 abbr_names
[ii
] = g_intern_string (buffer
);
929 g_date_strftime (buffer
, sizeof (buffer
), "%B", &date
);
930 full_names
[ii
] = g_intern_string (buffer
);
931 g_date_add_months (&date
, 1);
937 return abbreviated
? abbr_names
[month
] : full_names
[month
];
941 * e_get_weekday_name:
942 * @weekday: weekday index
943 * @abbreviated: if %TRUE, abbreviate the weekday name
945 * Returns the localized name for @weekday. If @abbreviated is %TRUE,
946 * returns the locale's abbreviated weekday name.
948 * Returns: localized weekday name
951 e_get_weekday_name (GDateWeekday weekday
,
952 gboolean abbreviated
)
954 /* Make the indices correspond to the enum values. */
955 static const gchar
*abbr_names
[G_DATE_SUNDAY
+ 1];
956 static const gchar
*full_names
[G_DATE_SUNDAY
+ 1];
957 static gboolean first_time
= TRUE
;
959 g_return_val_if_fail (weekday
>= G_DATE_MONDAY
, NULL
);
960 g_return_val_if_fail (weekday
<= G_DATE_SUNDAY
, NULL
);
962 if (G_UNLIKELY (first_time
)) {
967 memset (abbr_names
, 0, sizeof (abbr_names
));
968 memset (full_names
, 0, sizeof (full_names
));
970 /* First Julian day was a Monday. */
971 g_date_set_julian (&date
, 1);
973 for (ii
= G_DATE_MONDAY
; ii
<= G_DATE_SUNDAY
; ii
++) {
974 g_date_strftime (buffer
, sizeof (buffer
), "%a", &date
);
975 abbr_names
[ii
] = g_intern_string (buffer
);
976 g_date_strftime (buffer
, sizeof (buffer
), "%A", &date
);
977 full_names
[ii
] = g_intern_string (buffer
);
978 g_date_add_days (&date
, 1);
984 return abbreviated
? abbr_names
[weekday
] : full_names
[weekday
];
989 * @nptr: the string to convert to a numeric value.
990 * @endptr: if non-NULL, it returns the character after
991 * the last character used in the conversion.
993 * Converts a string to a gdouble value. This function detects
994 * strings either in the standard C locale or in the current locale.
996 * This function is typically used when reading configuration files or
997 * other non-user input that should not be locale dependent, but may
998 * have been in the past. To handle input from the user you should
999 * normally use the locale-sensitive system strtod function.
1001 * To convert from a double to a string in a locale-insensitive way, use
1004 * Returns: the gdouble value
1007 e_flexible_strtod (const gchar
*nptr
, gchar
**endptr
)
1011 struct lconv
*locale_data
;
1012 const gchar
*decimal_point
;
1013 gint decimal_point_len
;
1014 const gchar
*p
, *decimal_point_pos
;
1015 const gchar
*end
= NULL
; /* Silence gcc */
1018 g_return_val_if_fail (nptr
!= NULL
, 0);
1022 locale_data
= localeconv ();
1023 decimal_point
= locale_data
->decimal_point
;
1024 decimal_point_len
= strlen (decimal_point
);
1026 g_return_val_if_fail (decimal_point_len
!= 0, 0);
1028 decimal_point_pos
= NULL
;
1029 if (!strcmp (decimal_point
, "."))
1030 return strtod (nptr
, endptr
);
1034 /* Skip leading space */
1035 while (isspace ((guchar
)*p
))
1038 /* Skip leading optional sign */
1039 if (*p
== '+' || *p
== '-')
1043 (p
[1] == 'x' || p
[1] == 'X')) {
1045 /* HEX - find the (optional) decimal point */
1047 while (isxdigit ((guchar
)*p
))
1051 decimal_point_pos
= p
++;
1053 while (isxdigit ((guchar
)*p
))
1056 if (*p
== 'p' || *p
== 'P')
1058 if (*p
== '+' || *p
== '-')
1060 while (isdigit ((guchar
)*p
))
1063 } else if (strncmp (p
, decimal_point
, decimal_point_len
) == 0) {
1064 return strtod (nptr
, endptr
);
1067 while (isdigit ((guchar
)*p
))
1071 decimal_point_pos
= p
++;
1073 while (isdigit ((guchar
)*p
))
1076 if (*p
== 'e' || *p
== 'E')
1078 if (*p
== '+' || *p
== '-')
1080 while (isdigit ((guchar
)*p
))
1083 } else if (strncmp (p
, decimal_point
, decimal_point_len
) == 0) {
1084 return strtod (nptr
, endptr
);
1087 /* For the other cases, we need not convert the decimal point */
1089 if (!decimal_point_pos
)
1090 return strtod (nptr
, endptr
);
1092 /* We need to convert the '.' to the locale specific decimal point */
1093 copy
= g_malloc (end
- nptr
+ 1 + decimal_point_len
);
1096 memcpy (c
, nptr
, decimal_point_pos
- nptr
);
1097 c
+= decimal_point_pos
- nptr
;
1098 memcpy (c
, decimal_point
, decimal_point_len
);
1099 c
+= decimal_point_len
;
1100 memcpy (c
, decimal_point_pos
+ 1, end
- (decimal_point_pos
+ 1));
1101 c
+= end
- (decimal_point_pos
+ 1);
1104 val
= strtod (copy
, &fail_pos
);
1107 if (fail_pos
> decimal_point_pos
)
1109 (gchar
*) nptr
+ (fail_pos
- copy
) -
1110 (decimal_point_len
- 1);
1112 fail_pos
= (gchar
*) nptr
+ (fail_pos
- copy
);
1125 * @buffer: A buffer to place the resulting string in
1126 * @buf_len: The length of the buffer.
1127 * @format: The printf-style format to use for the
1128 * code to use for converting.
1129 * @d: The double to convert
1131 * Converts a double to a string, using the '.' as
1132 * decimal_point. To format the number you pass in
1133 * a printf-style formating string. Allowed conversion
1134 * specifiers are eEfFgG.
1136 * If you want to generates enough precision that converting
1137 * the string back using @g_strtod gives the same machine-number
1138 * (on machines with IEEE compatible 64bit doubles) use the format
1139 * string "%.17g". If you do this it is guaranteed that the size
1140 * of the resulting string will never be larger than
1141 * @G_ASCII_DTOSTR_BUF_SIZE bytes.
1143 * Returns: the pointer to the buffer with the converted string
1146 e_ascii_dtostr (gchar
*buffer
, gint buf_len
, const gchar
*format
, gdouble d
)
1148 struct lconv
*locale_data
;
1149 const gchar
*decimal_point
;
1150 gint decimal_point_len
;
1155 g_return_val_if_fail (buffer
!= NULL
, NULL
);
1156 g_return_val_if_fail (format
[0] == '%', NULL
);
1157 g_return_val_if_fail (strpbrk (format
+ 1, "'l%") == NULL
, NULL
);
1159 format_char
= format
[strlen (format
) - 1];
1161 g_return_val_if_fail (format_char
== 'e' || format_char
== 'E' ||
1162 format_char
== 'f' || format_char
== 'F' ||
1163 format_char
== 'g' || format_char
== 'G',
1166 if (format
[0] != '%')
1169 if (strpbrk (format
+ 1, "'l%"))
1172 if (!(format_char
== 'e' || format_char
== 'E' ||
1173 format_char
== 'f' || format_char
== 'F' ||
1174 format_char
== 'g' || format_char
== 'G'))
1177 g_snprintf (buffer
, buf_len
, format
, d
);
1179 locale_data
= localeconv ();
1180 decimal_point
= locale_data
->decimal_point
;
1181 decimal_point_len
= strlen (decimal_point
);
1183 g_return_val_if_fail (decimal_point_len
!= 0, NULL
);
1185 if (strcmp (decimal_point
, ".")) {
1188 if (*p
== '+' || *p
== '-')
1191 while (isdigit ((guchar
)*p
))
1194 if (strncmp (p
, decimal_point
, decimal_point_len
) == 0) {
1197 if (decimal_point_len
> 1) {
1198 rest_len
= strlen (p
+ (decimal_point_len
-1));
1199 memmove (p
, p
+ (decimal_point_len
-1),
1209 /* Evolution Locks for crash recovery */
1211 static const gchar
*
1212 get_lock_filename (void)
1214 static gchar
*filename
= NULL
;
1216 if (G_UNLIKELY (filename
== NULL
))
1217 filename
= g_build_filename (
1218 e_get_user_config_dir (), ".running", NULL
);
1224 e_file_lock_create (void)
1226 const gchar
*filename
= get_lock_filename ();
1227 gboolean status
= FALSE
;
1230 file
= g_fopen (filename
, "w");
1232 /* The lock file also serves as a PID file. */
1234 file
, "%" G_GINT64_FORMAT
"\n",
1235 (gint64
) getpid ());
1239 const gchar
*errmsg
= g_strerror (errno
);
1240 g_warning ("Lock file creation failed: %s", errmsg
);
1247 e_file_lock_destroy (void)
1249 const gchar
*filename
= get_lock_filename ();
1251 if (g_unlink (filename
) == -1) {
1252 const gchar
*errmsg
= g_strerror (errno
);
1253 g_warning ("Lock file deletion failed: %s", errmsg
);
1258 e_file_lock_exists (void)
1260 const gchar
*filename
= get_lock_filename ();
1262 return g_file_test (filename
, G_FILE_TEST_EXISTS
);
1266 * e_util_guess_mime_type:
1267 * @filename: a local file name, or URI
1268 * @localfile: %TRUE to check the file content, FALSE to check only the name
1270 * Tries to determine the MIME type for @filename. Free the returned
1271 * string with g_free().
1273 * Returns: the MIME type of @filename, or %NULL if the the MIME type could
1277 e_util_guess_mime_type (const gchar
*filename
, gboolean localfile
)
1279 gchar
*mime_type
= NULL
;
1281 g_return_val_if_fail (filename
!= NULL
, NULL
);
1287 if (strstr (filename
, "://"))
1288 file
= g_file_new_for_uri (filename
);
1290 file
= g_file_new_for_path (filename
);
1292 fi
= g_file_query_info (
1293 file
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
,
1294 G_FILE_QUERY_INFO_NONE
, NULL
, NULL
);
1296 mime_type
= g_content_type_get_mime_type (
1297 g_file_info_get_content_type (fi
));
1298 g_object_unref (fi
);
1301 g_object_unref (file
);
1305 /* file doesn't exists locally, thus guess based on the filename */
1306 gboolean uncertain
= FALSE
;
1307 gchar
*content_type
;
1309 content_type
= g_content_type_guess (filename
, NULL
, 0, &uncertain
);
1311 mime_type
= g_content_type_get_mime_type (content_type
);
1312 g_free (content_type
);
1319 /* XXX: Should e-util/ really depend on filter/ ?? */
1321 e_util_get_category_filter_options (void)
1326 clist
= e_categories_get_list ();
1327 for (l
= clist
; l
; l
= l
->next
) {
1328 const gchar
*cname
= l
->data
;
1329 struct _filter_option
*fo
;
1331 if (!e_categories_is_searchable (cname
))
1334 fo
= g_new0 (struct _filter_option
, 1);
1336 fo
->title
= g_strdup (cname
);
1337 fo
->value
= g_strdup (cname
);
1338 res
= g_slist_prepend (res
, fo
);
1341 g_list_free (clist
);
1343 return g_slist_reverse (res
);
1347 * e_util_get_searchable_categories:
1349 * Returns list of searchable categories only. The list should
1350 * be freed with g_list_free() when done with it, but the items
1351 * are internal strings, names of categories, which should not
1352 * be touched in other than read-only way, in other words the same
1353 * restrictions as for e_categories_get_list() applies here too.
1356 e_util_get_searchable_categories (void)
1358 GList
*res
= NULL
, *all_categories
, *l
;
1360 all_categories
= e_categories_get_list ();
1361 for (l
= all_categories
; l
; l
= l
->next
) {
1362 const gchar
*cname
= l
->data
;
1364 if (e_categories_is_searchable (cname
))
1365 res
= g_list_prepend (res
, (gpointer
) cname
);
1368 g_list_free (all_categories
);
1370 return g_list_reverse (res
);
1374 * e_util_set_source_combo_box_list:
1375 * @source_combo_box: an #ESourceComboBox
1376 * @source_gconf_path: GConf path with sources to use in an #ESourceList
1378 * Sets an #ESourceList of a given GConf path to an #ESourceComboBox.
1381 e_util_set_source_combo_box_list (GtkWidget
*source_combo_box
,
1382 const gchar
*source_gconf_path
)
1384 ESourceList
*source_list
;
1385 GConfClient
*gconf_client
;
1387 g_return_if_fail (source_combo_box
!= NULL
);
1388 g_return_if_fail (source_gconf_path
!= NULL
);
1390 gconf_client
= gconf_client_get_default ();
1391 source_list
= e_source_list_new_for_gconf (
1392 gconf_client
, source_gconf_path
);
1393 g_object_set (source_combo_box
, "source-list", source_list
, NULL
);
1394 g_object_unref (source_list
);
1395 g_object_unref (gconf_client
);
1399 * e_binding_transform_color_to_string:
1400 * @binding: a #GBinding
1401 * @source_value: a #GValue of type #GDK_TYPE_COLOR
1402 * @target_value: a #GValue of type #G_TYPE_STRING
1403 * @not_used: not used
1405 * Transforms a #GdkColor value to a color string specification.
1407 * Returns: %TRUE always
1410 e_binding_transform_color_to_string (GBinding
*binding
,
1411 const GValue
*source_value
,
1412 GValue
*target_value
,
1415 const GdkColor
*color
;
1418 g_return_val_if_fail (G_IS_BINDING (binding
), FALSE
);
1420 color
= g_value_get_boxed (source_value
);
1421 string
= gdk_color_to_string (color
);
1422 g_value_set_string (target_value
, string
);
1429 * e_binding_transform_string_to_color:
1430 * @binding: a #GBinding
1431 * @source_value: a #GValue of type #G_TYPE_STRING
1432 * @target_value: a #GValue of type #GDK_TYPE_COLOR
1433 * @not_used: not used
1435 * Transforms a color string specification to a #GdkColor.
1437 * Returns: %TRUE if color string specification was valid
1440 e_binding_transform_string_to_color (GBinding
*binding
,
1441 const GValue
*source_value
,
1442 GValue
*target_value
,
1446 const gchar
*string
;
1447 gboolean success
= FALSE
;
1449 g_return_val_if_fail (G_IS_BINDING (binding
), FALSE
);
1451 string
= g_value_get_string (source_value
);
1452 if (gdk_color_parse (string
, &color
)) {
1453 g_value_set_boxed (target_value
, &color
);
1461 * e_binding_transform_enum_value_to_nick:
1462 * @binding: a #GBinding
1463 * @source_value: a #GValue whose type is derived from #G_TYPE_ENUM
1464 * @target_value: a #GValue of type #G_TYPE_STRING
1465 * @not_used: not used
1467 * Transforms an enumeration value to its corresponding nickname.
1469 * Returns: %TRUE if the enum value has a corresponding nickname
1472 e_binding_transform_enum_value_to_nick (GBinding
*binding
,
1473 const GValue
*source_value
,
1474 GValue
*target_value
,
1477 GEnumClass
*enum_class
;
1478 GEnumValue
*enum_value
;
1480 gboolean success
= FALSE
;
1482 g_return_val_if_fail (G_IS_BINDING (binding
), FALSE
);
1484 enum_class
= g_type_class_peek (G_VALUE_TYPE (source_value
));
1485 g_return_val_if_fail (G_IS_ENUM_CLASS (enum_class
), FALSE
);
1487 value
= g_value_get_enum (source_value
);
1488 enum_value
= g_enum_get_value (enum_class
, value
);
1489 if (enum_value
!= NULL
) {
1490 g_value_set_string (target_value
, enum_value
->value_nick
);
1498 * e_binding_transform_enum_nick_to_value:
1499 * @binding: a #GBinding
1500 * @source_value: a #GValue of type #G_TYPE_STRING
1501 * @target_value: a #GValue whose type is derived from #G_TYPE_ENUM
1502 * @not_used: not_used
1504 * Transforms an enumeration nickname to its corresponding value.
1506 * Returns: %TRUE if the enum nickname has a corresponding value
1509 e_binding_transform_enum_nick_to_value (GBinding
*binding
,
1510 const GValue
*source_value
,
1511 GValue
*target_value
,
1514 GEnumClass
*enum_class
;
1515 GEnumValue
*enum_value
;
1516 const gchar
*string
;
1517 gboolean success
= FALSE
;
1519 g_return_val_if_fail (G_IS_BINDING (binding
), FALSE
);
1521 enum_class
= g_type_class_peek (G_VALUE_TYPE (target_value
));
1522 g_return_val_if_fail (G_IS_ENUM_CLASS (enum_class
), FALSE
);
1524 string
= g_value_get_string (source_value
);
1525 enum_value
= g_enum_get_value_by_nick (enum_class
, string
);
1526 if (enum_value
!= NULL
) {
1527 g_value_set_enum (target_value
, enum_value
->value
);
1535 * e_binding_transform_source_to_uid:
1536 * @binding: a #GBinding
1537 * @source_value: a #GValue of type #E_TYPE_SOURCE
1538 * @target_value: a #GValue of type #G_TYPE_STRING
1539 * @source_list: an #ESourceList
1541 * Transforms an #ESource object to its UID string.
1543 * Returns: %TRUE if @source_value was an #ESource object
1546 e_binding_transform_source_to_uid (GBinding
*binding
,
1547 const GValue
*source_value
,
1548 GValue
*target_value
,
1549 ESourceList
*source_list
)
1552 const gchar
*string
;
1553 gboolean success
= FALSE
;
1555 g_return_val_if_fail (G_IS_BINDING (binding
), FALSE
);
1556 g_return_val_if_fail (E_IS_SOURCE_LIST (source_list
), FALSE
);
1558 source
= g_value_get_object (source_value
);
1559 if (E_IS_SOURCE (source
)) {
1560 string
= e_source_peek_uid (source
);
1561 g_value_set_string (target_value
, string
);
1569 * e_binding_transform_uid_to_source:
1570 * @binding: a #GBinding
1571 * @source_value: a #GValue of type #G_TYPE_STRING
1572 * @target_value: a #GValue of type #E_TYPE_SOURCe
1573 * @source_list: an #ESourceList
1575 * Transforms an #ESource UID string to the corresponding #ESource object
1578 * Returns: %TRUE if @source_list had an #ESource object with a matching
1582 e_binding_transform_uid_to_source (GBinding
*binding
,
1583 const GValue
*source_value
,
1584 GValue
*target_value
,
1585 ESourceList
*source_list
)
1588 const gchar
*string
;
1589 gboolean success
= FALSE
;
1591 g_return_val_if_fail (G_IS_BINDING (binding
), FALSE
);
1592 g_return_val_if_fail (E_IS_SOURCE_LIST (source_list
), FALSE
);
1594 string
= g_value_get_string (source_value
);
1595 if (string
== NULL
|| *string
== '\0')
1598 source
= e_source_list_peek_source_by_uid (source_list
, string
);
1599 if (source
!= NULL
) {
1600 g_value_set_object (target_value
, source
);