src/mn-util.c (43442B) - raw
1 /*
2 * Mail Notification
3 * Copyright (C) 2003-2008 Jean-Yves Lefort <jylefort@brutele.be>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <stdio.h>
21 #include <string.h>
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <time.h>
25 #include <errno.h>
26 #include <gmodule.h>
27 #include <glib/gi18n.h>
28 #include <gobject/gvaluecollector.h>
29 #include <gnome.h>
30 #include <glade/glade.h>
31 #include "mn-util.h"
32 #include "mn-mailboxes.h"
33 #include "mn-shell.h"
34 #include "mn-conf.h"
35
36 typedef struct
37 {
38 GtkContainer *container;
39 const char *callback_prefix;
40 } ContainerCreateInterfaceConnectInfo;
41
42 enum
43 {
44 TARGET_URI_LIST,
45 TARGET_MOZ_URL
46 };
47
48 typedef struct
49 {
50 gpointer instance;
51 unsigned long id;
52 } SignalHandler;
53
54 typedef struct
55 {
56 GMainLoop *loop;
57 int response;
58 gboolean destroyed;
59 } RunNonmodalInfo;
60
61 void
62 mn_info (const char *format, ...)
63 {
64 va_list args;
65
66 g_return_if_fail(format != NULL);
67
68 va_start(args, format);
69 g_logv(NULL, G_LOG_LEVEL_INFO, format, args);
70 va_end(args);
71 }
72
73 void
74 mn_g_list_free_deep_custom (GList *list,
75 GFunc element_free_func,
76 gpointer user_data)
77 {
78 g_list_foreach(list, element_free_func, user_data);
79 g_list_free(list);
80 }
81
82 GSList *
83 mn_g_slist_append_elements (GSList *list, gpointer data, ...)
84 {
85 va_list args;
86
87 va_start(args, data);
88
89 while (data)
90 {
91 list = g_slist_append(list, data);
92 data = va_arg(args, gpointer);
93 }
94
95 va_end(args);
96
97 return list;
98 }
99
100 void
101 mn_g_slist_free_deep (GSList *list)
102 {
103 mn_g_slist_free_deep_custom(list, (GFunc) g_free, NULL);
104 }
105
106 void
107 mn_g_slist_free_deep_custom (GSList *list,
108 GFunc element_free_func,
109 gpointer user_data)
110 {
111 g_slist_foreach(list, element_free_func, user_data);
112 g_slist_free(list);
113 }
114
115 void
116 mn_g_slist_clear (GSList **list)
117 {
118 g_return_if_fail(list != NULL);
119
120 g_slist_free(*list);
121 *list = NULL;
122 }
123
124 void
125 mn_g_slist_clear_deep (GSList **list)
126 {
127 g_return_if_fail(list != NULL);
128
129 mn_g_slist_free_deep(*list);
130 *list = NULL;
131 }
132
133 void
134 mn_g_slist_clear_deep_custom (GSList **list,
135 GFunc element_free_func,
136 gpointer user_data)
137 {
138 g_return_if_fail(list != NULL);
139 g_return_if_fail(element_free_func != NULL);
140
141 mn_g_slist_free_deep_custom(*list, element_free_func, user_data);
142 *list = NULL;
143 }
144
145 /**
146 * mn_g_slist_delete_link_deep_custom:
147 * @list: a #GSList of @element_free_func-freeable objects
148 * @link_: an element in the #GSList
149 * @element_free_func: a function to free @link_->data
150 * @user_data: user data to pass to @element_free_func
151 *
152 * Equivalent of g_slist_delete_link() for a list of
153 * @element_free_func-freeable objects.
154 *
155 * Return value: new head of @list.
156 **/
157 GSList *
158 mn_g_slist_delete_link_deep_custom (GSList *list,
159 GSList *link_,
160 GFunc element_free_func,
161 gpointer user_data)
162 {
163 g_return_val_if_fail(element_free_func != NULL, NULL);
164
165 if (link_)
166 element_free_func(link_->data, user_data);
167
168 return g_slist_delete_link(list, link_);
169 }
170
171 static int
172 str_slist_compare_func (gconstpointer a, gconstpointer b)
173 {
174 return strcmp(a, b);
175 }
176
177 GSList *
178 mn_g_str_slist_find (GSList *list, const char *str)
179 {
180 g_return_val_if_fail(str != NULL, NULL);
181
182 return g_slist_find_custom(list, str, str_slist_compare_func);
183 }
184
185 void
186 mn_g_object_list_free (GList *list)
187 {
188 mn_g_list_free_deep_custom(list, (GFunc) g_object_unref, NULL);
189 }
190
191 GSList *
192 mn_g_object_slist_ref (GSList *list)
193 {
194 g_slist_foreach(list, (GFunc) g_object_ref, NULL);
195 return list;
196 }
197
198 GSList *
199 mn_g_object_slist_copy (GSList *list)
200 {
201 return g_slist_copy(mn_g_object_slist_ref(list));
202 }
203
204 /**
205 * mn_g_object_slist_free:
206 * @list: a #GSList of #GObject instances
207 *
208 * Equivalent of mn_g_object_list_free() for a singly-linked list.
209 **/
210 void
211 mn_g_object_slist_free (GSList *list)
212 {
213 mn_g_slist_free_deep_custom(list, (GFunc) g_object_unref, NULL);
214 }
215
216 void
217 mn_g_object_slist_clear (GSList **list)
218 {
219 g_return_if_fail(list != NULL);
220
221 mn_g_object_slist_free(*list);
222 *list = NULL;
223 }
224
225 /**
226 * mn_str_isnumeric:
227 * @str: the ASCII string to test
228 *
229 * Tests if the ASCII string @str is numeric. Implemented by calling
230 * g_ascii_isdigit() on each character of @str.
231 *
232 * Return value: %TRUE if the ASCII string @str only consists of digits
233 **/
234 gboolean
235 mn_str_isnumeric (const char *str)
236 {
237 int i;
238
239 g_return_val_if_fail(str != NULL, FALSE);
240
241 for (i = 0; str[i]; i++)
242 if (! g_ascii_isdigit(str[i]))
243 return FALSE;
244
245 return i > 0;
246 }
247
248 gboolean
249 mn_str_ishex (const char *str)
250 {
251 int i;
252
253 g_return_val_if_fail(str != NULL, FALSE);
254
255 for (i = 0; str[i]; i++)
256 if (! g_ascii_isxdigit(str[i]))
257 return FALSE;
258
259 return i > 0;
260 }
261
262 /**
263 * mn_strstr_span:
264 * @big: a string.
265 * @little: a string to search for in @big.
266 *
267 * Locates the first occurrence of @little in @big.
268 *
269 * Return value: a pointer to the character following the first
270 * occurrence of @little in @big, or %NULL if @little does not appear
271 * in @big.
272 **/
273 char *
274 mn_strstr_span (const char *big, const char *little)
275 {
276 char *s;
277
278 g_return_val_if_fail(big != NULL, NULL);
279 g_return_val_if_fail(little != NULL, NULL);
280
281 s = strstr(big, little);
282 if (s)
283 s += strlen(little);
284
285 return s;
286 }
287
288 GdkPixbuf *
289 mn_pixbuf_new (const char *filename)
290 {
291 GdkPixbuf *pixbuf;
292 GError *err = NULL;
293
294 g_return_val_if_fail(filename != NULL, NULL);
295
296 pixbuf = gdk_pixbuf_new_from_file(filename, &err);
297 if (! pixbuf)
298 {
299 mn_show_fatal_error_dialog(NULL, "Unable to load image \"%s\" (%s).", filename, err->message);
300 g_error_free(err);
301 }
302
303 return pixbuf;
304 }
305
306 static GladeXML *
307 mn_glade_xml_new (const char *filename, const char *root, const char *domain)
308 {
309 GladeXML *xml;
310
311 g_return_val_if_fail(filename != NULL, NULL);
312
313 xml = glade_xml_new(filename, root, domain);
314 if (! xml)
315 mn_show_fatal_error_dialog(NULL, "Unable to load interface \"%s\".", filename);
316
317 return xml;
318 }
319
320 static GtkWidget *
321 mn_glade_xml_get_widget (GladeXML *xml, const char *widget_name)
322 {
323 GtkWidget *widget;
324
325 g_return_val_if_fail(GLADE_IS_XML(xml), NULL);
326 g_return_val_if_fail(widget_name != NULL, NULL);
327
328 widget = glade_xml_get_widget(xml, widget_name);
329 if (! widget)
330 mn_show_fatal_error_dialog(NULL, "Widget \"%s\" not found in interface \"%s\".", widget_name, xml->filename);
331
332 return widget;
333 }
334
335 static void
336 create_interface_connect_cb (const char *handler_name,
337 GObject *object,
338 const char *signal_name,
339 const char *signal_data,
340 GObject *connect_object,
341 gboolean after,
342 gpointer user_data)
343 {
344 static GModule *module = NULL;
345 ContainerCreateInterfaceConnectInfo *info = user_data;
346 char *cb_name;
347 GCallback cb;
348 GConnectFlags flags;
349
350 if (! module)
351 {
352 module = g_module_open(NULL, 0);
353 if (! module)
354 mn_show_fatal_error_dialog(NULL, "Unable to open the program as a module (%s).", g_module_error());
355 }
356
357 cb_name = g_strconcat(info->callback_prefix, handler_name, NULL);
358 if (! g_module_symbol(module, cb_name, (gpointer) &cb))
359 mn_show_fatal_error_dialog(NULL, "Signal handler \"%s\" not found.", cb_name);
360 g_free(cb_name);
361
362 flags = G_CONNECT_SWAPPED;
363 if (after)
364 flags |= G_CONNECT_AFTER;
365
366 g_signal_connect_data(object, signal_name, cb, info->container, NULL, flags);
367 }
368
369 void
370 mn_container_create_interface (GtkContainer *container,
371 const char *filename,
372 const char *child_name,
373 const char *callback_prefix,
374 ...)
375 {
376 GladeXML *xml;
377 GtkWidget *child;
378 ContainerCreateInterfaceConnectInfo info;
379 va_list args;
380 const char *widget_name;
381
382 g_return_if_fail(GTK_IS_CONTAINER(container));
383 g_return_if_fail(filename != NULL);
384 g_return_if_fail(child_name != NULL);
385 g_return_if_fail(callback_prefix != NULL);
386
387 xml = mn_glade_xml_new(filename, child_name, NULL);
388 child = mn_glade_xml_get_widget(xml, child_name);
389
390 if (GTK_IS_DIALOG(container))
391 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(container)->vbox), child, TRUE, TRUE, 0);
392 else
393 gtk_container_add(container, child);
394
395 info.container = container;
396 info.callback_prefix = callback_prefix;
397 glade_xml_signal_autoconnect_full(xml, create_interface_connect_cb, &info);
398
399 va_start(args, callback_prefix);
400
401 while ((widget_name = va_arg(args, const char *)))
402 {
403 GtkWidget **widget;
404
405 widget = va_arg(args, GtkWidget **);
406 g_return_if_fail(widget != NULL);
407
408 *widget = mn_glade_xml_get_widget(xml, widget_name);
409 }
410
411 va_end(args);
412
413 g_object_unref(xml);
414 }
415
416 GtkWindow *
417 mn_widget_get_parent_window (GtkWidget *widget)
418 {
419 GtkWidget *toplevel;
420
421 g_return_val_if_fail(GTK_IS_WIDGET(widget), NULL);
422
423 toplevel = gtk_widget_get_toplevel(widget);
424
425 return GTK_WIDGET_TOPLEVEL(toplevel) ? GTK_WINDOW(toplevel) : NULL;
426 }
427
428 static void
429 file_chooser_dialog_file_activated_h (GtkFileChooser *chooser,
430 gpointer user_data)
431 {
432 int accept_id = GPOINTER_TO_INT(user_data);
433
434 gtk_dialog_response(GTK_DIALOG(chooser), accept_id);
435 }
436
437 static void
438 file_chooser_dialog_response_h (GtkDialog *dialog,
439 int response_id,
440 gpointer user_data)
441 {
442 int accept_id = GPOINTER_TO_INT(user_data);
443
444 if (response_id == accept_id)
445 {
446 char *uri;
447
448 uri = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
449 if (uri)
450 g_free(uri);
451 else
452 g_signal_stop_emission_by_name(dialog, "response");
453 }
454 }
455
456 /**
457 * mn_file_chooser_dialog_allow_select_folder:
458 * @dialog: a #GtkFileChooserDialog
459 * @accept_id: the "accept" response ID (must not be
460 * GTK_RESPONSE_ACCEPT, GTK_RESPONSE_OK, GTK_RESPONSE_YES
461 * or GTK_RESPONSE_APPLY)
462 *
463 * Allows @dialog to pick a file (%GTK_FILE_CHOOSER_ACTION_OPEN) or
464 * select a folder (%GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) at the
465 * same time.
466 *
467 * Can be removed after
468 * http://bugzilla.gnome.org/show_bug.cgi?id=136294 is fixed.
469 **/
470 void
471 mn_file_chooser_dialog_allow_select_folder (GtkFileChooserDialog *dialog,
472 int accept_id)
473 {
474 g_return_if_fail(GTK_IS_FILE_CHOOSER_DIALOG(dialog));
475 g_return_if_fail(gtk_file_chooser_get_action(GTK_FILE_CHOOSER(dialog)) == GTK_FILE_CHOOSER_ACTION_OPEN);
476 g_return_if_fail(! (accept_id == GTK_RESPONSE_ACCEPT
477 || accept_id == GTK_RESPONSE_OK
478 || accept_id == GTK_RESPONSE_YES
479 || accept_id == GTK_RESPONSE_APPLY));
480
481 g_object_connect(dialog,
482 "signal::file-activated", file_chooser_dialog_file_activated_h, GINT_TO_POINTER(accept_id),
483 "signal::response", file_chooser_dialog_response_h, GINT_TO_POINTER(accept_id),
484 NULL);
485 }
486
487 static gboolean
488 scrolled_window_drag_motion_h (GtkWidget *widget,
489 GdkDragContext *drag_context,
490 int x,
491 int y,
492 unsigned int time_,
493 gpointer user_data)
494 {
495 GtkAdjustment *adjustment;
496
497 adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(widget));
498 gtk_adjustment_set_value(adjustment, (double) y / (widget->allocation.height - 2) * (adjustment->upper - adjustment->page_size));
499
500 return TRUE; /* we're forcibly in a drop zone */
501 }
502
503 static void
504 drag_data_received_h (GtkWidget *widget,
505 GdkDragContext *drag_context,
506 int x,
507 int y,
508 GtkSelectionData *selection_data,
509 unsigned int info,
510 unsigned int time_,
511 gpointer user_data)
512 {
513 switch (info)
514 {
515 case TARGET_URI_LIST:
516 {
517 char **uriv;
518 int i;
519 GSList *invalid_uri_list = NULL;
520
521 uriv = gtk_selection_data_get_uris(selection_data);
522 if (! uriv)
523 {
524 mn_show_error_dialog(mn_widget_get_parent_window(widget),
525 _("A drag and drop error has occurred"),
526 _("An invalid location list has been received."));
527 return;
528 }
529
530 for (i = 0; uriv[i]; i++)
531 if (*uriv[i])
532 {
533 MNMailbox *mailbox;
534
535 mailbox = mn_mailbox_new_from_uri(uriv[i]);
536 if (mailbox)
537 {
538 mn_mailbox_seal(mailbox);
539 mn_mailboxes_queue_add(mn_shell->mailboxes, mailbox);
540 g_object_unref(mailbox);
541 }
542 else
543 invalid_uri_list = g_slist_append(invalid_uri_list, uriv[i]);
544 }
545
546 if (invalid_uri_list)
547 {
548 mn_show_invalid_uri_list_dialog(mn_widget_get_parent_window(widget), _("A drag and drop error has occurred"), invalid_uri_list);
549 g_slist_free(invalid_uri_list);
550 }
551
552 g_strfreev(uriv);
553 }
554 break;
555
556 case TARGET_MOZ_URL:
557 {
558 GString *url;
559 const guint16 *char_data;
560 int char_len;
561 int i;
562 MNMailbox *mailbox;
563
564 /* text/x-moz-url is encoded in UCS-2 but in format 8: broken */
565 if (selection_data->format != 8 || selection_data->length <= 0 || (selection_data->length % 2) != 0)
566 {
567 mn_show_error_dialog(mn_widget_get_parent_window(widget),
568 _("A drag and drop error has occurred"),
569 _("An invalid Mozilla location has been received."));
570 return;
571 }
572
573 char_data = (const guint16 *) selection_data->data;
574 char_len = selection_data->length / 2;
575
576 url = g_string_new(NULL);
577 for (i = 0; i < char_len && char_data[i] != '\n'; i++)
578 g_string_append_unichar(url, char_data[i]);
579
580 g_assert(mn_shell != NULL);
581
582 mailbox = mn_mailbox_new_from_uri(url->str);
583 if (mailbox)
584 {
585 mn_mailbox_seal(mailbox);
586 mn_mailboxes_queue_add(mn_shell->mailboxes, mailbox);
587 g_object_unref(mailbox);
588 }
589 else
590 mn_show_invalid_uri_dialog(mn_widget_get_parent_window(widget), _("A drag and drop error has occurred"), url->str);
591
592 g_string_free(url, TRUE);
593 }
594 break;
595 }
596 }
597
598 /**
599 * mn_setup_dnd:
600 * @widget: a widget to setup mailbox drag-and-drop for
601 *
602 * Configures @widget so that when mailboxes are dropped on it, they
603 * will be added to the Mail Notification mailbox list.
604 **/
605 void
606 mn_setup_dnd (GtkWidget *widget)
607 {
608 static const GtkTargetEntry targets[] = {
609 { "text/uri-list", 0, TARGET_URI_LIST },
610 { "text/x-moz-url", 0, TARGET_MOZ_URL }
611 };
612
613 g_return_if_fail(GTK_IS_WIDGET(widget));
614
615 gtk_drag_dest_set(widget,
616 GTK_DEST_DEFAULT_ALL,
617 targets,
618 G_N_ELEMENTS(targets),
619 GDK_ACTION_COPY);
620
621 if (GTK_IS_SCROLLED_WINDOW(widget))
622 g_signal_connect(widget,
623 "drag-motion",
624 G_CALLBACK(scrolled_window_drag_motion_h),
625 NULL);
626
627 g_signal_connect(widget,
628 "drag-data-received",
629 G_CALLBACK(drag_data_received_h),
630 NULL);
631 }
632
633 gboolean
634 mn_parse_gnome_copied_files (const char *gnome_copied_files,
635 MNGnomeCopiedFilesType *type,
636 GSList **uri_list)
637 {
638 char **strv;
639 gboolean status = FALSE;
640
641 g_return_val_if_fail(gnome_copied_files != NULL, FALSE);
642 g_return_val_if_fail(type != NULL, FALSE);
643 g_return_val_if_fail(uri_list != NULL, FALSE);
644
645 strv = g_strsplit(gnome_copied_files, "\n", 0);
646 if (strv[0])
647 {
648 int i;
649
650 if (! strcmp(strv[0], "cut"))
651 {
652 status = TRUE;
653 *type = MN_GNOME_COPIED_FILES_CUT;
654 }
655 else if (! strcmp(strv[0], "copy"))
656 {
657 status = TRUE;
658 *type = MN_GNOME_COPIED_FILES_COPY;
659 }
660
661 if (status)
662 {
663 *uri_list = NULL;
664 for (i = 1; strv[i]; i++)
665 *uri_list = g_slist_append(*uri_list, g_strdup(strv[i]));
666 }
667 }
668
669 g_strfreev(strv);
670 return status;
671 }
672
673 void
674 mn_show_help (GtkWindow *parent, const char *link_id)
675 {
676 GError *err = NULL;
677
678 if (! gnome_help_display("mail-notification.xml", link_id, &err))
679 {
680 mn_show_error_dialog(parent, _("Unable to display help"), "%s", err->message);
681 g_error_free(err);
682 }
683 }
684
685 void
686 mn_open_link (GtkWindow *parent, const char *url)
687 {
688 GError *err = NULL;
689
690 if (! gnome_url_show(url, &err))
691 {
692 mn_show_error_dialog(parent, _("Unable to open link"), "%s", err->message);
693 g_error_free(err);
694 }
695 }
696
697 void
698 mn_thread_create (GThreadFunc func, gpointer data)
699 {
700 GError *err = NULL;
701
702 g_return_if_fail(func != NULL);
703
704 if (! g_thread_create(func, data, FALSE, &err))
705 {
706 mn_show_fatal_error_dialog(NULL, "Unable to create a thread: %s.", err->message);
707 g_error_free(err);
708 }
709 }
710
711 static GtkWidget *
712 menu_item_new (const char *stock_id, const char *mnemonic)
713 {
714 GtkWidget *item;
715
716 if (stock_id && mnemonic)
717 {
718 GtkWidget *image;
719
720 item = gtk_image_menu_item_new_with_mnemonic(mnemonic);
721
722 image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_MENU);
723 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
724 gtk_widget_show(image);
725 }
726 else if (stock_id)
727 item = gtk_image_menu_item_new_from_stock(stock_id, NULL);
728 else if (mnemonic)
729 item = gtk_menu_item_new_with_mnemonic(mnemonic);
730 else
731 item = gtk_separator_menu_item_new();
732
733 return item;
734 }
735
736 /**
737 * mn_menu_shell_append:
738 * @shell: the #GtkMenuShell to append to
739 * @stock_id: the stock ID of the item or %NULL
740 * @mnemonic: the mnemonic of the item or %NULL
741 *
742 * Creates a new menu item, shows it and appends it to @shell.
743 *
744 * If both @stock_id and @mnemonic are provided, a #GtkImageMenuItem
745 * will be created using the text of @mnemonic and the icon of
746 * @stock_id.
747 *
748 * If only @stock_id is provided, a #GtkImageMenuitem will be created
749 * using the text and icon of @stock_id.
750 *
751 * If only @mnemonic is provided, a #GtkMenuItem will be created using
752 * the text of @mnemonic.
753 *
754 * If @stock_id and @mnemonic are both %NULL, a #GtkSeparatorMenuItem
755 * will be created.
756 *
757 * Return value: the new menu item.
758 **/
759 GtkWidget *
760 mn_menu_shell_append (GtkMenuShell *shell,
761 const char *stock_id,
762 const char *mnemonic)
763 {
764 GtkWidget *item;
765
766 g_return_val_if_fail(GTK_IS_MENU_SHELL(shell), NULL);
767
768 item = menu_item_new(stock_id, mnemonic);
769 gtk_menu_shell_append(shell, item);
770 gtk_widget_show(item);
771
772 return item;
773 }
774
775 static void
776 show_error_dialog_real (GtkWindow *parent,
777 MNDialogFlags flags,
778 const char *primary,
779 const char *format,
780 va_list args)
781 {
782 char *secondary;
783 GtkWidget *dialog;
784
785 g_return_if_fail(primary != NULL);
786 g_return_if_fail(format != NULL);
787
788 secondary = g_strdup_vprintf(format, args);
789 dialog = mn_alert_dialog_new(parent, GTK_MESSAGE_ERROR, flags, primary, secondary);
790 g_free(secondary);
791
792 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_OK, GTK_RESPONSE_OK);
793
794 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
795
796 if ((flags & MN_DIALOG_BLOCKING) != 0)
797 {
798 gtk_dialog_run(GTK_DIALOG(dialog));
799 gtk_widget_destroy(dialog);
800 }
801 else
802 {
803 g_signal_connect_swapped(dialog,
804 "response",
805 G_CALLBACK(gtk_widget_destroy),
806 dialog);
807 gtk_widget_show(dialog);
808 }
809 }
810
811 void
812 mn_show_error_dialog (GtkWindow *parent,
813 const char *primary,
814 const char *format,
815 ...)
816 {
817 va_list args;
818
819 g_return_if_fail(primary != NULL);
820 g_return_if_fail(format != NULL);
821
822 va_start(args, format);
823 show_error_dialog_real(parent, 0, primary, format, args);
824 va_end(args);
825 }
826
827 /* only the secondary text can have markup */
828 void
829 mn_show_error_dialog_with_markup (GtkWindow *parent,
830 const char *primary,
831 const char *format,
832 ...)
833 {
834 va_list args;
835
836 g_return_if_fail(primary != NULL);
837 g_return_if_fail(format != NULL);
838
839 va_start(args, format);
840 show_error_dialog_real(parent, MN_DIALOG_MARKUP, primary, format, args);
841 va_end(args);
842 }
843
844 void
845 mn_show_invalid_uri_dialog (GtkWindow *parent,
846 const char *primary,
847 const char *invalid_uri)
848 {
849 GSList *list = NULL;
850
851 g_return_if_fail(primary != NULL);
852 g_return_if_fail(invalid_uri != NULL);
853
854 list = g_slist_append(list, (gpointer) invalid_uri);
855 mn_show_invalid_uri_list_dialog(parent, primary, list);
856 g_slist_free(list);
857 }
858
859 void
860 mn_show_invalid_uri_list_dialog (GtkWindow *parent,
861 const char *primary,
862 GSList *invalid_uri_list)
863 {
864 GString *string;
865 GSList *l;
866
867 g_return_if_fail(primary != NULL);
868 g_return_if_fail(invalid_uri_list != NULL);
869
870 string = g_string_new(NULL);
871
872 MN_LIST_FOREACH(l, invalid_uri_list)
873 {
874 const char *uri = l->data;
875
876 g_string_append(string, uri);
877 if (l->next)
878 g_string_append_c(string, '\n');
879 }
880
881 mn_show_error_dialog(parent,
882 primary,
883 ngettext("The following location is invalid:\n\n%s",
884 "The following locations are invalid:\n\n%s",
885 g_slist_length((GSList *) invalid_uri_list)),
886 string->str);
887
888 g_string_free(string, TRUE);
889 }
890
891 void
892 mn_show_fatal_error_dialog (GtkWindow *parent, const char *format, ...)
893 {
894 va_list args;
895
896 g_assert(format != NULL);
897
898 va_start(args, format);
899 show_error_dialog_real(parent, MN_DIALOG_BLOCKING, _("A fatal error has occurred in Mail Notification"), format, args);
900 va_end(args);
901
902 exit(1);
903 }
904
905 GtkWidget *
906 mn_alert_dialog_new (GtkWindow *parent,
907 GtkMessageType type,
908 MNDialogFlags flags,
909 const char *primary,
910 const char *secondary)
911 {
912 GtkWidget *dialog;
913
914 g_return_val_if_fail(primary != NULL, NULL);
915 g_return_val_if_fail(secondary != NULL, NULL);
916
917 dialog = gtk_message_dialog_new(parent,
918 GTK_DIALOG_DESTROY_WITH_PARENT,
919 type,
920 GTK_BUTTONS_NONE,
921 "%s",
922 primary);
923
924 if ((flags & MN_DIALOG_MARKUP) != 0)
925 gtk_message_dialog_format_secondary_markup(GTK_MESSAGE_DIALOG(dialog), "%s", secondary);
926 else
927 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", secondary);
928
929 gtk_window_set_title(GTK_WINDOW(dialog), ""); /* HIG */
930
931 return dialog;
932 }
933
934 time_t
935 mn_time (void)
936 {
937 time_t t;
938
939 t = time(NULL);
940 if (t < 0)
941 {
942 t = 0;
943 g_warning("unable to get current time: %s", g_strerror(errno));
944 }
945
946 return t;
947 }
948
949 char *
950 mn_strftime (const char *format, const struct tm *timeptr)
951 {
952 char *buf;
953 size_t bufsize = 64;
954
955 g_return_val_if_fail(format != NULL, NULL);
956 g_return_val_if_fail(timeptr != NULL, NULL);
957
958 buf = g_malloc(bufsize);
959 while (strftime(buf, bufsize, format, timeptr) == 0)
960 {
961 bufsize *= 2;
962 buf = g_realloc(buf, bufsize);
963 }
964
965 return buf;
966 }
967
968 char *
969 mn_format_past_time (time_t past_time, time_t now)
970 {
971 time_t diff;
972
973 g_return_val_if_fail(past_time > 0, NULL);
974
975 diff = now - past_time;
976 if (diff >= 0)
977 {
978 if (diff < 60)
979 return g_strdup_printf(ngettext("%i second ago", "%i seconds ago", (int) diff), (int) diff);
980 else if (diff < 60 * 60)
981 {
982 int minutes = diff / 60;
983 return g_strdup_printf(ngettext("about %i minute ago", "about %i minutes ago", minutes), minutes);
984 }
985 else if (diff < 60 * 60 * 24)
986 {
987 int hours = diff / (60 * 60);
988 return g_strdup_printf(ngettext("about %i hour ago", "about %i hours ago", hours), hours);
989 }
990 else if (diff < 60 * 60 * 24 * 7)
991 {
992 int days = diff / (60 * 60 * 24);
993 return g_strdup_printf(ngettext("about %i day ago", "about %i days ago", days), days);
994 }
995 else
996 {
997 int weeks = diff / (60 * 60 * 24 * 7);
998 return g_strdup_printf(ngettext("about %i week ago", "about %i weeks ago", weeks), weeks);
999 }
1000 }
1001 else /* future time: simply format it */
1002 {
1003 struct tm *tm;
1004
1005 tm = localtime(&past_time);
1006 g_assert(tm != NULL);
1007
1008 return mn_strftime("%c", tm);
1009 }
1010 }
1011
1012 char *
1013 mn_format_seconds (int seconds)
1014 {
1015 if (seconds < MN_MINS(1))
1016 return g_strdup_printf(ngettext("%i second", "%i seconds", seconds), seconds);
1017 else if (seconds < MN_HOURS(1))
1018 {
1019 int mins = seconds / MN_MINS(1);
1020 return g_strdup_printf(ngettext("%i minute", "%i minutes", mins), mins);
1021 }
1022 else if (seconds < MN_DAYS(1))
1023 {
1024 int hours = seconds / MN_HOURS(1);
1025 return g_strdup_printf(ngettext("%i hour", "%i hours", hours), hours);
1026 }
1027 else
1028 {
1029 g_return_val_if_fail(seconds == MN_DAYS(1), NULL);
1030 return g_strdup(_("1 day"));
1031 }
1032 }
1033
1034 void
1035 mn_g_object_null_unref (gpointer object)
1036 {
1037 if (object)
1038 g_object_unref(object);
1039 }
1040
1041 static void
1042 object_connect_weak_notify_cb (gpointer data, GObject *former_object)
1043 {
1044 SignalHandler *handler = data;
1045
1046 if (handler->instance)
1047 {
1048 g_signal_handler_disconnect(handler->instance, handler->id);
1049 mn_remove_weak_pointer(&handler->instance);
1050 }
1051 g_free(handler);
1052 }
1053
1054 /**
1055 * mn_g_object_connect:
1056 * @object: the object to associate the handlers with
1057 * @instance: the instance to connect to
1058 * @signal_spec: the spec for the first signal
1059 * @...: #GCallback for the first signal, followed by data for the
1060 * first signal, followed optionally by more signal spec/callback/data
1061 * triples, followed by NULL
1062 *
1063 * Connects to one or more signals of @instance, associating the
1064 * handlers with @object. The handlers will be disconnected whenever
1065 * @object is finalized.
1066 *
1067 * Note: this function is not thread-safe. If @object and @instance
1068 * are finalized concurrently, the behaviour is undefined.
1069 *
1070 * The signals specs must be in the same format than those passed to
1071 * g_object_connect(), except that object-signal,
1072 * swapped-object-signal, object-signal-after and
1073 * swapped-object-signal-after are not accepted.
1074 *
1075 * Note that this function is only useful because of
1076 * http://bugzilla.gnome.org/show_bug.cgi?id=118536, otherwise
1077 * g_signal_connect_object() and the object specs of
1078 * g_object_connect() could be used.
1079 *
1080 * Return value: @object
1081 **/
1082 gpointer
1083 mn_g_object_connect (gpointer object,
1084 gpointer instance,
1085 const char *signal_spec,
1086 ...)
1087 {
1088 va_list args;
1089
1090 g_return_val_if_fail(G_IS_OBJECT(object), NULL);
1091 g_return_val_if_fail(G_IS_OBJECT(instance), NULL);
1092
1093 va_start(args, signal_spec);
1094
1095 while (signal_spec)
1096 {
1097 GCallback callback = va_arg(args, GCallback);
1098 gpointer data = va_arg(args, gpointer);
1099 SignalHandler *handler;
1100
1101 handler = g_new(SignalHandler, 1);
1102 handler->instance = instance;
1103
1104 if (g_str_has_prefix(signal_spec, "signal::"))
1105 handler->id = g_signal_connect(instance, signal_spec + 8, callback, data);
1106 else if (g_str_has_prefix(signal_spec, "swapped_signal::")
1107 || g_str_has_prefix(signal_spec, "swapped-signal::"))
1108 handler->id = g_signal_connect_swapped(instance, signal_spec + 16, callback, data);
1109 else if (g_str_has_prefix(signal_spec, "signal_after::")
1110 || g_str_has_prefix(signal_spec, "signal-after::"))
1111 handler->id = g_signal_connect_after(instance, signal_spec + 14, callback, data);
1112 else if (g_str_has_prefix(signal_spec, "swapped_signal_after::")
1113 || g_str_has_prefix(signal_spec, "swapped-signal-after::"))
1114 handler->id = g_signal_connect_data(instance, signal_spec + 22, callback, data, NULL, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
1115 else
1116 g_critical("invalid signal specification \"%s\"", signal_spec);
1117
1118 mn_add_weak_pointer(&handler->instance);
1119 g_object_weak_ref(object, object_connect_weak_notify_cb, handler);
1120
1121 signal_spec = va_arg(args, const char *);
1122 }
1123
1124 va_end(args);
1125
1126 return object;
1127 }
1128
1129 static void
1130 object_clone_parameter_free (GParameter *parameter)
1131 {
1132 g_return_if_fail(parameter != NULL);
1133
1134 g_value_unset(¶meter->value);
1135 g_free(parameter);
1136 }
1137
1138 static void
1139 object_clone_parameters_foreach_cb (gpointer key,
1140 GParameter *parameter,
1141 gpointer user_data)
1142 {
1143 GArray *parameters = user_data;
1144
1145 g_array_append_val(parameters, *parameter);
1146 }
1147
1148 gpointer
1149 mn_g_object_clone (gpointer object, const char *property_name, ...)
1150 {
1151 GHashTable *parameters;
1152 va_list args;
1153 GParamSpec **properties;
1154 unsigned int n_properties;
1155 int i;
1156 GArray *parameters_array;
1157 GObject *new_object;
1158
1159 g_return_val_if_fail(G_IS_OBJECT(object), NULL);
1160
1161 parameters = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify) object_clone_parameter_free);
1162
1163 /* add provided properties */
1164
1165 va_start(args, property_name);
1166
1167 while (property_name)
1168 {
1169 GParamSpec *pspec;
1170 GParameter *parameter;
1171 char *error = NULL;
1172
1173 pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(object), property_name);
1174 g_assert(pspec != NULL);
1175
1176 parameter = g_new0(GParameter, 1);
1177 parameter->name = g_param_spec_get_name(pspec);
1178
1179 g_value_init(¶meter->value, G_PARAM_SPEC_VALUE_TYPE(pspec));
1180 G_VALUE_COLLECT(¶meter->value, args, 0, &error);
1181 g_assert(error == NULL);
1182
1183 g_hash_table_replace(parameters, (gpointer) parameter->name, parameter);
1184
1185 property_name = va_arg(args, const char *);
1186 }
1187
1188 va_end(args);
1189
1190 /* clone existing properties */
1191
1192 properties = g_object_class_list_properties(G_OBJECT_GET_CLASS(object), &n_properties);
1193 for (i = 0; i < n_properties; i++)
1194 if ((properties[i]->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE)
1195 {
1196 const char *name;
1197
1198 name = g_param_spec_get_name(properties[i]);
1199 if (! g_hash_table_lookup(parameters, name))
1200 {
1201 GParameter *parameter;
1202
1203 parameter = g_new0(GParameter, 1);
1204 parameter->name = name;
1205
1206 g_value_init(¶meter->value, G_PARAM_SPEC_VALUE_TYPE(properties[i]));
1207 g_object_get_property(object, parameter->name, ¶meter->value);
1208
1209 g_hash_table_insert(parameters, (gpointer) parameter->name, parameter);
1210 }
1211 }
1212 g_free(properties);
1213
1214 parameters_array = g_array_new(FALSE, FALSE, sizeof(GParameter));
1215
1216 g_hash_table_foreach(parameters, (GHFunc) object_clone_parameters_foreach_cb, parameters_array);
1217
1218 new_object = g_object_newv(G_OBJECT_TYPE(object), parameters_array->len, (GParameter *) parameters_array->data);
1219
1220 g_hash_table_destroy(parameters);
1221 g_array_free(parameters_array, TRUE);
1222
1223 return new_object;
1224 }
1225
1226 int
1227 mn_utf8_strcasecmp (const char *s1, const char *s2)
1228 {
1229 char *normalized_s1;
1230 char *normalized_s2;
1231 char *folded_s1;
1232 char *folded_s2;
1233 int cmp;
1234
1235 g_return_val_if_fail(s1 != NULL, 0);
1236 g_return_val_if_fail(s2 != NULL, 0);
1237
1238 normalized_s1 = g_utf8_normalize(s1, -1, G_NORMALIZE_ALL);
1239 normalized_s2 = g_utf8_normalize(s2, -1, G_NORMALIZE_ALL);
1240 folded_s1 = g_utf8_casefold(normalized_s1, -1);
1241 folded_s2 = g_utf8_casefold(normalized_s2, -1);
1242
1243 cmp = strcmp(folded_s1, folded_s2);
1244
1245 g_free(normalized_s1);
1246 g_free(normalized_s2);
1247 g_free(folded_s1);
1248 g_free(folded_s2);
1249
1250 return cmp;
1251 }
1252
1253 gboolean
1254 mn_utf8_str_case_has_suffix (const char *str, const char *suffix)
1255 {
1256 char *normalized_str;
1257 char *normalized_suffix;
1258 char *folded_str;
1259 char *folded_suffix;
1260 int str_len;
1261 int suffix_len;
1262 gboolean has;
1263
1264 g_return_val_if_fail(str != NULL, FALSE);
1265 g_return_val_if_fail(suffix != NULL, FALSE);
1266
1267 normalized_str = g_utf8_normalize(str, -1, G_NORMALIZE_ALL);
1268 normalized_suffix = g_utf8_normalize(suffix, -1, G_NORMALIZE_ALL);
1269 folded_str = g_utf8_casefold(normalized_str, -1);
1270 folded_suffix = g_utf8_casefold(normalized_suffix, -1);
1271
1272 str_len = strlen(folded_str);
1273 suffix_len = strlen(folded_suffix);
1274
1275 has = str_len >= suffix_len
1276 && ! strcmp(folded_str + str_len - suffix_len, folded_suffix);
1277
1278 g_free(normalized_str);
1279 g_free(normalized_suffix);
1280 g_free(folded_str);
1281 g_free(folded_suffix);
1282
1283 return has;
1284 }
1285
1286 char *
1287 mn_utf8_escape (const char *str)
1288 {
1289 GString *escaped;
1290
1291 g_return_val_if_fail(str != NULL, NULL);
1292
1293 escaped = g_string_new(NULL);
1294
1295 while (*str)
1296 {
1297 gunichar c;
1298
1299 c = g_utf8_get_char_validated(str, -1);
1300 if (c != (gunichar) -2 && c != (gunichar) -1)
1301 {
1302 g_string_append_unichar(escaped, c);
1303 str = g_utf8_next_char(str);
1304 }
1305 else
1306 {
1307 g_string_append_printf(escaped, "\\x%02x", (unsigned int) (unsigned char) *str);
1308 str++;
1309 }
1310 }
1311
1312 return g_string_free(escaped, FALSE);
1313 }
1314
1315 static void
1316 dialog_run_nonmodal_shutdown_loop (RunNonmodalInfo *info)
1317 {
1318 g_return_if_fail(info != NULL);
1319
1320 if (g_main_loop_is_running(info->loop))
1321 g_main_loop_quit(info->loop);
1322 }
1323
1324 static void
1325 dialog_run_nonmodal_destroy_h (GtkObject *object, gpointer user_data)
1326 {
1327 RunNonmodalInfo *info = user_data;
1328
1329 info->destroyed = TRUE;
1330
1331 /*
1332 * mn_dialog_run_nonmodal_shutdown_loop() will be called by
1333 * mn_dialog_run_nonmodal_unmap_h()
1334 */
1335 }
1336
1337 static void
1338 dialog_run_nonmodal_unmap_h (GtkWidget *widget, gpointer user_data)
1339 {
1340 RunNonmodalInfo *info = user_data;
1341
1342 dialog_run_nonmodal_shutdown_loop(info);
1343 }
1344
1345 static void
1346 dialog_run_nonmodal_response_h (GtkDialog *dialog,
1347 int response,
1348 gpointer user_data)
1349 {
1350 RunNonmodalInfo *info = user_data;
1351
1352 info->response = response;
1353
1354 dialog_run_nonmodal_shutdown_loop(info);
1355 }
1356
1357 static gboolean
1358 dialog_run_nonmodal_delete_event_h (GtkWidget *widget,
1359 GdkEvent *event,
1360 gpointer user_data)
1361 {
1362 RunNonmodalInfo *info = user_data;
1363
1364 dialog_run_nonmodal_shutdown_loop(info);
1365
1366 return TRUE; /* do not destroy */
1367 }
1368
1369 int
1370 mn_dialog_run_nonmodal (GtkDialog *dialog)
1371 {
1372 RunNonmodalInfo info = { NULL, GTK_RESPONSE_NONE, FALSE };
1373
1374 g_return_val_if_fail(GTK_IS_DIALOG(dialog), -1);
1375
1376 g_object_ref(dialog);
1377
1378 if (! GTK_WIDGET_VISIBLE(dialog))
1379 gtk_widget_show(GTK_WIDGET(dialog));
1380
1381 g_object_connect(dialog,
1382 "signal::destroy", dialog_run_nonmodal_destroy_h, &info,
1383 "signal::unmap", dialog_run_nonmodal_unmap_h, &info,
1384 "signal::response", dialog_run_nonmodal_response_h, &info,
1385 "signal::delete-event", dialog_run_nonmodal_delete_event_h, &info,
1386 NULL);
1387
1388 info.loop = g_main_loop_new(NULL, FALSE);
1389
1390 GDK_THREADS_LEAVE();
1391 g_main_loop_run(info.loop);
1392 GDK_THREADS_ENTER();
1393
1394 g_main_loop_unref(info.loop);
1395
1396 if (! info.destroyed)
1397 g_object_disconnect(dialog,
1398 "any-signal", dialog_run_nonmodal_destroy_h, &info,
1399 "any-signal", dialog_run_nonmodal_unmap_h, &info,
1400 "any-signal", dialog_run_nonmodal_response_h, &info,
1401 "any-signal", dialog_run_nonmodal_delete_event_h, &info,
1402 NULL);
1403
1404 g_object_unref(dialog);
1405
1406 return info.response;
1407 }
1408
1409 void
1410 mn_source_clear (unsigned int *tag)
1411 {
1412 g_return_if_fail(tag != NULL);
1413
1414 if (*tag)
1415 {
1416 g_source_remove(*tag);
1417 *tag = 0;
1418 }
1419 }
1420
1421 gboolean
1422 mn_ascii_str_case_has_prefix (const char *str, const char *prefix)
1423 {
1424 int str_len;
1425 int prefix_len;
1426
1427 g_return_val_if_fail(str != NULL, FALSE);
1428 g_return_val_if_fail(prefix != NULL, FALSE);
1429
1430 str_len = strlen(str);
1431 prefix_len = strlen(prefix);
1432
1433 if (str_len < prefix_len)
1434 return FALSE;
1435
1436 return g_ascii_strncasecmp(str, prefix, prefix_len) == 0;
1437 }
1438
1439 char *
1440 mn_ascii_strcasestr (const char *big, const char *little)
1441 {
1442 char *lower_big;
1443 char *lower_little;
1444 char *s;
1445
1446 g_return_val_if_fail(big != NULL, NULL);
1447 g_return_val_if_fail(little != NULL, NULL);
1448
1449 lower_big = g_ascii_strdown(big, -1);
1450 lower_little = g_ascii_strdown(little, -1);
1451
1452 s = strstr(lower_big, lower_little);
1453
1454 g_free(lower_big);
1455 g_free(lower_little);
1456
1457 return s ? (char *) big + (s - lower_big) : NULL;
1458 }
1459
1460 char *
1461 mn_ascii_strcasestr_span (const char *big, const char *little)
1462 {
1463 char *s;
1464
1465 g_return_val_if_fail(big != NULL, NULL);
1466 g_return_val_if_fail(little != NULL, NULL);
1467
1468 s = mn_ascii_strcasestr(big, little);
1469 if (s)
1470 s += strlen(little);
1471
1472 return s;
1473 }
1474
1475 char *
1476 mn_subst_command (const char *command,
1477 MNSubstCommandFunction subst,
1478 gpointer data,
1479 GError **err)
1480 {
1481 GString *result;
1482 const char *p;
1483
1484 g_return_val_if_fail(command != NULL, NULL);
1485 g_return_val_if_fail(subst != NULL, NULL);
1486
1487 result = g_string_new(NULL);
1488
1489 for (p = command; *p;)
1490 if (*p == '%')
1491 {
1492 char *name = NULL;
1493
1494 if (p[1] == '%')
1495 {
1496 g_string_append_c(result, '%');
1497 p += 2;
1498 }
1499 else if (p[1] == '{')
1500 {
1501 char *end;
1502
1503 end = strchr(p + 2, '}');
1504 if (! end)
1505 {
1506 g_set_error(err, 0, 0, _("unterminated substitution"));
1507 goto error;
1508 }
1509
1510 name = g_strndup(p + 2, end - p - 2);
1511 p = end + 1;
1512 }
1513 else
1514 {
1515 const char *end = p + 1;
1516
1517 while (g_ascii_isalnum(*end) || *end == '-' || *end == '_')
1518 end++;
1519
1520 name = g_strndup(p + 1, end - p - 1);
1521 p = end;
1522 }
1523
1524 if (name)
1525 {
1526 gboolean ok = FALSE;
1527
1528 if (*name)
1529 {
1530 char *value;
1531
1532 if (subst(name, &value, data))
1533 {
1534 char *quoted;
1535
1536 quoted = mn_shell_quote_safe(value ? value : "");
1537 g_free(value);
1538
1539 g_string_append(result, quoted);
1540 g_free(quoted);
1541
1542 ok = TRUE;
1543 }
1544 else
1545 g_set_error(err, 0, 0, _("unknown substitution \"%s\""), name);
1546 }
1547 else
1548 g_set_error(err, 0, 0, _("empty substitution"));
1549
1550 g_free(name);
1551 if (! ok)
1552 goto error;
1553 }
1554 }
1555 else
1556 {
1557 g_string_append_c(result, *p);
1558 p++;
1559 }
1560
1561 goto end; /* success */
1562
1563 error:
1564 g_string_free(result, TRUE);
1565 result = NULL;
1566
1567 end:
1568 return result ? g_string_free(result, FALSE) : NULL;
1569 }
1570
1571 static void
1572 handle_execute_result (int status, const char *command)
1573 {
1574 if (status < 0)
1575 mn_show_error_dialog(NULL,
1576 _("A command error has occurred in Mail Notification"),
1577 _("Unable to execute \"%s\": %s."),
1578 command,
1579 g_strerror(errno));
1580 }
1581
1582 void
1583 mn_execute_command (const char *command)
1584 {
1585 g_return_if_fail(command != NULL);
1586
1587 handle_execute_result(gnome_execute_shell(NULL, command), command);
1588 }
1589
1590 void
1591 mn_execute_command_in_terminal (const char *command)
1592 {
1593 g_return_if_fail(command != NULL);
1594
1595 handle_execute_result(gnome_execute_terminal_shell(NULL, command), command);
1596 }
1597
1598 /**
1599 * mn_shell_quote_safe:
1600 * @unquoted_string: a literal string
1601 *
1602 * Like g_shell_quote(), but guarantees that the string will be quoted
1603 * using single quotes, therefore making sure that backticks will not
1604 * be processed.
1605 *
1606 * Return value: the quoted string
1607 **/
1608 char *
1609 mn_shell_quote_safe (const char *unquoted_string)
1610 {
1611 GString *result;
1612 int i;
1613
1614 g_return_val_if_fail(unquoted_string != NULL, NULL);
1615
1616 result = g_string_new("'");
1617
1618 for (i = 0; unquoted_string[i]; i++)
1619 if (unquoted_string[i] == '\'')
1620 g_string_append(result, "'\\''");
1621 else
1622 g_string_append_c(result, unquoted_string[i]);
1623
1624 g_string_append_c(result, '\'');
1625
1626 return g_string_free(result, FALSE);
1627 }
1628
1629 GtkWidget *
1630 mn_hig_section_new (const char *title,
1631 GtkWidget **label,
1632 GtkWidget **alignment)
1633 {
1634 GtkWidget *section;
1635 char *markup;
1636 GtkWidget *_label;
1637 GtkWidget *_alignment;
1638
1639 g_return_val_if_fail(title != NULL, NULL);
1640
1641 section = gtk_vbox_new(FALSE, 6);
1642
1643 markup = g_markup_printf_escaped("<span weight=\"bold\">%s</span>", title);
1644 _label = gtk_label_new(markup);
1645 g_free(markup);
1646
1647 gtk_misc_set_alignment(GTK_MISC(_label), 0.0, 0.5);
1648 gtk_label_set_use_markup(GTK_LABEL(_label), TRUE);
1649
1650 gtk_box_pack_start(GTK_BOX(section), _label, FALSE, FALSE, 0);
1651
1652 _alignment = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
1653 gtk_alignment_set_padding(GTK_ALIGNMENT(_alignment), 0, 0, 12, 0);
1654
1655 gtk_box_pack_start(GTK_BOX(section), _alignment, TRUE, TRUE, 0);
1656
1657 gtk_widget_show(_label);
1658 gtk_widget_show(_alignment);
1659
1660 if (label)
1661 *label = _label;
1662 if (alignment)
1663 *alignment = _alignment;
1664
1665 return section;
1666 }
1667
1668 GtkWidget *
1669 mn_hig_section_new_with_box (const char *title,
1670 GtkWidget **label,
1671 GtkWidget **vbox)
1672 {
1673 GtkWidget *section;
1674 GtkWidget *alignment;
1675 GtkWidget *_vbox;
1676
1677 g_return_val_if_fail(title != NULL, NULL);
1678
1679 section = mn_hig_section_new(title, label, &alignment);
1680
1681 _vbox = gtk_vbox_new(FALSE, 6);
1682 gtk_container_add(GTK_CONTAINER(alignment), _vbox);
1683 gtk_widget_show(_vbox);
1684
1685 if (vbox)
1686 *vbox = _vbox;
1687
1688 return section;
1689 }
1690
1691 char *
1692 mn_g_value_to_string (const GValue *value)
1693 {
1694 char *str;
1695
1696 g_return_val_if_fail(G_IS_VALUE(value), NULL);
1697
1698 /*
1699 * We only handle types which we actually export (grep for
1700 * MN_MESSAGE_PARAM_EXPORT and MN_MAILBOX_PARAM.*SAVE).
1701 */
1702
1703 if (G_VALUE_HOLDS_INT(value))
1704 str = g_strdup_printf("%i", g_value_get_int(value));
1705 else if (G_VALUE_HOLDS_ULONG(value))
1706 str = g_strdup_printf("%lu", g_value_get_ulong(value));
1707 else if (G_VALUE_HOLDS_STRING(value))
1708 str = g_value_dup_string(value);
1709 else if (G_VALUE_HOLDS_ENUM(value))
1710 {
1711 GEnumClass *enum_class;
1712 GEnumValue *enum_value;
1713
1714 enum_class = g_type_class_ref(G_VALUE_TYPE(value));
1715 enum_value = g_enum_get_value(enum_class, g_value_get_enum(value));
1716 g_assert(enum_value != NULL);
1717
1718 str = g_strdup(enum_value->value_nick);
1719 g_type_class_unref(enum_class);
1720 }
1721 else
1722 g_return_val_if_reached(NULL);
1723
1724 return str;
1725 }
1726
1727 gboolean
1728 mn_g_value_from_string (GValue *value, const char *str)
1729 {
1730 g_return_val_if_fail(G_IS_VALUE(value), FALSE);
1731 g_return_val_if_fail(str != NULL, FALSE);
1732
1733 /*
1734 * We only handle types which we actually import (grep for
1735 * MN_MAILBOX_PARAM_LOAD).
1736 */
1737
1738 if (G_VALUE_HOLDS_INT(value))
1739 {
1740 int n;
1741 char *endptr;
1742
1743 n = strtol(str, &endptr, 10);
1744 if (*endptr == '\0') /* successful conversion */
1745 g_value_set_int(value, n);
1746 else
1747 return FALSE;
1748 }
1749 else if (G_VALUE_HOLDS_STRING(value))
1750 g_value_set_string(value, str);
1751 else if (G_VALUE_HOLDS_ENUM(value))
1752 {
1753 GEnumClass *enum_class;
1754 GEnumValue *enum_value;
1755 gboolean found;
1756
1757 enum_class = g_type_class_ref(G_VALUE_TYPE(value));
1758 enum_value = g_enum_get_value_by_nick(enum_class, str);
1759
1760 if (enum_value)
1761 {
1762 g_value_set_enum(value, enum_value->value);
1763 found = TRUE;
1764 }
1765 else
1766 found = FALSE;
1767
1768 g_type_class_unref(enum_class);
1769
1770 if (! found)
1771 return FALSE;
1772 }
1773 else
1774 g_return_val_if_reached(FALSE);
1775
1776 return TRUE;
1777 }
1778
1779 void
1780 mn_window_present_from_event (GtkWindow *window)
1781 {
1782 g_return_if_fail(GTK_IS_WINDOW(window));
1783
1784 gtk_window_present_with_time(window, gtk_get_current_event_time());
1785 }
1786
1787 void
1788 mn_add_weak_pointer (gpointer object_location)
1789 {
1790 gpointer *p;
1791
1792 g_return_if_fail(object_location != NULL);
1793
1794 p = (gpointer *) object_location;
1795 g_return_if_fail(G_IS_OBJECT(*p));
1796
1797 g_object_add_weak_pointer(G_OBJECT(*p), p);
1798 }
1799
1800 void
1801 mn_remove_weak_pointer (gpointer object_location)
1802 {
1803 gpointer *p;
1804
1805 g_return_if_fail(object_location != NULL);
1806
1807 p = (gpointer *) object_location;
1808 g_return_if_fail(G_IS_OBJECT(*p));
1809
1810 g_object_remove_weak_pointer(G_OBJECT(*p), p);
1811 *p = NULL;
1812 }
1813
1814 int
1815 mn_strv_find (char **strv, const char *elem)
1816 {
1817 int i;
1818
1819 g_return_val_if_fail(strv != NULL, -1);
1820 g_return_val_if_fail(elem != NULL, -1);
1821
1822 for (i = 0; strv[i]; i++)
1823 if (! strcmp(strv[i], elem))
1824 return i;
1825
1826 return -1;
1827 }
1828
1829 GSList *
1830 mn_g_ptr_array_to_slist (GPtrArray *array)
1831 {
1832 GSList *list = NULL;
1833 int i;
1834
1835 g_return_val_if_fail(array != NULL, NULL);
1836
1837 for (i = array->len - 1; i >= 0; i--)
1838 list = g_slist_prepend(list, g_ptr_array_index(array, i));
1839
1840 return list;
1841 }
1842
1843 void
1844 mn_g_ptr_array_free_deep_custom (GPtrArray *array,
1845 GFunc element_free_func,
1846 gpointer user_data)
1847 {
1848 g_return_if_fail(array != NULL);
1849 g_return_if_fail(element_free_func != NULL);
1850
1851 g_ptr_array_foreach(array, element_free_func, user_data);
1852 g_ptr_array_free(array, TRUE);
1853 }
1854
1855 void
1856 mn_g_object_ptr_array_free (GPtrArray *array)
1857 {
1858 g_return_if_fail(array != NULL);
1859
1860 mn_g_ptr_array_free_deep_custom(array, (GFunc) g_object_unref, NULL);
1861 }
1862
1863 const char *
1864 mn_enum_get_value_nick (GType type, int value)
1865 {
1866 GEnumClass *enum_class;
1867 GEnumValue *enum_value;
1868
1869 g_return_val_if_fail(G_TYPE_IS_ENUM(type), NULL);
1870
1871 enum_class = g_type_class_ref(type);
1872 g_return_val_if_fail(G_IS_ENUM_CLASS(enum_class), NULL);
1873
1874 enum_value = g_enum_get_value(enum_class, value);
1875 g_return_val_if_fail(enum_value != NULL, NULL);
1876
1877 g_type_class_unref(enum_class);
1878
1879 return enum_value->value_nick;
1880 }