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 }