2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful, but
7 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 * Chris Lahey <clahey@ximian.com>
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
26 #include "e-misc-utils.h"
42 #include <glib/gi18n.h>
43 #include <glib/gstdio.h>
48 #include <gio/gdesktopappinfo.h>
51 #include <camel/camel.h>
52 #include <libedataserver/libedataserver.h>
54 #include "e-alert-dialog.h"
55 #include "e-alert-sink.h"
56 #include "e-client-cache.h"
57 #include "e-filter-option.h"
58 #include "e-util-private.h"
60 typedef struct _WindowData WindowData
;
65 ERestoreWindowFlags flags
;
72 window_data_free (WindowData
*data
)
74 if (data
->settings
!= NULL
)
75 g_object_unref (data
->settings
);
77 if (data
->timeout_id
> 0)
78 g_source_remove (data
->timeout_id
);
80 g_slice_free (WindowData
, data
);
84 window_update_settings (gpointer user_data
)
86 WindowData
*data
= user_data
;
87 GSettings
*settings
= data
->settings
;
89 if (data
->flags
& E_RESTORE_WINDOW_SIZE
) {
94 window
= gtk_widget_get_window (GTK_WIDGET (data
->window
));
95 state
= gdk_window_get_state (window
);
96 maximized
= ((state
& GDK_WINDOW_STATE_MAXIMIZED
) != 0);
98 g_settings_set_boolean (settings
, "maximized", maximized
);
103 gtk_window_get_size (data
->window
, &width
, &height
);
105 g_settings_set_int (settings
, "width", width
);
106 g_settings_set_int (settings
, "height", height
);
110 if (data
->flags
& E_RESTORE_WINDOW_POSITION
) {
113 gtk_window_get_position (data
->window
, &x
, &y
);
115 g_settings_set_int (settings
, "x", x
);
116 g_settings_set_int (settings
, "y", y
);
119 data
->timeout_id
= 0;
125 window_delayed_update_settings (WindowData
*data
)
127 if (data
->timeout_id
> 0)
128 g_source_remove (data
->timeout_id
);
130 data
->timeout_id
= e_named_timeout_add_seconds (
131 1, window_update_settings
, data
);
135 window_configure_event_cb (GtkWindow
*window
,
136 GdkEventConfigure
*event
,
139 window_delayed_update_settings (data
);
145 window_state_event_cb (GtkWindow
*window
,
146 GdkEventWindowState
*event
,
149 gboolean window_was_unmaximized
;
151 if (data
->timeout_id
> 0) {
152 g_source_remove (data
->timeout_id
);
153 data
->timeout_id
= 0;
156 window_was_unmaximized
=
157 ((event
->changed_mask
& GDK_WINDOW_STATE_MAXIMIZED
) != 0) &&
158 ((event
->new_window_state
& GDK_WINDOW_STATE_MAXIMIZED
) == 0);
160 if (window_was_unmaximized
) {
163 width
= data
->premax_width
;
164 data
->premax_width
= 0;
166 height
= data
->premax_height
;
167 data
->premax_height
= 0;
169 /* This only applies when the window is initially restored
170 * as maximized and is then unmaximized. GTK+ handles the
171 * unmaximized window size thereafter. */
172 if (width
> 0 && height
> 0)
173 gtk_window_resize (window
, width
, height
);
176 window_delayed_update_settings (data
);
182 window_unmap_cb (GtkWindow
*window
,
185 if (data
->timeout_id
> 0) {
186 g_source_remove (data
->timeout_id
);
187 data
->timeout_id
= 0;
190 /* Reset the flags so the window position and size are not
191 * accidentally reverted to their default value at the next run. */
198 * e_get_accels_filename:
200 * Returns the name of the user data file containing custom keyboard
201 * accelerator specifications.
203 * Returns: filename for accelerator specifications
206 e_get_accels_filename (void)
208 static gchar
*filename
= NULL
;
210 if (G_UNLIKELY (filename
== NULL
)) {
211 const gchar
*config_dir
= e_get_user_config_dir ();
212 filename
= g_build_filename (config_dir
, "accels", NULL
);
220 * @parent: a parent #GtkWindow or %NULL
221 * @uri: the URI to show
223 * Launches the default application to show the given URI. The URI must
224 * be of a form understood by GIO. If the URI cannot be shown, it presents
225 * a dialog describing the error. The dialog is set as transient to @parent
226 * if @parent is non-%NULL.
229 e_show_uri (GtkWindow
*parent
,
233 GdkScreen
*screen
= NULL
;
234 GError
*error
= NULL
;
237 g_return_if_fail (uri
!= NULL
);
239 timestamp
= gtk_get_current_event_time ();
242 screen
= gtk_widget_get_screen (GTK_WIDGET (parent
));
244 if (gtk_show_uri (screen
, uri
, timestamp
, &error
))
247 dialog
= gtk_message_dialog_new_with_markup (
248 parent
, GTK_DIALOG_DESTROY_WITH_PARENT
,
249 GTK_MESSAGE_ERROR
, GTK_BUTTONS_OK
,
250 "<big><b>%s</b></big>",
251 _("Could not open the link."));
253 gtk_message_dialog_format_secondary_text (
254 GTK_MESSAGE_DIALOG (dialog
), "%s", error
->message
);
256 gtk_dialog_run (GTK_DIALOG (dialog
));
258 gtk_widget_destroy (dialog
);
259 g_error_free (error
);
263 e_misc_utils_is_help_package_installed (void)
265 gboolean is_installed
;
268 /* Viewing user documentation requires the evolution help
269 * files. Look for one of the files it installs. */
270 path
= g_build_filename (EVOLUTION_DATADIR
, "help", "C", PACKAGE
, "index.page", NULL
);
272 is_installed
= g_file_test (path
, G_FILE_TEST_IS_REGULAR
);
277 GAppInfo
*help_handler
;
279 help_handler
= g_app_info_get_default_for_uri_scheme ("help");
281 is_installed
= help_handler
&& g_app_info_get_commandline (help_handler
);
283 g_clear_object (&help_handler
);
291 * @parent: a parent #GtkWindow or %NULL
292 * @link_id: help section to present or %NULL
294 * Opens the user documentation to the section given by @link_id, or to the
295 * table of contents if @link_id is %NULL. If the user documentation cannot
296 * be opened, it presents a dialog describing the error. The dialog is set
297 * as transient to @parent if @parent is non-%NULL.
300 e_display_help (GtkWindow
*parent
,
301 const gchar
*link_id
)
305 GdkScreen
*screen
= NULL
;
306 GError
*error
= NULL
;
309 if (e_misc_utils_is_help_package_installed ()) {
310 uri
= g_string_new ("help:" PACKAGE
);
312 uri
= g_string_new ("https://help.gnome.org/users/" PACKAGE
"/");
313 g_string_append_printf (uri
, "%d.%d", EDS_MAJOR_VERSION
, EDS_MINOR_VERSION
);
316 timestamp
= gtk_get_current_event_time ();
319 screen
= gtk_widget_get_screen (GTK_WIDGET (parent
));
322 if (link_id
!= NULL
) {
323 g_string_append (uri
, "/");
324 g_string_append (uri
, link_id
);
327 if (gtk_show_uri (screen
, uri
->str
, timestamp
, &error
))
330 dialog
= gtk_message_dialog_new_with_markup (
331 parent
, GTK_DIALOG_DESTROY_WITH_PARENT
,
332 GTK_MESSAGE_ERROR
, GTK_BUTTONS_OK
,
333 "<big><b>%s</b></big>",
334 _("Could not display help for Evolution."));
336 gtk_message_dialog_format_secondary_text (
337 GTK_MESSAGE_DIALOG (dialog
), "%s", error
->message
);
339 gtk_dialog_run (GTK_DIALOG (dialog
));
341 gtk_widget_destroy (dialog
);
342 g_error_free (error
);
345 g_string_free (uri
, TRUE
);
350 * @window: a #GtkWindow
351 * @settings_path: a #GSettings path
352 * @flags: flags indicating which window features to restore
354 * This function can restore one of or both a window's size and position
355 * using #GSettings keys at @settings_path which conform to the relocatable
356 * schema "org.gnome.evolution.window".
358 * If #E_RESTORE_WINDOW_SIZE is present in @flags, restore @window's
359 * previously recorded size and maximize state.
361 * If #E_RESTORE_WINDOW_POSITION is present in @flags, move @window to
362 * the previously recorded screen coordinates.
364 * The respective #GSettings values will be updated when the window is
365 * resized and/or moved.
368 e_restore_window (GtkWindow
*window
,
369 const gchar
*settings_path
,
370 ERestoreWindowFlags flags
)
376 g_return_if_fail (GTK_IS_WINDOW (window
));
377 g_return_if_fail (settings_path
!= NULL
);
379 schema
= "org.gnome.evolution.window";
380 settings
= g_settings_new_with_path (schema
, settings_path
);
382 data
= g_slice_new0 (WindowData
);
383 data
->window
= window
;
384 data
->settings
= g_object_ref (settings
);
387 if (flags
& E_RESTORE_WINDOW_SIZE
) {
390 width
= g_settings_get_int (settings
, "width");
391 height
= g_settings_get_int (settings
, "height");
393 if (width
> 0 && height
> 0)
394 gtk_window_resize (window
, width
, height
);
396 if (g_settings_get_boolean (settings
, "maximized")) {
398 GdkRectangle monitor_area
;
401 x
= g_settings_get_int (settings
, "x");
402 y
= g_settings_get_int (settings
, "y");
404 screen
= gtk_window_get_screen (window
);
405 gtk_window_get_size (window
, &width
, &height
);
407 data
->premax_width
= width
;
408 data
->premax_height
= height
;
410 monitor
= gdk_screen_get_monitor_at_point (screen
, x
, y
);
414 if (monitor
>= gdk_screen_get_n_monitors (screen
))
417 gdk_screen_get_monitor_workarea (
418 screen
, monitor
, &monitor_area
);
423 monitor_area
.height
);
425 gtk_window_maximize (window
);
429 if (flags
& E_RESTORE_WINDOW_POSITION
) {
432 x
= g_settings_get_int (settings
, "x");
433 y
= g_settings_get_int (settings
, "y");
435 gtk_window_move (window
, x
, y
);
438 g_object_set_data_full (
440 "e-util-window-data", data
,
441 (GDestroyNotify
) window_data_free
);
444 window
, "configure-event",
445 G_CALLBACK (window_configure_event_cb
), data
);
448 window
, "window-state-event",
449 G_CALLBACK (window_state_event_cb
), data
);
453 G_CALLBACK (window_unmap_cb
), data
);
455 g_object_unref (settings
);
460 * @ui_manager: a #GtkUIManager
461 * @action_name: the name of an action
463 * Returns the first #GtkAction named @action_name by traversing the
464 * list of action groups in @ui_manager. If no such action exists, the
465 * function emits a critical warning before returning %NULL, since this
466 * probably indicates a programming error and most code is not prepared
467 * to deal with lookup failures.
469 * Returns: the first #GtkAction named @action_name
472 e_lookup_action (GtkUIManager
*ui_manager
,
473 const gchar
*action_name
)
475 GtkAction
*action
= NULL
;
478 g_return_val_if_fail (GTK_IS_UI_MANAGER (ui_manager
), NULL
);
479 g_return_val_if_fail (action_name
!= NULL
, NULL
);
481 iter
= gtk_ui_manager_get_action_groups (ui_manager
);
483 while (iter
!= NULL
) {
484 GtkActionGroup
*action_group
= iter
->data
;
486 action
= gtk_action_group_get_action (
487 action_group
, action_name
);
491 iter
= g_list_next (iter
);
494 g_critical ("%s: action '%s' not found", G_STRFUNC
, action_name
);
500 * e_lookup_action_group:
501 * @ui_manager: a #GtkUIManager
502 * @group_name: the name of an action group
504 * Returns the #GtkActionGroup in @ui_manager named @group_name. If no
505 * such action group exists, the function emits a critical warnings before
506 * returning %NULL, since this probably indicates a programming error and
507 * most code is not prepared to deal with lookup failures.
509 * Returns: the #GtkActionGroup named @group_name
512 e_lookup_action_group (GtkUIManager
*ui_manager
,
513 const gchar
*group_name
)
517 g_return_val_if_fail (GTK_IS_UI_MANAGER (ui_manager
), NULL
);
518 g_return_val_if_fail (group_name
!= NULL
, NULL
);
520 iter
= gtk_ui_manager_get_action_groups (ui_manager
);
522 while (iter
!= NULL
) {
523 GtkActionGroup
*action_group
= iter
->data
;
526 name
= gtk_action_group_get_name (action_group
);
527 if (strcmp (name
, group_name
) == 0)
530 iter
= g_list_next (iter
);
533 g_critical ("%s: action group '%s' not found", G_STRFUNC
, group_name
);
539 * e_action_compare_by_label:
540 * @action1: a #GtkAction
541 * @action2: a #GtkAction
543 * Compares the labels for @action1 and @action2 using g_utf8_collate().
545 * Returns: < 0 if @action1 compares before @action2, 0 if they
546 * compare equal, > 0 if @action1 compares after @action2
549 e_action_compare_by_label (GtkAction
*action1
,
556 /* XXX This is horribly inefficient but will generally only be
557 * used on short lists of actions during UI construction. */
559 if (action1
== action2
)
562 g_object_get (action1
, "label", &label1
, NULL
);
563 g_object_get (action2
, "label", &label2
, NULL
);
565 result
= g_utf8_collate (label1
, label2
);
574 * e_action_group_remove_all_actions:
575 * @action_group: a #GtkActionGroup
577 * Removes all actions from the action group.
580 e_action_group_remove_all_actions (GtkActionGroup
*action_group
)
584 /* XXX I've proposed this function for inclusion in GTK+.
585 * GtkActionGroup stores actions in an internal hash
586 * table and can do this more efficiently by calling
587 * g_hash_table_remove_all().
589 * http://bugzilla.gnome.org/show_bug.cgi?id=550485 */
591 g_return_if_fail (GTK_IS_ACTION_GROUP (action_group
));
593 list
= gtk_action_group_list_actions (action_group
);
594 for (iter
= list
; iter
!= NULL
; iter
= iter
->next
)
595 gtk_action_group_remove_action (action_group
, iter
->data
);
600 * e_radio_action_get_current_action:
601 * @radio_action: a #GtkRadioAction
603 * Returns the currently active member of the group to which @radio_action
606 * Returns: the currently active group member
609 e_radio_action_get_current_action (GtkRadioAction
*radio_action
)
614 g_return_val_if_fail (GTK_IS_RADIO_ACTION (radio_action
), NULL
);
616 group
= gtk_radio_action_get_group (radio_action
);
617 current_value
= gtk_radio_action_get_current_value (radio_action
);
619 while (group
!= NULL
) {
622 radio_action
= GTK_RADIO_ACTION (group
->data
);
623 g_object_get (radio_action
, "value", &value
, NULL
);
625 if (value
== current_value
)
628 group
= g_slist_next (group
);
635 * e_action_group_add_actions_localized:
636 * @action_group: a #GtkActionGroup to add @entries to
637 * @translation_domain: a translation domain to use
638 * to translate label and tooltip strings in @entries
639 * @entries: (array length=n_entries): an array of action descriptions
640 * @n_entries: the number of entries
641 * @user_data: data to pass to the action callbacks
643 * Adds #GtkAction-s defined by @entries to @action_group, with action's
644 * label and tooltip localized in the given translation domain, instead
645 * of the domain set on the @action_group.
650 e_action_group_add_actions_localized (GtkActionGroup
*action_group
,
651 const gchar
*translation_domain
,
652 const GtkActionEntry
*entries
,
656 GtkActionGroup
*tmp_group
;
660 g_return_if_fail (action_group
!= NULL
);
661 g_return_if_fail (entries
!= NULL
);
662 g_return_if_fail (n_entries
> 0);
663 g_return_if_fail (translation_domain
!= NULL
);
664 g_return_if_fail (*translation_domain
);
666 tmp_group
= gtk_action_group_new ("temporary-group");
667 gtk_action_group_set_translation_domain (tmp_group
, translation_domain
);
668 gtk_action_group_add_actions (tmp_group
, entries
, n_entries
, user_data
);
670 list
= gtk_action_group_list_actions (tmp_group
);
671 for (iter
= list
; iter
!= NULL
; iter
= iter
->next
) {
672 GtkAction
*action
= GTK_ACTION (iter
->data
);
673 const gchar
*action_name
;
675 g_object_ref (action
);
677 action_name
= gtk_action_get_name (action
);
679 for (ii
= 0; ii
< n_entries
; ii
++) {
680 if (g_strcmp0 (entries
[ii
].name
, action_name
) == 0) {
681 gtk_action_group_remove_action (
683 gtk_action_group_add_action_with_accel (
684 action_group
, action
,
685 entries
[ii
].accelerator
);
690 g_object_unref (action
);
694 g_object_unref (tmp_group
);
698 * e_builder_get_widget:
699 * @builder: a #GtkBuilder
700 * @widget_name: name of a widget in @builder
702 * Gets the widget named @widget_name. Note that this function does not
703 * increment the reference count of the returned widget. If @widget_name
704 * could not be found in the @builder<!-- -->'s object tree, a run-time
705 * warning is emitted since this usually indicates a programming error.
707 * This is a convenience function to work around the awkwardness of
708 * #GtkBuilder returning #GObject pointers, when the vast majority of
709 * the time you want a #GtkWidget pointer.
711 * If you need something from @builder other than a #GtkWidget, or you
712 * want to test for the existence of some widget name without incurring
713 * a run-time warning, use gtk_builder_get_object().
715 * Returns: the widget named @widget_name, or %NULL
718 e_builder_get_widget (GtkBuilder
*builder
,
719 const gchar
*widget_name
)
723 g_return_val_if_fail (GTK_IS_BUILDER (builder
), NULL
);
724 g_return_val_if_fail (widget_name
!= NULL
, NULL
);
726 object
= gtk_builder_get_object (builder
, widget_name
);
727 if (object
== NULL
) {
728 g_warning ("Could not find widget '%s'", widget_name
);
732 return GTK_WIDGET (object
);
736 * e_load_ui_builder_definition:
737 * @builder: a #GtkBuilder
738 * @basename: basename of the UI definition file
740 * Loads a UI definition into @builder from Evolution's UI directory.
741 * Failure here is fatal, since the application can't function without
742 * its UI definitions.
745 e_load_ui_builder_definition (GtkBuilder
*builder
,
746 const gchar
*basename
)
749 GError
*error
= NULL
;
751 g_return_if_fail (GTK_IS_BUILDER (builder
));
752 g_return_if_fail (basename
!= NULL
);
754 filename
= g_build_filename (EVOLUTION_UIDIR
, basename
, NULL
);
755 gtk_builder_add_from_file (builder
, filename
, &error
);
759 g_error ("%s: %s", basename
, error
->message
);
760 g_warn_if_reached ();
765 * e_load_ui_manager_definition:
766 * @ui_manager: a #GtkUIManager
767 * @basename: basename of the UI definition file
769 * Loads a UI definition into @ui_manager from Evolution's UI directory.
770 * Failure here is fatal, since the application can't function without
771 * its UI definitions.
773 * Returns: The merge ID for the merged UI. The merge ID can be used to
774 * unmerge the UI with gtk_ui_manager_remove_ui().
777 e_load_ui_manager_definition (GtkUIManager
*ui_manager
,
778 const gchar
*basename
)
782 GError
*error
= NULL
;
784 g_return_val_if_fail (GTK_IS_UI_MANAGER (ui_manager
), 0);
785 g_return_val_if_fail (basename
!= NULL
, 0);
787 filename
= g_build_filename (EVOLUTION_UIDIR
, basename
, NULL
);
788 merge_id
= gtk_ui_manager_add_ui_from_file (
789 ui_manager
, filename
, &error
);
793 g_error ("%s: %s", basename
, error
->message
);
794 g_warn_if_reached ();
800 /* Helper for e_categories_add_change_hook() */
802 categories_changed_cb (GObject
*useless_opaque_object
,
803 GHookList
*hook_list
)
805 /* e_categories_register_change_listener() is broken because
806 * it requires callbacks to allow for some opaque GObject as
807 * the first argument (not does it document this). */
808 g_hook_list_invoke (hook_list
, FALSE
);
811 /* Helper for e_categories_add_change_hook() */
813 categories_weak_notify_cb (GHookList
*hook_list
,
814 gpointer where_the_object_was
)
818 /* This should not happen, but if we fail to find the hook for
819 * some reason, g_hook_destroy_link() will warn about the NULL
820 * pointer, which is all we would do anyway so no need to test
821 * for it ourselves. */
822 hook
= g_hook_find_data (hook_list
, TRUE
, where_the_object_was
);
823 g_hook_destroy_link (hook_list
, hook
);
827 * e_categories_add_change_hook:
828 * @func: a hook function
829 * @object: a #GObject to be passed to @func, or %NULL
831 * A saner alternative to e_categories_register_change_listener().
833 * Adds a hook function to be called when a category is added, removed or
834 * modified. If @object is not %NULL, the hook function is automatically
835 * removed when @object is finalized.
838 e_categories_add_change_hook (GHookFunc func
,
841 static gboolean initialized
= FALSE
;
842 static GHookList hook_list
;
845 g_return_if_fail (func
!= NULL
);
848 g_return_if_fail (G_IS_OBJECT (object
));
851 g_hook_list_init (&hook_list
, sizeof (GHook
));
852 e_categories_register_change_listener (
853 G_CALLBACK (categories_changed_cb
), &hook_list
);
857 hook
= g_hook_alloc (&hook_list
);
864 G_OBJECT (object
), (GWeakNotify
)
865 categories_weak_notify_cb
, &hook_list
);
867 g_hook_append (&hook_list
, hook
);
872 * @nptr: the string to convert to a numeric value.
873 * @endptr: if non-NULL, it returns the character after
874 * the last character used in the conversion.
876 * Converts a string to a gdouble value. This function detects
877 * strings either in the standard C locale or in the current locale.
879 * This function is typically used when reading configuration files or
880 * other non-user input that should not be locale dependent, but may
881 * have been in the past. To handle input from the user you should
882 * normally use the locale-sensitive system strtod function.
884 * To convert from a double to a string in a locale-insensitive way, use
887 * Returns: the gdouble value
890 e_flexible_strtod (const gchar
*nptr
,
895 struct lconv
*locale_data
;
896 const gchar
*decimal_point
;
897 gint decimal_point_len
;
898 const gchar
*p
, *decimal_point_pos
;
899 const gchar
*end
= NULL
; /* Silence gcc */
902 g_return_val_if_fail (nptr
!= NULL
, 0);
906 locale_data
= localeconv ();
907 decimal_point
= locale_data
->decimal_point
;
908 decimal_point_len
= strlen (decimal_point
);
910 g_return_val_if_fail (decimal_point_len
!= 0, 0);
912 decimal_point_pos
= NULL
;
913 if (!strcmp (decimal_point
, "."))
914 return strtod (nptr
, endptr
);
918 /* Skip leading space */
919 while (isspace ((guchar
) * p
))
922 /* Skip leading optional sign */
923 if (*p
== '+' || *p
== '-')
927 (p
[1] == 'x' || p
[1] == 'X')) {
929 /* HEX - find the (optional) decimal point */
931 while (isxdigit ((guchar
) * p
))
935 decimal_point_pos
= p
++;
937 while (isxdigit ((guchar
) * p
))
940 if (*p
== 'p' || *p
== 'P')
942 if (*p
== '+' || *p
== '-')
944 while (isdigit ((guchar
) * p
))
947 } else if (strncmp (p
, decimal_point
, decimal_point_len
) == 0) {
948 return strtod (nptr
, endptr
);
951 while (isdigit ((guchar
) * p
))
955 decimal_point_pos
= p
++;
957 while (isdigit ((guchar
) * p
))
960 if (*p
== 'e' || *p
== 'E')
962 if (*p
== '+' || *p
== '-')
964 while (isdigit ((guchar
) * p
))
967 } else if (strncmp (p
, decimal_point
, decimal_point_len
) == 0) {
968 return strtod (nptr
, endptr
);
971 /* For the other cases, we need not convert the decimal point */
973 if (!decimal_point_pos
)
974 return strtod (nptr
, endptr
);
976 /* We need to convert the '.' to the locale specific decimal point */
977 copy
= g_malloc (end
- nptr
+ 1 + decimal_point_len
);
980 memcpy (c
, nptr
, decimal_point_pos
- nptr
);
981 c
+= decimal_point_pos
- nptr
;
982 memcpy (c
, decimal_point
, decimal_point_len
);
983 c
+= decimal_point_len
;
984 memcpy (c
, decimal_point_pos
+ 1, end
- (decimal_point_pos
+ 1));
985 c
+= end
- (decimal_point_pos
+ 1);
988 val
= strtod (copy
, &fail_pos
);
991 if (fail_pos
> decimal_point_pos
)
993 (gchar
*) nptr
+ (fail_pos
- copy
) -
994 (decimal_point_len
- 1);
996 fail_pos
= (gchar
*) nptr
+ (fail_pos
- copy
);
1009 * @buffer: A buffer to place the resulting string in
1010 * @buf_len: The length of the buffer.
1011 * @format: The printf-style format to use for the
1012 * code to use for converting.
1013 * @d: The double to convert
1015 * Converts a double to a string, using the '.' as
1016 * decimal_point. To format the number you pass in
1017 * a printf-style formating string. Allowed conversion
1018 * specifiers are eEfFgG.
1020 * If you want to generates enough precision that converting
1021 * the string back using @g_strtod gives the same machine-number
1022 * (on machines with IEEE compatible 64bit doubles) use the format
1023 * string "%.17g". If you do this it is guaranteed that the size
1024 * of the resulting string will never be larger than
1025 * @G_ASCII_DTOSTR_BUF_SIZE bytes.
1027 * Returns: the pointer to the buffer with the converted string
1030 e_ascii_dtostr (gchar
*buffer
,
1032 const gchar
*format
,
1035 struct lconv
*locale_data
;
1036 const gchar
*decimal_point
;
1037 gint decimal_point_len
;
1042 g_return_val_if_fail (buffer
!= NULL
, NULL
);
1043 g_return_val_if_fail (format
[0] == '%', NULL
);
1044 g_return_val_if_fail (strpbrk (format
+ 1, "'l%") == NULL
, NULL
);
1046 format_char
= format
[strlen (format
) - 1];
1048 g_return_val_if_fail (format_char
== 'e' || format_char
== 'E' ||
1049 format_char
== 'f' || format_char
== 'F' ||
1050 format_char
== 'g' || format_char
== 'G',
1053 if (format
[0] != '%')
1056 if (strpbrk (format
+ 1, "'l%"))
1059 if (!(format_char
== 'e' || format_char
== 'E' ||
1060 format_char
== 'f' || format_char
== 'F' ||
1061 format_char
== 'g' || format_char
== 'G'))
1064 g_snprintf (buffer
, buf_len
, format
, d
);
1066 locale_data
= localeconv ();
1067 decimal_point
= locale_data
->decimal_point
;
1068 decimal_point_len
= strlen (decimal_point
);
1070 g_return_val_if_fail (decimal_point_len
!= 0, NULL
);
1072 if (strcmp (decimal_point
, ".")) {
1075 if (*p
== '+' || *p
== '-')
1078 while (isdigit ((guchar
) * p
))
1081 if (strncmp (p
, decimal_point
, decimal_point_len
) == 0) {
1084 if (decimal_point_len
> 1) {
1085 rest_len
= strlen (p
+ (decimal_point_len
- 1));
1087 p
, p
+ (decimal_point_len
- 1),
1098 * e_str_without_underscores:
1099 * @string: the string to strip underscores from
1101 * Strips underscores from a string in the same way
1102 * @gtk_label_new_with_mnemonics does. The returned string should be freed
1105 * Returns: a newly-allocated string without underscores
1108 e_str_without_underscores (const gchar
*string
)
1114 new_string
= g_malloc (strlen (string
) + 1);
1117 for (sp
= string
; *sp
!= '\0'; sp
++) {
1121 } else if (sp
[1] == '_') {
1122 /* Translate "__" in "_". */
1134 * e_str_replace_string
1135 * @text: the string to replace
1136 * @before: the string to be replaced
1137 * @after: the string to replaced with
1139 * Replaces every occurrence of the string @before with the string @after in
1140 * the string @text and returns a #GString with result that should be freed
1141 * with g_string_free().
1143 * Returns: a newly-allocated #GString
1146 e_str_replace_string (const gchar
*text
,
1147 const gchar
*before
,
1150 const gchar
*p
, *next
;
1154 g_return_val_if_fail (text
!= NULL
, NULL
);
1155 g_return_val_if_fail (before
!= NULL
, NULL
);
1156 g_return_val_if_fail (*before
, NULL
);
1158 find_len
= strlen (before
);
1159 str
= g_string_new ("");
1162 while (next
= strstr (p
, before
), next
) {
1164 g_string_append_len (str
, p
, next
- p
);
1166 if (after
&& *after
)
1167 g_string_append (str
, after
);
1169 p
= next
+ find_len
;
1172 g_string_append (str
, p
);
1178 e_str_compare (gconstpointer x
,
1181 if (x
== NULL
|| y
== NULL
) {
1188 return strcmp (x
, y
);
1192 e_str_case_compare (gconstpointer x
,
1198 if (x
== NULL
|| y
== NULL
) {
1205 cx
= g_utf8_casefold (x
, -1);
1206 cy
= g_utf8_casefold (y
, -1);
1208 res
= g_utf8_collate (cx
, cy
);
1217 e_collate_compare (gconstpointer x
,
1220 if (x
== NULL
|| y
== NULL
) {
1227 return g_utf8_collate (x
, y
);
1231 e_int_compare (gconstpointer x
,
1234 gint nx
= GPOINTER_TO_INT (x
);
1235 gint ny
= GPOINTER_TO_INT (y
);
1237 return (nx
== ny
) ? 0 : (nx
< ny
) ? -1 : 1;
1242 * @color: a #GdkColor
1244 * Converts a #GdkColor to a 24-bit RGB color value.
1246 * Returns: a 24-bit color value
1249 e_color_to_value (const GdkColor
*color
)
1253 g_return_val_if_fail (color
!= NULL
, 0);
1255 rgba
.red
= color
->red
/ 65535.0;
1256 rgba
.green
= color
->green
/ 65535.0;
1257 rgba
.blue
= color
->blue
/ 65535.0;
1260 return e_rgba_to_value (&rgba
);
1267 * Converts #GdkRGBA to a 24-bit RGB color value
1269 * Returns: a 24-bit color value
1272 e_rgba_to_value (const GdkRGBA
*rgba
)
1278 g_return_val_if_fail (rgba
!= NULL
, 0);
1280 red
= 255 * rgba
->red
;
1281 green
= 255 * rgba
->green
;
1282 blue
= 255 * rgba
->blue
;
1285 ((((red
& 0xFF) << 16) |
1286 ((green
& 0xFF) << 8) |
1287 (blue
& 0xFF)) & 0xffffff);
1292 * @rgba: a source #GdkRGBA
1293 * @color: a destination #GdkColor
1295 * Converts @rgba into @color, but loses the alpha channel from @rgba.
1298 e_rgba_to_color (const GdkRGBA
*rgba
,
1301 g_return_if_fail (rgba
!= NULL
);
1302 g_return_if_fail (color
!= NULL
);
1305 color
->red
= rgba
->red
* 65535.0;
1306 color
->green
= rgba
->green
* 65535.0;
1307 color
->blue
= rgba
->blue
* 65535.0;
1311 * e_utils_get_theme_color:
1312 * @widget: a #GtkWidget instance
1313 * @color_names: comma-separated theme color names
1314 * @fallback_color_ident: fallback color identificator, in a format for gdk_rgba_parse()
1315 * @rgba: where to store the read color
1317 * Reads named theme color from a #GtkStyleContext of @widget.
1318 * The @color_names are read one after another from left to right,
1319 * the next are meant as fallbacks, in case the theme doesn't
1320 * define the previous color. If none is found then the @fallback_color_ident
1324 e_utils_get_theme_color (GtkWidget
*widget
,
1325 const gchar
*color_names
,
1326 const gchar
*fallback_color_ident
,
1329 GtkStyleContext
*style_context
;
1333 g_return_if_fail (GTK_IS_WIDGET (widget
));
1334 g_return_if_fail (color_names
!= NULL
);
1335 g_return_if_fail (fallback_color_ident
!= NULL
);
1336 g_return_if_fail (rgba
!= NULL
);
1338 style_context
= gtk_widget_get_style_context (widget
);
1340 names
= g_strsplit (color_names
, ",", -1);
1341 for (ii
= 0; names
&& names
[ii
]; ii
++) {
1342 if (gtk_style_context_lookup_color (style_context
, names
[ii
], rgba
)) {
1350 g_warn_if_fail (gdk_rgba_parse (rgba
, fallback_color_ident
));
1354 * e_utils_get_theme_color_color:
1355 * @widget: a #GtkWidget instance
1356 * @color_names: comma-separated theme color names
1357 * @fallback_color_ident: fallback color identificator, in a format for gdk_rgba_parse()
1358 * @color: where to store the read color
1360 * The same as e_utils_get_theme_color(), only populates #GdkColor,
1361 * instead of #GdkRGBA.
1364 e_utils_get_theme_color_color (GtkWidget
*widget
,
1365 const gchar
*color_names
,
1366 const gchar
*fallback_color_ident
,
1371 g_return_if_fail (GTK_IS_WIDGET (widget
));
1372 g_return_if_fail (color_names
!= NULL
);
1373 g_return_if_fail (fallback_color_ident
!= NULL
);
1374 g_return_if_fail (color
!= NULL
);
1376 e_utils_get_theme_color (widget
, color_names
, fallback_color_ident
, &rgba
);
1378 e_rgba_to_color (&rgba
, color
);
1381 /* This is copied from gtk+ sources */
1383 rgb_to_hls (gdouble
*r
,
1421 l
= (max
+ min
) / 2;
1427 s
= (max
- min
) / (max
+ min
);
1429 s
= (max
- min
) / (2 - max
- min
);
1433 h
= (green
- blue
) / delta
;
1434 else if (green
== max
)
1435 h
= 2 + (blue
- red
) / delta
;
1436 else if (blue
== max
)
1437 h
= 4 + (red
- green
) / delta
;
1449 /* This is copied from gtk+ sources */
1451 hls_to_rgb (gdouble
*h
,
1464 if (lightness
<= 0.5)
1465 m2
= lightness
* (1 + saturation
);
1467 m2
= lightness
+ saturation
- lightness
* saturation
;
1468 m1
= 2 * lightness
- m2
;
1470 if (saturation
== 0) {
1482 r
= m1
+ (m2
- m1
) * hue
/ 60;
1486 r
= m1
+ (m2
- m1
) * (240 - hue
) / 60;
1497 g
= m1
+ (m2
- m1
) * hue
/ 60;
1501 g
= m1
+ (m2
- m1
) * (240 - hue
) / 60;
1512 b
= m1
+ (m2
- m1
) * hue
/ 60;
1516 b
= m1
+ (m2
- m1
) * (240 - hue
) / 60;
1526 /* This is copied from gtk+ sources */
1528 e_utils_shade_color (const GdkRGBA
*a
,
1536 g_return_if_fail (a
!= NULL
);
1537 g_return_if_fail (b
!= NULL
);
1543 rgb_to_hls (&red
, &green
, &blue
);
1548 else if (green
< 0.0)
1554 else if (blue
< 0.0)
1557 hls_to_rgb (&red
, &green
, &blue
);
1562 b
->alpha
= a
->alpha
;
1566 epow10 (gint number
)
1570 while (number
-- > 0)
1577 e_format_number (gint number
)
1579 GList
*iterator
, *list
= NULL
;
1580 struct lconv
*locality
;
1581 gint char_length
= 0;
1582 gint group_count
= 0;
1584 gint last_count
= 3;
1587 gchar
*value_iterator
;
1589 locality
= localeconv ();
1590 grouping
= locality
->grouping
;
1593 switch (*grouping
) {
1595 last_count
= *grouping
;
1597 /* coverity[fallthrough] */
1599 divider
= epow10 (last_count
);
1600 if (number
>= divider
) {
1601 group
= g_strdup_printf (
1602 "%0*d", last_count
, number
% divider
);
1604 group
= g_strdup_printf (
1605 "%d", number
% divider
);
1610 group
= g_strdup_printf ("%d", number
);
1614 char_length
+= strlen (group
);
1615 list
= g_list_prepend (list
, group
);
1621 gchar
, 1 + char_length
+ (group_count
- 1) *
1622 strlen (locality
->thousands_sep
));
1625 value_iterator
= value
;
1627 strcpy (value_iterator
, iterator
->data
);
1628 value_iterator
+= strlen (iterator
->data
);
1629 for (iterator
= iterator
->next
; iterator
; iterator
= iterator
->next
) {
1630 strcpy (value_iterator
, locality
->thousands_sep
);
1631 value_iterator
+= strlen (locality
->thousands_sep
);
1633 strcpy (value_iterator
, iterator
->data
);
1634 value_iterator
+= strlen (iterator
->data
);
1636 g_list_foreach (list
, (GFunc
) g_free
, NULL
);
1640 return g_strdup ("0");
1644 /* Perform a binary search for key in base which has nmemb elements
1645 * of size bytes each. The comparisons are done by (*compare)(). */
1647 e_bsearch (gconstpointer key
,
1651 ESortCompareFunc compare
,
1659 if (!(start
|| end
))
1666 p
= (((const gchar
*) base
) + (idx
* size
));
1667 comparison
= (*compare
) (key
, p
, closure
);
1670 else if (comparison
> 0)
1679 p
= (((const gchar
*) base
) + (idx
* size
));
1680 comparison
= (*compare
) (key
, p
, closure
);
1681 if (comparison
<= 0)
1694 p
= (((const gchar
*) base
) + (idx
* size
));
1695 comparison
= (*compare
) (key
, p
, closure
);
1713 /* Function to do a last minute fixup of the AM/PM stuff if the locale
1714 * and gettext haven't done it right. Most English speaking countries
1715 * except the USA use the 24 hour clock (UK, Australia etc). However
1716 * since they are English nobody bothers to write a language
1717 * translation (gettext) file. So the locale turns off the AM/PM, but
1718 * gettext does not turn on the 24 hour clock. Leaving a mess.
1720 * This routine checks if AM/PM are defined in the locale, if not it
1721 * forces the use of the 24 hour clock.
1723 * The function itself is a front end on strftime and takes exactly
1724 * the same arguments.
1726 * TODO: Actually remove the '%p' from the fixed up string so that
1727 * there isn't a stray space.
1731 e_strftime_fix_am_pm (gchar
*str
,
1734 const struct tm
*tm
)
1741 if (strstr (fmt
, "%p") == NULL
&& strstr (fmt
, "%P") == NULL
) {
1742 /* No AM/PM involved - can use the fmt string directly */
1743 ret
= e_strftime (str
, max
, fmt
, tm
);
1745 /* Get the AM/PM symbol from the locale */
1746 e_strftime (buf
, 10, "%p", tm
);
1749 /* AM/PM have been defined in the locale
1750 * so we can use the fmt string directly. */
1751 ret
= e_strftime (str
, max
, fmt
, tm
);
1753 /* No AM/PM defined by locale
1754 * must change to 24 hour clock. */
1755 ffmt
= g_strdup (fmt
);
1756 for (sp
= ffmt
; (sp
= strstr (sp
, "%l")); sp
++) {
1757 /* Maybe this should be 'k', but I have never
1758 * seen a 24 clock actually use that format. */
1761 for (sp
= ffmt
; (sp
= strstr (sp
, "%I")); sp
++) {
1764 ret
= e_strftime (str
, max
, ffmt
, tm
);
1773 e_utf8_strftime_fix_am_pm (gchar
*str
,
1776 const struct tm
*tm
)
1779 gchar
*locale_fmt
, *buf
;
1781 locale_fmt
= g_locale_from_utf8 (fmt
, -1, NULL
, &sz
, NULL
);
1785 ret
= e_strftime_fix_am_pm (str
, max
, locale_fmt
, tm
);
1787 g_free (locale_fmt
);
1791 buf
= g_locale_to_utf8 (str
, ret
, NULL
, &sz
, NULL
);
1793 g_free (locale_fmt
);
1798 gchar
*tmp
= buf
+ max
- 1;
1799 tmp
= g_utf8_find_prev_char (buf
, tmp
);
1805 memcpy (str
, buf
, sz
);
1807 g_free (locale_fmt
);
1814 * @month: month index
1815 * @abbreviated: if %TRUE, abbreviate the month name
1817 * Returns the localized name for @month. If @abbreviated is %TRUE,
1818 * returns the locale's abbreviated month name.
1820 * Returns: localized month name
1823 e_get_month_name (GDateMonth month
,
1824 gboolean abbreviated
)
1826 /* Make the indices correspond to the enum values. */
1827 static const gchar
*abbr_names
[G_DATE_DECEMBER
+ 1];
1828 static const gchar
*full_names
[G_DATE_DECEMBER
+ 1];
1829 static gboolean first_time
= TRUE
;
1831 g_return_val_if_fail (month
>= G_DATE_JANUARY
, NULL
);
1832 g_return_val_if_fail (month
<= G_DATE_DECEMBER
, NULL
);
1834 if (G_UNLIKELY (first_time
)) {
1839 memset (abbr_names
, 0, sizeof (abbr_names
));
1840 memset (full_names
, 0, sizeof (full_names
));
1842 /* First Julian day was in January. */
1843 g_date_set_julian (&date
, 1);
1845 for (ii
= G_DATE_JANUARY
; ii
<= G_DATE_DECEMBER
; ii
++) {
1846 g_date_strftime (buffer
, sizeof (buffer
), "%b", &date
);
1847 abbr_names
[ii
] = g_intern_string (buffer
);
1848 g_date_strftime (buffer
, sizeof (buffer
), "%B", &date
);
1849 full_names
[ii
] = g_intern_string (buffer
);
1850 g_date_add_months (&date
, 1);
1856 return abbreviated
? abbr_names
[month
] : full_names
[month
];
1860 * e_get_weekday_name:
1861 * @weekday: weekday index
1862 * @abbreviated: if %TRUE, abbreviate the weekday name
1864 * Returns the localized name for @weekday. If @abbreviated is %TRUE,
1865 * returns the locale's abbreviated weekday name.
1867 * Returns: localized weekday name
1870 e_get_weekday_name (GDateWeekday weekday
,
1871 gboolean abbreviated
)
1873 /* Make the indices correspond to the enum values. */
1874 static const gchar
*abbr_names
[G_DATE_SUNDAY
+ 1];
1875 static const gchar
*full_names
[G_DATE_SUNDAY
+ 1];
1876 static gboolean first_time
= TRUE
;
1878 g_return_val_if_fail (weekday
>= G_DATE_MONDAY
, NULL
);
1879 g_return_val_if_fail (weekday
<= G_DATE_SUNDAY
, NULL
);
1881 if (G_UNLIKELY (first_time
)) {
1886 memset (abbr_names
, 0, sizeof (abbr_names
));
1887 memset (full_names
, 0, sizeof (full_names
));
1889 /* First Julian day was a Monday. */
1890 g_date_set_julian (&date
, 1);
1892 for (ii
= G_DATE_MONDAY
; ii
<= G_DATE_SUNDAY
; ii
++) {
1893 g_date_strftime (buffer
, sizeof (buffer
), "%a", &date
);
1894 abbr_names
[ii
] = g_intern_string (buffer
);
1895 g_date_strftime (buffer
, sizeof (buffer
), "%A", &date
);
1896 full_names
[ii
] = g_intern_string (buffer
);
1897 g_date_add_days (&date
, 1);
1903 return abbreviated
? abbr_names
[weekday
] : full_names
[weekday
];
1907 * e_weekday_get_next:
1908 * @weekday: a #GDateWeekday
1910 * Returns the #GDateWeekday after @weekday.
1912 * Returns: the day after @weekday
1915 e_weekday_get_next (GDateWeekday weekday
)
1919 /* Verbose for readability. */
1922 next
= G_DATE_TUESDAY
;
1924 case G_DATE_TUESDAY
:
1925 next
= G_DATE_WEDNESDAY
;
1927 case G_DATE_WEDNESDAY
:
1928 next
= G_DATE_THURSDAY
;
1930 case G_DATE_THURSDAY
:
1931 next
= G_DATE_FRIDAY
;
1934 next
= G_DATE_SATURDAY
;
1936 case G_DATE_SATURDAY
:
1937 next
= G_DATE_SUNDAY
;
1940 next
= G_DATE_MONDAY
;
1943 next
= G_DATE_BAD_WEEKDAY
;
1951 * e_weekday_get_prev:
1952 * @weekday: a #GDateWeekday
1954 * Returns the #GDateWeekday before @weekday.
1956 * Returns: the day before @weekday
1959 e_weekday_get_prev (GDateWeekday weekday
)
1963 /* Verbose for readability. */
1966 prev
= G_DATE_SUNDAY
;
1968 case G_DATE_TUESDAY
:
1969 prev
= G_DATE_MONDAY
;
1971 case G_DATE_WEDNESDAY
:
1972 prev
= G_DATE_TUESDAY
;
1974 case G_DATE_THURSDAY
:
1975 prev
= G_DATE_WEDNESDAY
;
1978 prev
= G_DATE_THURSDAY
;
1980 case G_DATE_SATURDAY
:
1981 prev
= G_DATE_FRIDAY
;
1984 prev
= G_DATE_SATURDAY
;
1987 prev
= G_DATE_BAD_WEEKDAY
;
1995 * e_weekday_add_days:
1996 * @weekday: a #GDateWeekday
1997 * @n_days: number of days to add
1999 * Increments @weekday by @n_days.
2001 * Returns: a #GDateWeekday
2004 e_weekday_add_days (GDateWeekday weekday
,
2007 g_return_val_if_fail (
2008 g_date_valid_weekday (weekday
),
2009 G_DATE_BAD_WEEKDAY
);
2011 n_days
%= 7; /* Weekdays repeat every 7 days. */
2013 while (n_days
-- > 0)
2014 weekday
= e_weekday_get_next (weekday
);
2020 * e_weekday_subtract_days:
2021 * @weekday: a #GDateWeekday
2022 * @n_days: number of days to subtract
2024 * Decrements @weekday by @n_days.
2026 * Returns: a #GDateWeekday
2029 e_weekday_subtract_days (GDateWeekday weekday
,
2032 g_return_val_if_fail (
2033 g_date_valid_weekday (weekday
),
2034 G_DATE_BAD_WEEKDAY
);
2036 n_days
%= 7; /* Weekdays repeat every 7 days. */
2038 while (n_days
-- > 0)
2039 weekday
= e_weekday_get_prev (weekday
);
2045 * e_weekday_get_days_between:
2046 * @weekday1: a #GDateWeekday
2047 * @weekday2: a #GDateWeekday
2049 * Counts the number of days starting at @weekday1 and ending at @weekday2.
2051 * Returns: the number of days
2054 e_weekday_get_days_between (GDateWeekday weekday1
,
2055 GDateWeekday weekday2
)
2059 g_return_val_if_fail (g_date_valid_weekday (weekday1
), 0);
2060 g_return_val_if_fail (g_date_valid_weekday (weekday2
), 0);
2062 while (weekday1
!= weekday2
) {
2064 weekday1
= e_weekday_get_next (weekday1
);
2071 * e_weekday_to_tm_wday:
2072 * @weekday: a #GDateWeekday
2074 * Converts a #GDateWeekday to the numbering used in
2075 * <structname>struct tm</structname>.
2077 * Returns: number of days since Sunday (0 - 6)
2080 e_weekday_to_tm_wday (GDateWeekday weekday
)
2088 case G_DATE_TUESDAY
:
2091 case G_DATE_WEDNESDAY
:
2094 case G_DATE_THURSDAY
:
2100 case G_DATE_SATURDAY
:
2107 g_return_val_if_reached (-1);
2114 * e_weekday_from_tm_wday:
2115 * @tm_wday: number of days since Sunday (0 - 6)
2117 * Converts a weekday in the numbering used in
2118 * <structname>struct tm</structname> to a #GDateWeekday.
2120 * Returns: a #GDateWeekday
2123 e_weekday_from_tm_wday (gint tm_wday
)
2125 GDateWeekday weekday
;
2129 weekday
= G_DATE_SUNDAY
;
2132 weekday
= G_DATE_MONDAY
;
2135 weekday
= G_DATE_TUESDAY
;
2138 weekday
= G_DATE_WEDNESDAY
;
2141 weekday
= G_DATE_THURSDAY
;
2144 weekday
= G_DATE_FRIDAY
;
2147 weekday
= G_DATE_SATURDAY
;
2150 g_return_val_if_reached (G_DATE_BAD_WEEKDAY
);
2156 /* Evolution Locks for crash recovery */
2157 static const gchar
*
2158 get_lock_filename (void)
2160 static gchar
*filename
= NULL
;
2162 if (G_UNLIKELY (filename
== NULL
))
2163 filename
= g_build_filename (
2164 e_get_user_config_dir (), ".running", NULL
);
2170 e_file_lock_create (void)
2172 const gchar
*filename
= get_lock_filename ();
2173 gboolean status
= FALSE
;
2176 file
= g_fopen (filename
, "w");
2178 /* The lock file also serves as a PID file. */
2180 file
, "%" G_GINT64_FORMAT
"\n",
2181 (gint64
) getpid ());
2185 const gchar
*errmsg
= g_strerror (errno
);
2186 g_warning ("Lock file creation failed: %s", errmsg
);
2193 e_file_lock_destroy (void)
2195 const gchar
*filename
= get_lock_filename ();
2197 if (g_unlink (filename
) == -1) {
2198 const gchar
*errmsg
= g_strerror (errno
);
2199 g_warning ("Lock file deletion failed: %s", errmsg
);
2204 e_file_lock_exists (void)
2206 const gchar
*filename
= get_lock_filename ();
2208 return g_file_test (filename
, G_FILE_TEST_EXISTS
);
2211 /* Returns a PID stored in the lock file; 0 if no such file exists. */
2213 e_file_lock_get_pid (void)
2215 const gchar
*filename
= get_lock_filename ();
2216 gchar
*contents
= NULL
;
2217 GPid pid
= (GPid
) 0;
2220 if (!g_file_get_contents (filename
, &contents
, NULL
, NULL
)) {
2224 /* Try to extract an integer value from the string. */
2225 n_int64
= g_ascii_strtoll (contents
, NULL
, 10);
2226 if (n_int64
> 0 && n_int64
< G_MAXINT64
) {
2227 /* XXX Probably not portable. */
2228 pid
= (GPid
) n_int64
;
2237 * e_util_guess_mime_type:
2238 * @filename: a local file name, or URI
2239 * @localfile: %TRUE to check the file content, FALSE to check only the name
2241 * Tries to determine the MIME type for @filename. Free the returned
2242 * string with g_free().
2244 * Returns: the MIME type of @filename, or %NULL if the the MIME type could
2248 e_util_guess_mime_type (const gchar
*filename
,
2251 gchar
*mime_type
= NULL
;
2253 g_return_val_if_fail (filename
!= NULL
, NULL
);
2259 if (strstr (filename
, "://"))
2260 file
= g_file_new_for_uri (filename
);
2262 file
= g_file_new_for_path (filename
);
2264 fi
= g_file_query_info (
2265 file
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
,
2266 G_FILE_QUERY_INFO_NONE
, NULL
, NULL
);
2268 mime_type
= g_content_type_get_mime_type (
2269 g_file_info_get_content_type (fi
));
2270 g_object_unref (fi
);
2273 g_object_unref (file
);
2277 /* file doesn't exists locally, thus guess based on the filename */
2278 gboolean uncertain
= FALSE
;
2279 gchar
*content_type
;
2281 content_type
= g_content_type_guess (filename
, NULL
, 0, &uncertain
);
2283 mime_type
= g_content_type_get_mime_type (content_type
);
2284 g_free (content_type
);
2292 e_util_get_category_filter_options (void)
2297 clist
= e_categories_dup_list ();
2298 for (l
= clist
; l
; l
= l
->next
) {
2299 const gchar
*cname
= l
->data
;
2300 struct _filter_option
*fo
;
2302 if (!e_categories_is_searchable (cname
))
2305 fo
= g_new0 (struct _filter_option
, 1);
2307 fo
->title
= g_strdup (cname
);
2308 fo
->value
= g_strdup (cname
);
2309 res
= g_slist_prepend (res
, fo
);
2312 g_list_free_full (clist
, g_free
);
2314 return g_slist_reverse (res
);
2318 * e_util_dup_searchable_categories:
2320 * Returns a list of the searchable categories, with each item being a UTF-8
2321 * category name. The list should be freed with g_list_free() when done with it,
2322 * and the items should be freed with g_free(). Everything can be freed at once
2323 * using g_list_free_full().
2325 * Returns: (transfer full) (element-type utf8): a list of searchable category
2326 * names; free with g_list_free_full()
2329 e_util_dup_searchable_categories (void)
2331 GList
*res
= NULL
, *all_categories
, *l
;
2333 all_categories
= e_categories_dup_list ();
2334 for (l
= all_categories
; l
; l
= l
->next
) {
2335 gchar
*cname
= l
->data
;
2337 /* Steal the string from e_categories_dup_list(). */
2338 if (e_categories_is_searchable (cname
))
2339 res
= g_list_prepend (res
, (gpointer
) cname
);
2344 /* NOTE: Do *not* free the items. They have been freed or stolen
2346 g_list_free (all_categories
);
2348 return g_list_reverse (res
);
2351 * e_util_get_open_source_job_info:
2352 * @extension_name: an extension name of the source
2353 * @source_display_name: an ESource's display name
2354 * @description: (out) (transfer-full): a description to use
2355 * @alert_ident: (out) (transfer-full): an alert ident to use on failure
2356 * @alert_arg_0: (out) (transfer-full): an alert argument 0 to use on failure
2358 * Populates @desription, @alert_ident and @alert_arg_0 to be used
2359 * to open an #ESource with extension @extension_name. The values
2360 * can be used for functions like e_alert_sink_submit_thread_job().
2362 * If #TRUE is returned, then the caller is responsible to free
2363 * all @desription, @alert_ident and @alert_arg_0 with g_free(),
2364 * when no longer needed.
2366 * Returns: #TRUE, if the values for @desription, @alert_ident and @alert_arg_0
2367 * were set for the given @extension_name; when #FALSE is returned, then
2368 * none of these out variables are changed.
2373 e_util_get_open_source_job_info (const gchar
*extension_name
,
2374 const gchar
*source_display_name
,
2375 gchar
**description
,
2376 gchar
**alert_ident
,
2377 gchar
**alert_arg_0
)
2379 g_return_val_if_fail (extension_name
!= NULL
, FALSE
);
2380 g_return_val_if_fail (source_display_name
!= NULL
, FALSE
);
2381 g_return_val_if_fail (description
!= NULL
, FALSE
);
2382 g_return_val_if_fail (alert_ident
!= NULL
, FALSE
);
2383 g_return_val_if_fail (alert_arg_0
!= NULL
, FALSE
);
2385 if (g_ascii_strcasecmp (extension_name
, E_SOURCE_EXTENSION_CALENDAR
) == 0) {
2386 *alert_ident
= g_strdup ("calendar:failed-open-calendar");
2387 *description
= g_strdup_printf (_("Opening calendar '%s'"), source_display_name
);
2388 } else if (g_ascii_strcasecmp (extension_name
, E_SOURCE_EXTENSION_MEMO_LIST
) == 0) {
2389 *alert_ident
= g_strdup ("calendar:failed-open-memos");
2390 *description
= g_strdup_printf (_("Opening memo list '%s'"), source_display_name
);
2391 } else if (g_ascii_strcasecmp (extension_name
, E_SOURCE_EXTENSION_TASK_LIST
) == 0) {
2392 *alert_ident
= g_strdup ("calendar:failed-open-tasks");
2393 *description
= g_strdup_printf (_("Opening task list '%s'"), source_display_name
);
2394 } else if (g_ascii_strcasecmp (extension_name
, E_SOURCE_EXTENSION_ADDRESS_BOOK
) == 0) {
2395 *alert_ident
= g_strdup ("addressbook:load-error");
2396 *description
= g_strdup_printf (_("Opening address book '%s'"), source_display_name
);
2401 *alert_arg_0
= g_strdup (source_display_name
);
2407 * e_util_propagate_open_source_job_error:
2408 * @job_data: an #EAlertSinkThreadJobData instance
2409 * @extension_name: what extension name had beeing opened
2410 * @local_error: (allow none): a #GError as obtained in a thread job; can be NULL for success
2411 * @error: (allow none): an output #GError, to which propagate the @local_error
2413 * Propagates (and cosumes) the @local_error into the @error, eventually
2414 * changes alert_ident for the @job_data for well-known error codes,
2415 * where is available better error description.
2420 e_util_propagate_open_source_job_error (EAlertSinkThreadJobData
*job_data
,
2421 const gchar
*extension_name
,
2422 GError
*local_error
,
2425 const gchar
*alert_ident
= NULL
;
2427 g_return_if_fail (job_data
!= NULL
);
2428 g_return_if_fail (extension_name
!= NULL
);
2434 g_error_free (local_error
);
2438 if (g_error_matches (local_error
, E_CLIENT_ERROR
, E_CLIENT_ERROR_REPOSITORY_OFFLINE
)) {
2439 if (g_ascii_strcasecmp (extension_name
, E_SOURCE_EXTENSION_CALENDAR
) == 0) {
2440 alert_ident
= "calendar:prompt-no-contents-offline-calendar";
2441 } else if (g_ascii_strcasecmp (extension_name
, E_SOURCE_EXTENSION_MEMO_LIST
) == 0) {
2442 alert_ident
= "calendar:prompt-no-contents-offline-memos";
2443 } else if (g_ascii_strcasecmp (extension_name
, E_SOURCE_EXTENSION_TASK_LIST
) == 0) {
2444 alert_ident
= "calendar:prompt-no-contents-offline-tasks";
2445 } else if (g_ascii_strcasecmp (extension_name
, E_SOURCE_EXTENSION_ADDRESS_BOOK
) == 0) {
2450 e_alert_sink_thread_job_set_alert_ident (job_data
, alert_ident
);
2452 g_propagate_error (error
, local_error
);
2456 e_util_open_client_sync (EAlertSinkThreadJobData
*job_data
,
2457 EClientCache
*client_cache
,
2458 const gchar
*extension_name
,
2460 guint32 wait_for_connected_seconds
,
2461 GCancellable
*cancellable
,
2464 gchar
*description
= NULL
, *alert_ident
= NULL
, *alert_arg_0
= NULL
;
2465 EClient
*client
= NULL
;
2466 ESourceRegistry
*registry
;
2467 gchar
*display_name
;
2468 GError
*local_error
= NULL
;
2470 registry
= e_client_cache_ref_registry (client_cache
);
2471 display_name
= e_util_get_source_full_name (registry
, source
);
2472 g_clear_object (®istry
);
2474 g_warn_if_fail (e_util_get_open_source_job_info (extension_name
,
2475 display_name
, &description
, &alert_ident
, &alert_arg_0
));
2477 g_free (display_name
);
2479 camel_operation_push_message (cancellable
, "%s", description
);
2481 client
= e_client_cache_get_client_sync (client_cache
, source
, extension_name
, wait_for_connected_seconds
, cancellable
, &local_error
);
2483 camel_operation_pop_message (cancellable
);
2486 e_alert_sink_thread_job_set_alert_ident (job_data
, alert_ident
);
2487 e_alert_sink_thread_job_set_alert_arg_0 (job_data
, alert_arg_0
);
2489 e_util_propagate_open_source_job_error (job_data
, extension_name
, local_error
, error
);
2492 g_free (description
);
2493 g_free (alert_ident
);
2494 g_free (alert_arg_0
);
2500 * e_binding_transform_color_to_string:
2501 * @binding: a #GBinding
2502 * @source_value: a #GValue of type #GDK_TYPE_COLOR
2503 * @target_value: a #GValue of type #G_TYPE_STRING
2504 * @not_used: not used
2506 * Transforms a #GdkColor value to a color string specification.
2508 * Returns: %TRUE always
2511 e_binding_transform_color_to_string (GBinding
*binding
,
2512 const GValue
*source_value
,
2513 GValue
*target_value
,
2516 const GdkColor
*color
;
2519 g_return_val_if_fail (G_IS_BINDING (binding
), FALSE
);
2521 color
= g_value_get_boxed (source_value
);
2523 g_value_set_string (target_value
, "");
2525 /* encode color manually, because css styles expect colors in #rrggbb,
2526 * not in #rrrrggggbbbb, which is a result of gdk_color_to_string()
2528 string
= g_strdup_printf (
2530 (gint
) color
->red
* 256 / 65536,
2531 (gint
) color
->green
* 256 / 65536,
2532 (gint
) color
->blue
* 256 / 65536);
2533 g_value_set_string (target_value
, string
);
2541 * e_binding_transform_string_to_color:
2542 * @binding: a #GBinding
2543 * @source_value: a #GValue of type #G_TYPE_STRING
2544 * @target_value: a #GValue of type #GDK_TYPE_COLOR
2545 * @not_used: not used
2547 * Transforms a color string specification to a #GdkColor.
2549 * Returns: %TRUE if color string specification was valid
2552 e_binding_transform_string_to_color (GBinding
*binding
,
2553 const GValue
*source_value
,
2554 GValue
*target_value
,
2558 const gchar
*string
;
2559 gboolean success
= FALSE
;
2561 g_return_val_if_fail (G_IS_BINDING (binding
), FALSE
);
2563 string
= g_value_get_string (source_value
);
2564 if (gdk_color_parse (string
, &color
)) {
2565 g_value_set_boxed (target_value
, &color
);
2573 * e_binding_transform_source_to_uid:
2574 * @binding: a #GBinding
2575 * @source_value: a #GValue of type #E_TYPE_SOURCE
2576 * @target_value: a #GValue of type #G_TYPE_STRING
2577 * @registry: an #ESourceRegistry
2579 * Transforms an #ESource object to its UID string.
2581 * Returns: %TRUE if @source_value was an #ESource object
2584 e_binding_transform_source_to_uid (GBinding
*binding
,
2585 const GValue
*source_value
,
2586 GValue
*target_value
,
2587 ESourceRegistry
*registry
)
2590 const gchar
*string
;
2591 gboolean success
= FALSE
;
2593 g_return_val_if_fail (G_IS_BINDING (binding
), FALSE
);
2594 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry
), FALSE
);
2596 source
= g_value_get_object (source_value
);
2597 if (E_IS_SOURCE (source
)) {
2598 string
= e_source_get_uid (source
);
2599 g_value_set_string (target_value
, string
);
2607 * e_binding_transform_uid_to_source:
2608 * @binding: a #GBinding
2609 * @source_value: a #GValue of type #G_TYPE_STRING
2610 * @target_value: a #GValue of type #E_TYPE_SOURCe
2611 * @registry: an #ESourceRegistry
2613 * Transforms an #ESource UID string to the corresponding #ESource object
2616 * Returns: %TRUE if @registry had an #ESource object with a matching
2620 e_binding_transform_uid_to_source (GBinding
*binding
,
2621 const GValue
*source_value
,
2622 GValue
*target_value
,
2623 ESourceRegistry
*registry
)
2626 const gchar
*string
;
2627 gboolean success
= FALSE
;
2629 g_return_val_if_fail (G_IS_BINDING (binding
), FALSE
);
2630 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry
), FALSE
);
2632 string
= g_value_get_string (source_value
);
2633 if (string
== NULL
|| *string
== '\0')
2636 source
= e_source_registry_ref_source (registry
, string
);
2637 if (source
!= NULL
) {
2638 g_value_take_object (target_value
, source
);
2646 * e_binding_transform_text_non_null:
2647 * @binding: a #GBinding
2648 * @source_value: a #GValue of type #G_TYPE_STRING
2649 * @target_value: a #GValue of type #G_TYPE_STRING
2650 * @user_data: custom user data, unused
2652 * Transforms a text value to a text value which is never NULL;
2653 * an empty string is used instead of NULL.
2655 * Returns: %TRUE on success
2658 e_binding_transform_text_non_null (GBinding
*binding
,
2659 const GValue
*source_value
,
2660 GValue
*target_value
,
2665 g_return_val_if_fail (G_IS_BINDING (binding
), FALSE
);
2666 g_return_val_if_fail (source_value
!= NULL
, FALSE
);
2667 g_return_val_if_fail (target_value
!= NULL
, FALSE
);
2669 str
= g_value_get_string (source_value
);
2673 g_value_set_string (target_value
, str
);
2679 * e_binding_bind_object_text_property:
2680 * @source: the source #GObject
2681 * @source_property: the text property on the source to bind
2682 * @target: the target #GObject
2683 * @target_property: the text property on the target to bind
2684 * @flags: flags to pass to e_binding_bind_property_full()
2686 * Installs a new text property object binding, using e_binding_bind_property_full(),
2687 * with transform functions to make sure that a NULL pointer is not
2688 * passed in either way. Instead of NULL an empty string is used.
2690 * Returns: the #GBinding instance representing the binding between the two #GObject instances;
2691 * there applies the same rules to it as for the result of e_binding_bind_property_full().
2694 e_binding_bind_object_text_property (gpointer source
,
2695 const gchar
*source_property
,
2697 const gchar
*target_property
,
2698 GBindingFlags flags
)
2700 GObjectClass
*klass
;
2701 GParamSpec
*property
;
2703 g_return_val_if_fail (G_IS_OBJECT (source
), NULL
);
2704 g_return_val_if_fail (source_property
!= NULL
, NULL
);
2705 g_return_val_if_fail (G_IS_OBJECT (target
), NULL
);
2706 g_return_val_if_fail (target_property
!= NULL
, NULL
);
2708 klass
= G_OBJECT_GET_CLASS (source
);
2709 property
= g_object_class_find_property (klass
, source_property
);
2710 g_return_val_if_fail (property
!= NULL
, NULL
);
2711 g_return_val_if_fail (property
->value_type
== G_TYPE_STRING
, NULL
);
2713 klass
= G_OBJECT_GET_CLASS (target
);
2714 property
= g_object_class_find_property (klass
, target_property
);
2715 g_return_val_if_fail (property
!= NULL
, NULL
);
2716 g_return_val_if_fail (property
->value_type
== G_TYPE_STRING
, NULL
);
2718 return e_binding_bind_property_full (source
, source_property
,
2719 target
, target_property
,
2721 e_binding_transform_text_non_null
,
2722 e_binding_transform_text_non_null
,
2726 typedef struct _EConnectNotifyData
{
2727 GConnectFlags flags
;
2730 GCallback c_handler
;
2732 } EConnectNotifyData
;
2734 static EConnectNotifyData
*
2735 e_connect_notify_data_new (GCallback c_handler
,
2737 guint32 connect_flags
)
2739 EConnectNotifyData
*connect_data
;
2741 connect_data
= g_new0 (EConnectNotifyData
, 1);
2742 connect_data
->flags
= connect_flags
;
2743 connect_data
->c_handler
= c_handler
;
2744 connect_data
->user_data
= user_data
;
2746 return connect_data
;
2750 e_connect_notify_data_free (EConnectNotifyData
*data
)
2755 if (data
->old_value
) {
2756 g_value_unset (data
->old_value
);
2757 g_free (data
->old_value
);
2763 e_value_equal (GValue
*value1
,
2766 if (value1
== value2
)
2769 if (!value1
|| !value2
)
2772 #define testType(_uc,_lc) G_STMT_START { \
2773 if (G_VALUE_HOLDS_ ## _uc (value1)) \
2774 return g_value_get_ ## _lc (value1) == g_value_get_ ## _lc (value2); \
2777 testType (BOOLEAN
, boolean
);
2778 testType (BOXED
, boxed
);
2779 testType (CHAR
, schar
);
2780 testType (DOUBLE
, double);
2781 testType (ENUM
, enum);
2782 testType (FLAGS
, flags
);
2783 testType (FLOAT
, float);
2784 testType (GTYPE
, gtype
);
2785 testType (INT
, int);
2786 testType (INT64
, int64
);
2787 testType (LONG
, long);
2788 testType (OBJECT
, object
);
2789 testType (POINTER
, pointer
);
2790 testType (UCHAR
, uchar
);
2791 testType (UINT
, uint
);
2792 testType (UINT64
, uint64
);
2793 testType (ULONG
, ulong
);
2797 if (G_VALUE_HOLDS_PARAM (value1
)) {
2798 GParamSpec
*param1
, *param2
;
2800 param1
= g_value_get_param (value1
);
2801 param2
= g_value_get_param (value2
);
2803 return param1
&& param2
&&
2804 g_strcmp0 (param1
->name
, param2
->name
) == 0 &&
2805 param1
->flags
== param2
->flags
&&
2806 param1
->value_type
== param2
->value_type
&&
2807 param1
->owner_type
== param2
->owner_type
;
2808 } else if (G_VALUE_HOLDS_STRING (value1
)) {
2809 const gchar
*string1
, *string2
;
2811 string1
= g_value_get_string (value1
);
2812 string2
= g_value_get_string (value2
);
2814 return g_strcmp0 (string1
, string2
) == 0;
2815 } else if (G_VALUE_HOLDS_VARIANT (value1
)) {
2816 GVariant
*variant1
, *variant2
;
2818 variant1
= g_value_get_variant (value1
);
2819 variant2
= g_value_get_variant (value2
);
2821 return variant1
== variant2
||
2822 (variant1
&& variant2
&& g_variant_equal (variant1
, variant2
));
2829 e_signal_connect_notify_cb (gpointer instance
,
2833 EConnectNotifyData
*connect_data
= user_data
;
2836 g_return_if_fail (connect_data
!= NULL
);
2838 value
= g_new0 (GValue
, 1);
2839 g_value_init (value
, param
->value_type
);
2840 g_object_get_property (instance
, param
->name
, value
);
2842 if (!e_value_equal (connect_data
->old_value
, value
)) {
2843 typedef void (* NotifyCBType
) (gpointer instance
, GParamSpec
*param
, gpointer user_data
);
2844 NotifyCBType c_handler
= (NotifyCBType
) connect_data
->c_handler
;
2846 if (connect_data
->old_value
) {
2847 g_value_unset (connect_data
->old_value
);
2848 g_free (connect_data
->old_value
);
2850 connect_data
->old_value
= value
;
2852 if (connect_data
->flags
== G_CONNECT_SWAPPED
) {
2853 c_handler (connect_data
->user_data
, param
, instance
);
2855 c_handler (instance
, param
, connect_data
->user_data
);
2858 g_value_unset (value
);
2864 * e_signal_connect_notify:
2866 * This installs a special handler in front of @c_handler, which will
2867 * call the @c_handler only if the property value changed since the last
2868 * time it was checked. Due to this, these handlers cannot be disconnected
2869 * by by any of the g_signal_handlers_* functions, but only with the returned
2870 * handler ID. A convenient e_signal_disconnect_notify_handler() was added
2871 * to make it easier.
2874 e_signal_connect_notify (gpointer instance
,
2875 const gchar
*notify_name
,
2876 GCallback c_handler
,
2879 EConnectNotifyData
*connect_data
;
2881 g_return_val_if_fail (g_str_has_prefix (notify_name
, "notify::"), 0);
2883 connect_data
= e_connect_notify_data_new (c_handler
, user_data
, 0);
2885 return g_signal_connect_data (instance
,
2887 G_CALLBACK (e_signal_connect_notify_cb
),
2889 (GClosureNotify
) e_connect_notify_data_free
,
2894 * e_signal_connect_notify_after:
2896 * This installs a special handler in front of @c_handler, which will
2897 * call the @c_handler only if the property value changed since the last
2898 * time it was checked. Due to this, these handlers cannot be disconnected
2899 * by by any of the g_signal_handlers_* functions, but only with the returned
2900 * handler ID. A convenient e_signal_disconnect_notify_handler() was added
2901 * to make it easier.
2904 e_signal_connect_notify_after (gpointer instance
,
2905 const gchar
*notify_name
,
2906 GCallback c_handler
,
2909 EConnectNotifyData
*connect_data
;
2911 g_return_val_if_fail (g_str_has_prefix (notify_name
, "notify::"), 0);
2913 connect_data
= e_connect_notify_data_new (c_handler
, user_data
, G_CONNECT_AFTER
);
2915 return g_signal_connect_data (instance
,
2917 G_CALLBACK (e_signal_connect_notify_cb
),
2919 (GClosureNotify
) e_connect_notify_data_free
,
2924 * e_signal_connect_notify_swapped:
2926 * This installs a special handler in front of @c_handler, which will
2927 * call the @c_handler only if the property value changed since the last
2928 * time it was checked. Due to this, these handlers cannot be disconnected
2929 * by by any of the g_signal_handlers_* functions, but only with the returned
2930 * handler ID. A convenient e_signal_disconnect_notify_handler() was added
2931 * to make it easier.
2934 e_signal_connect_notify_swapped (gpointer instance
,
2935 const gchar
*notify_name
,
2936 GCallback c_handler
,
2939 EConnectNotifyData
*connect_data
;
2941 g_return_val_if_fail (g_str_has_prefix (notify_name
, "notify::"), 0);
2943 connect_data
= e_connect_notify_data_new (c_handler
, user_data
, G_CONNECT_SWAPPED
);
2945 return g_signal_connect_data (instance
,
2947 G_CALLBACK (e_signal_connect_notify_cb
),
2949 (GClosureNotify
) e_connect_notify_data_free
,
2954 * e_signal_connect_notify_object:
2956 * This installs a special handler in front of @c_handler, which will
2957 * call the @c_handler only if the property value changed since the last
2958 * time it was checked. Due to this, these handlers cannot be disconnected
2959 * by by any of the g_signal_handlers_* functions, but only with the returned
2960 * handler ID. A convenient e_signal_disconnect_notify_handler() was added
2961 * to make it easier.
2964 e_signal_connect_notify_object (gpointer instance
,
2965 const gchar
*notify_name
,
2966 GCallback c_handler
,
2968 GConnectFlags connect_flags
)
2970 EConnectNotifyData
*connect_data
;
2973 g_return_val_if_fail (g_str_has_prefix (notify_name
, "notify::"), 0);
2976 if ((connect_flags
& G_CONNECT_SWAPPED
) != 0)
2977 return e_signal_connect_notify_swapped (instance
, notify_name
, c_handler
, gobject
);
2978 else if ((connect_flags
& G_CONNECT_AFTER
) != 0)
2979 e_signal_connect_notify_after (instance
, notify_name
, c_handler
, gobject
);
2981 g_warn_if_fail (connect_flags
== 0);
2983 return e_signal_connect_notify (instance
, notify_name
, c_handler
, gobject
);
2986 g_return_val_if_fail (G_IS_OBJECT (gobject
), 0);
2988 connect_data
= e_connect_notify_data_new (c_handler
, gobject
, connect_flags
& G_CONNECT_SWAPPED
);
2989 closure
= g_cclosure_new (
2990 G_CALLBACK (e_signal_connect_notify_cb
),
2992 (GClosureNotify
) e_connect_notify_data_free
);
2994 g_object_watch_closure (G_OBJECT (gobject
), closure
);
2996 return g_signal_connect_closure (instance
,
2999 connect_flags
& G_CONNECT_AFTER
);
3003 * e_signal_disconnect_notify_handler:
3005 * Convenient handler disconnect function to be used with
3006 * returned handler IDs from:
3007 * e_signal_connect_notify()
3008 * e_signal_connect_notify_after()
3009 * e_signal_connect_notify_swapped()
3010 * e_signal_connect_notify_object()
3011 * but not necessarily only with these functions.
3014 e_signal_disconnect_notify_handler (gpointer instance
,
3017 g_return_if_fail (instance
!= NULL
);
3018 g_return_if_fail (handler_id
!= NULL
);
3023 g_signal_handler_disconnect (instance
, *handler_id
);
3027 static GMutex settings_hash_lock
;
3028 static GHashTable
*settings_hash
= NULL
;
3031 * e_util_ref_settings:
3032 * @schema_id: the id of the schema to reference settings for
3034 * Either returns an existing referenced #GSettings object for the given @schema_id,
3035 * or creates a new one and remembers it for later use, to avoid having too many
3036 * #GSettings objects created for the same @schema_id.
3038 * Returns: A #GSettings for the given @schema_id. The returned #GSettings object
3039 * is referenced, thus free it with g_object_unref() when done with it.
3044 e_util_ref_settings (const gchar
*schema_id
)
3046 GSettings
*settings
;
3048 g_return_val_if_fail (schema_id
!= NULL
, NULL
);
3049 g_return_val_if_fail (*schema_id
, NULL
);
3051 g_mutex_lock (&settings_hash_lock
);
3053 if (!settings_hash
) {
3054 settings_hash
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, g_object_unref
);
3057 settings
= g_hash_table_lookup (settings_hash
, schema_id
);
3059 settings
= g_settings_new (schema_id
);
3060 g_hash_table_insert (settings_hash
, g_strdup (schema_id
), settings
);
3064 g_object_ref (settings
);
3066 g_mutex_unlock (&settings_hash_lock
);
3072 * e_util_cleanup_settings:
3074 * Frees all the memory taken by e_util_ref_settings().
3079 e_util_cleanup_settings (void)
3081 g_mutex_lock (&settings_hash_lock
);
3083 if (settings_hash
) {
3084 g_hash_table_destroy (settings_hash
);
3085 settings_hash
= NULL
;
3088 g_mutex_unlock (&settings_hash_lock
);
3092 * e_util_prompt_user:
3093 * @parent: parent window
3094 * @settings_schema: name of the settings schema where @promptkey belongs.
3095 * @promptkey: settings key to check if we should prompt the user or not.
3096 * @tag: e_alert tag.
3098 * Convenience function to query the user with a Yes/No dialog and a
3099 * "Do not show this dialog again" checkbox. If the user checks that
3100 * checkbox, then @promptkey is set to %FALSE, otherwise it is set to
3103 * Returns %TRUE if the user clicks Yes or %FALSE otherwise.
3106 e_util_prompt_user (GtkWindow
*parent
,
3107 const gchar
*settings_schema
,
3108 const gchar
*promptkey
,
3113 GtkWidget
*check
= NULL
;
3114 GtkWidget
*container
;
3117 GSettings
*settings
;
3118 EAlert
*alert
= NULL
;
3120 settings
= e_util_ref_settings (settings_schema
);
3122 if (promptkey
&& !g_settings_get_boolean (settings
, promptkey
)) {
3123 g_object_unref (settings
);
3128 alert
= e_alert_new_valist (tag
, ap
);
3131 dialog
= e_alert_dialog_new (parent
, alert
);
3132 g_object_unref (alert
);
3134 container
= e_alert_dialog_get_content_area (E_ALERT_DIALOG (dialog
));
3137 check
= gtk_check_button_new_with_mnemonic (
3138 _("_Do not show this message again"));
3139 gtk_box_pack_start (
3140 GTK_BOX (container
), check
, FALSE
, FALSE
, 0);
3141 gtk_widget_show (check
);
3144 button
= gtk_dialog_run (GTK_DIALOG (dialog
));
3146 g_settings_set_boolean (
3147 settings
, promptkey
,
3148 !gtk_toggle_button_get_active (
3149 GTK_TOGGLE_BUTTON (check
)));
3151 gtk_widget_destroy (dialog
);
3153 g_object_unref (settings
);
3155 return button
== GTK_RESPONSE_YES
;
3158 typedef struct _EUtilSimpleAsyncResultThreadData
{
3159 GSimpleAsyncResult
*simple
;
3160 GSimpleAsyncThreadFunc func
;
3161 GCancellable
*cancellable
;
3162 } EUtilSimpleAsyncResultThreadData
;
3165 e_util_simple_async_result_thread (gpointer data
,
3168 EUtilSimpleAsyncResultThreadData
*thread_data
= data
;
3169 GError
*error
= NULL
;
3171 g_return_if_fail (thread_data
!= NULL
);
3172 g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (thread_data
->simple
));
3173 g_return_if_fail (thread_data
->func
!= NULL
);
3175 if (g_cancellable_set_error_if_cancelled (thread_data
->cancellable
, &error
)) {
3176 g_simple_async_result_take_error (thread_data
->simple
, error
);
3178 thread_data
->func (thread_data
->simple
,
3179 g_async_result_get_source_object (G_ASYNC_RESULT (thread_data
->simple
)),
3180 thread_data
->cancellable
);
3183 g_simple_async_result_complete_in_idle (thread_data
->simple
);
3185 g_clear_object (&thread_data
->simple
);
3186 g_clear_object (&thread_data
->cancellable
);
3187 g_free (thread_data
);
3191 * e_util_run_simple_async_result_in_thread:
3192 * @simple: a #GSimpleAsyncResult
3193 * @func: a #GSimpleAsyncThreadFunc to execute in the thread
3194 * @cancellable: an optional #GCancellable, or %NULL
3196 * Similar to g_simple_async_result_run_in_thread(), except
3197 * it doesn't use GTask internally, thus doesn't block the GTask
3198 * thread pool with possibly long job.
3200 * It doesn't behave exactly the same as the g_simple_async_result_run_in_thread(),
3201 * the @cancellable checking is not done before the finish.
3206 e_util_run_simple_async_result_in_thread (GSimpleAsyncResult
*simple
,
3207 GSimpleAsyncThreadFunc func
,
3208 GCancellable
*cancellable
)
3210 static GThreadPool
*thread_pool
= NULL
;
3211 static GMutex thread_pool_mutex
;
3212 EUtilSimpleAsyncResultThreadData
*thread_data
;
3214 g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple
));
3215 g_return_if_fail (func
!= NULL
);
3217 g_mutex_lock (&thread_pool_mutex
);
3220 thread_pool
= g_thread_pool_new (e_util_simple_async_result_thread
, NULL
, 20, FALSE
, NULL
);
3222 thread_data
= g_new0 (EUtilSimpleAsyncResultThreadData
, 1);
3223 thread_data
->simple
= g_object_ref (simple
);
3224 thread_data
->func
= func
;
3225 thread_data
->cancellable
= cancellable
? g_object_ref (cancellable
) : NULL
;
3227 g_thread_pool_push (thread_pool
, thread_data
, NULL
);
3229 g_mutex_unlock (&thread_pool_mutex
);
3233 * e_util_is_running_gnome:
3235 * Returns: Whether the current running desktop environment is GNOME.
3240 e_util_is_running_gnome (void)
3245 static gint runs_gnome
= -1;
3247 if (runs_gnome
== -1) {
3248 runs_gnome
= g_strcmp0 (g_getenv ("XDG_CURRENT_DESKTOP"), "GNOME") == 0 ? 1 : 0;
3250 GDesktopAppInfo
*app_info
;
3252 app_info
= g_desktop_app_info_new ("gnome-notifications-panel.desktop");
3257 g_clear_object (&app_info
);
3261 return runs_gnome
!= 0;
3266 * e_util_set_entry_issue_hint:
3267 * @entry: a #GtkEntry
3268 * @hint: (allow none): a hint to set, or %NULL to unset
3270 * Sets a @hint on the secondary @entry icon about an entered value issue,
3271 * or unsets it, when the @hint is %NULL.
3276 e_util_set_entry_issue_hint (GtkWidget
*entry
,
3281 g_return_if_fail (GTK_IS_ENTRY (entry
));
3283 eentry
= GTK_ENTRY (entry
);
3286 gtk_entry_set_icon_from_icon_name (eentry
, GTK_ENTRY_ICON_SECONDARY
, "dialog-warning");
3287 gtk_entry_set_icon_tooltip_text (eentry
, GTK_ENTRY_ICON_SECONDARY
, hint
);
3289 gtk_entry_set_icon_from_icon_name (eentry
, GTK_ENTRY_ICON_SECONDARY
, NULL
);
3290 gtk_entry_set_icon_tooltip_text (eentry
, GTK_ENTRY_ICON_SECONDARY
, NULL
);