src/mn-shell.gob (25553B) - 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 %headertop{ 21 #include <gtk/gtk.h> 22 #include <dbus/dbus-glib.h> 23 #include "mn-mailboxes.h" 24 #include "mn-mailbox-properties-dialog.h" 25 #include "mn-mail-icon.h" 26 #include "mn-popups.h" 27 %} 28 29 %privateheader{ 30 #include "mn-sound-player.h" 31 %} 32 33 %{ 34 #include <unistd.h> 35 #include <fcntl.h> 36 #include <signal.h> 37 #include <glib/gi18n.h> 38 #include <libxml/tree.h> 39 #include "mn-properties-dialog.h" 40 #include "mn-util.h" 41 #include "mn-conf.h" 42 #include "mn-mailboxes.h" 43 #include "mn-about-dialog.h" 44 #include "mn-standard-message-view.h" 45 #include "mn-compact-message-view.h" 46 #include "mn-message.h" 47 #include "mn-stock.h" 48 #include "mn-locked-callback.h" 49 50 MNShell *mn_shell = NULL; 51 52 typedef struct 53 { 54 int num; 55 const char *name; 56 } UnixSignalInfo; 57 58 #define SIGNAL_INFO(name) { name, #name } 59 60 static const UnixSignalInfo unix_quit_signals[] = { 61 SIGNAL_INFO(SIGHUP), 62 SIGNAL_INFO(SIGINT), 63 SIGNAL_INFO(SIGTERM), 64 SIGNAL_INFO(SIGUSR1), 65 SIGNAL_INFO(SIGUSR2) 66 }; 67 %} 68 69 enum MN_SHELL_TOOLTIP_MAIL_SUMMARY 70 { 71 STANDARD, 72 COMPACT, 73 NONE 74 } MN:Shell:Tooltip:Mail:Summary; 75 76 class MN:Shell from G:Object 77 { 78 public MNMailboxes *mailboxes; 79 80 public MNMailIcon *icon; 81 public MNPopups *popups; 82 83 public DBusGConnection *session_bus; 84 property POINTER session_bus (flags = CONSTRUCT_ONLY, link, type = DBusGConnection *); 85 86 public DBusGProxy *session_bus_proxy; 87 property POINTER session_bus_proxy (flags = CONSTRUCT_ONLY, link, type = DBusGProxy *); 88 89 private int unix_signal_pipe_write_end; 90 91 private MNSoundPlayer *sound_player; 92 93 private gboolean has_new; 94 95 private GtkWidget *properties_dialog; 96 private GSList *mailbox_properties_dialogs; 97 private GtkWidget *about_dialog; 98 99 constructor (self) 100 { 101 g_assert(mn_shell == NULL); 102 103 mn_shell = self; 104 g_object_weak_ref(G_OBJECT(self), self_weak_notify_cb, NULL); 105 106 selfp->sound_player = mn_sound_player_new(); 107 mn_add_weak_pointer(&selfp->sound_player); 108 109 /* 110 * self->mailboxes is assigned in MNMailboxes itself, so that 111 * mailboxes can access it even during construction of 112 * MNMailboxes. 113 */ 114 mn_mailboxes_new(); 115 mn_add_weak_pointer(&self->mailboxes); 116 117 self_init_icon(self); 118 119 mn_g_object_gconf_notifications_add_gdk_locked(self, 120 MN_CONF_BLINK_ON_ERRORS, self_notify_icon_cb, self, 121 MN_CONF_ALWAYS_DISPLAY_ICON, self_notify_icon_cb, self, 122 MN_CONF_DISPLAY_MESSAGE_COUNT, self_notify_icon_cb, self, 123 MN_CONF_GNOME_MAIL_READER_NAMESPACE, self_notify_mail_reader_cb, self, 124 MN_CONF_TOOLTIP_MAIL_SUMMARY, self_notify_tooltip_cb, self, 125 MN_CONF_TOOLTIP_MAIL_SUMMARY_LIMIT, self_notify_tooltip_cb, self, 126 NULL); 127 128 g_object_connect(self->mailboxes, 129 "signal::messages-changed", self_messages_changed_h, self, 130 "signal::mailbox-removed", self_mailbox_removed_h, self, 131 "swapped-signal::notify::manually-checkable", self_update_sensitivity, self, 132 "swapped-signal::list-changed", self_update_tooltip, self, 133 "swapped-signal::list-changed", self_update_icon, self, 134 "swapped-signal::error-changed", self_update_tooltip, self, 135 "swapped-signal::error-changed", self_update_icon, self, 136 NULL); 137 138 self->popups = mn_popups_new(); 139 mn_add_weak_pointer(&self->popups); 140 141 /* 142 * Exit gracefully (unreferencing our components) when receiving a 143 * UNIX signal. 144 */ 145 if (! self_install_unix_quit_signal_handlers(self)) 146 /* unlikely and unimportant, not worth a translation */ 147 g_warning("unable to install UNIX quit signal handlers"); 148 } 149 150 finalize (self) 151 { 152 /* 153 * We explicitly unreference or destroy each component, even 154 * though MN will exit after the shell is finalized. This is done 155 * for the sake of respecting encapsulation: we should not know 156 * whether a particular component has something to do on exit or 157 * not (for instance, MNSoundPlayer must kill the play process). 158 * 159 * Of course, the order in which we get rid of the components 160 * matters: for instance, when destroyed, MNPropertiesDialog might 161 * remove the test mailbox and thus requires a valid MNMailboxes 162 * object. We destroy the components in the inverse order of their 163 * creation. 164 * 165 * Also note that we do not need to nullify the pointers after 166 * destruction since we use mn_add_weak_pointer() at creation 167 * time. 168 */ 169 170 g_slist_foreach(selfp->mailbox_properties_dialogs, (GFunc) gtk_widget_destroy, NULL); 171 /* the list is freed in mailbox_properties_dialog_weak_notify_cb() */ 172 173 if (selfp->properties_dialog) 174 gtk_widget_destroy(selfp->properties_dialog); 175 176 if (selfp->about_dialog) 177 gtk_widget_destroy(selfp->about_dialog); 178 179 mn_g_object_null_unref(self->popups); 180 181 if (self->icon) 182 { 183 /* do not recreate the icon after we destroy it */ 184 g_signal_handlers_disconnect_by_func(self->icon, self_icon_destroy_h, self); 185 gtk_widget_destroy(GTK_WIDGET(self->icon)); 186 } 187 188 mn_g_object_null_unref(self->mailboxes); 189 190 mn_g_object_null_unref(selfp->sound_player); 191 } 192 193 private gboolean 194 install_unix_quit_signal_handlers (self) 195 { 196 int sigpipe[2]; 197 int flags; 198 GIOChannel *read_channel = NULL; 199 int i; 200 201 if (pipe(sigpipe) < 0) 202 return FALSE; 203 204 /* 205 * Enable non-blocking mode for the write end, so that if another 206 * signal occurs while the main thread is busy processing the 207 * first signal, the signal handler will not block in write(). 208 */ 209 210 flags = fcntl(sigpipe[1], F_GETFL); 211 if (flags < 0) 212 goto error; 213 214 if (fcntl(sigpipe[1], F_SETFL, flags | O_NONBLOCK) < 0) 215 goto error; 216 217 selfp->unix_signal_pipe_write_end = sigpipe[1]; 218 219 read_channel = g_io_channel_unix_new(sigpipe[0]); 220 221 if (g_io_channel_set_encoding(read_channel, NULL, NULL) != G_IO_STATUS_NORMAL) 222 goto error; 223 224 for (i = 0; i < G_N_ELEMENTS(unix_quit_signals); i++) 225 { 226 struct sigaction sa; 227 228 sa.sa_handler = self_unix_quit_signal_handler; 229 sa.sa_flags = 0; 230 sigemptyset(&sa.sa_mask); 231 232 if (sigaction(unix_quit_signals[i].num, &sa, NULL) < 0) 233 goto error; 234 } 235 236 g_io_add_watch(read_channel, G_IO_IN | G_IO_PRI, self_unix_quit_signal_watch_cb, self); 237 238 return TRUE; /* ok */ 239 240 error: 241 if (read_channel) 242 { 243 g_io_channel_shutdown(read_channel, FALSE, NULL); 244 g_io_channel_unref(read_channel); 245 } 246 else 247 close(sigpipe[0]); 248 249 close(sigpipe[1]); 250 251 return FALSE; 252 } 253 254 private void 255 unix_quit_signal_handler (int signum) 256 { 257 write(mn_shell->_priv->unix_signal_pipe_write_end, &signum, sizeof(signum)); 258 } 259 260 private const UnixSignalInfo * 261 lookup_unix_quit_signal (int signum) 262 { 263 int i; 264 265 for (i = 0; i < G_N_ELEMENTS(unix_quit_signals); i++) 266 if (unix_quit_signals[i].num == signum) 267 return &unix_quit_signals[i]; 268 269 g_assert_not_reached(); 270 return NULL; 271 } 272 273 private gboolean 274 unix_quit_signal_watch_cb (GIOChannel *source, 275 GIOCondition condition, 276 gpointer data) 277 { 278 Self *self = data; 279 GIOStatus status; 280 int signum; 281 gsize bytes_read; 282 const UnixSignalInfo *info; 283 284 status = g_io_channel_read_chars(source, (char *) &signum, sizeof(signum), &bytes_read, NULL); 285 g_assert(status == G_IO_STATUS_NORMAL); 286 g_assert(bytes_read == sizeof(signum)); 287 288 info = self_lookup_unix_quit_signal(signum); 289 290 g_message(_("received %s signal, exiting"), info->name); 291 292 GDK_THREADS_ENTER(); 293 self_quit(self); 294 GDK_THREADS_LEAVE(); 295 296 return FALSE; /* remove source */ 297 } 298 299 private void 300 weak_notify_cb (gpointer data, GObject *former_object) 301 { 302 gtk_main_quit(); 303 } 304 305 private void 306 messages_changed_h (MNMailboxes *mailboxes, 307 gboolean has_new, 308 gpointer user_data) 309 { 310 Self *self = user_data; 311 312 if (mn_conf_has_command(MN_CONF_COMMANDS_MAIL_CHANGED_NAMESPACE)) 313 mn_conf_execute_command(MN_CONF_COMMANDS_MAIL_CHANGED_COMMAND); 314 315 if (has_new) 316 { 317 self_play_new_mail_sound(self); 318 319 if (mn_conf_has_command(MN_CONF_COMMANDS_NEW_MAIL_NAMESPACE)) 320 mn_conf_execute_command(MN_CONF_COMMANDS_NEW_MAIL_COMMAND); 321 } 322 323 self_update_sensitivity(self); 324 self_update_tooltip(self); 325 self_update_icon(self); 326 } 327 328 private void 329 play_new_mail_sound (self) 330 { 331 char *file; 332 333 if (! mn_conf_get_bool(MN_CONF_SOUNDS_NEW_MAIL_ENABLED)) 334 return; 335 336 file = mn_conf_get_string(MN_CONF_SOUNDS_NEW_MAIL_FILE); 337 338 if (file && *file) 339 mn_sound_player_play(selfp->sound_player, file, NULL); 340 341 g_free(file); 342 } 343 344 private void 345 mailbox_removed_h (MNMailboxes *mailboxes, 346 MNMailbox *mailbox, 347 gpointer user_data) 348 { 349 Self *self = user_data; 350 MNMailboxPropertiesDialog *dialog; 351 352 /* destroy the associated properties dialog, if any */ 353 dialog = self_get_mailbox_properties_dialog(self, mailbox); 354 if (dialog) 355 gtk_widget_destroy(GTK_WIDGET(dialog)); 356 } 357 358 private void 359 init_icon (self) 360 { 361 self->icon = MN_MAIL_ICON(mn_mail_icon_new()); 362 mn_add_weak_pointer(&self->icon); 363 364 g_object_connect(self->icon, 365 "signal::activate", self_icon_activate_h, self, 366 "signal::activate-mail-reader", self_icon_activate_mail_reader_h, self, 367 "signal::activate-open-latest-message", self_icon_activate_open_latest_message_h, self, 368 "swapped-signal::activate-consider-new-mail-as-read", self_consider_new_mail_as_read, self, 369 "swapped-signal::activate-update", self_update, self, 370 "signal::activate-properties", self_icon_activate_properties_h, self, 371 "signal::activate-help", self_icon_activate_help_h, self, 372 "signal::activate-about", self_icon_activate_about_h, self, 373 "swapped-signal::activate-remove", self_quit, self, 374 "signal::destroy", self_icon_destroy_h, self, 375 NULL); 376 377 self_update_sensitivity(self); 378 self_update_tooltip(self); 379 self_update_icon(self); 380 } 381 382 private void 383 notify_icon_cb (GConfClient *client, 384 unsigned int cnxn_id, 385 GConfEntry *entry, 386 gpointer user_data) 387 { 388 Self *self = user_data; 389 390 self_update_icon(self); 391 } 392 393 private void 394 notify_tooltip_cb (GConfClient *client, 395 unsigned int cnxn_id, 396 GConfEntry *entry, 397 gpointer user_data) 398 { 399 Self *self = user_data; 400 401 self_update_tooltip(self); 402 } 403 404 private void 405 notify_mail_reader_cb (GConfClient *client, 406 unsigned int cnxn_id, 407 GConfEntry *entry, 408 gpointer user_data) 409 { 410 Self *self = user_data; 411 412 self_update_sensitivity(self); 413 } 414 415 private void 416 icon_activate_h (MNMailIcon *icon, gpointer user_data) 417 { 418 MNShell *self = user_data; 419 MNAction action; 420 421 action = mn_conf_get_enum_value(MN_TYPE_ACTION, MN_CONF_CLICK_ACTION); 422 423 switch (action) 424 { 425 case MN_ACTION_LAUNCH_MAIL_READER: 426 if (mn_conf_has_command(MN_CONF_GNOME_MAIL_READER_NAMESPACE)) 427 mn_conf_execute_mail_reader(); 428 else 429 mn_show_error_dialog_with_markup(NULL, 430 _("No mail reader is configured"), 431 _("You can configure a mail reader by choosing <b>System → Preferences → Preferred Applications</b>.")); 432 break; 433 434 case MN_ACTION_OPEN_LATEST_MESSAGE: 435 if (self->mailboxes->messages->len != 0) 436 { 437 MNMessage *message = g_ptr_array_index(self->mailboxes->messages, 0); 438 439 if (mn_message_can_perform_action(message, mn_message_get_action("open"))) 440 self_open_latest_message(self); 441 else 442 mn_show_error_dialog(NULL, 443 _("Unable to open the latest message"), 444 _("Messages of mailbox \"%s\" cannot be opened."), 445 message->mailbox->runtime_name); 446 } 447 else 448 mn_show_error_dialog(NULL, 449 _("Unable to open the latest message"), 450 _("You have no new mail.")); 451 break; 452 453 case MN_ACTION_CONSIDER_NEW_MAIL_AS_READ: 454 self_consider_new_mail_as_read(self); 455 break; 456 457 case MN_ACTION_UPDATE_MAIL_STATUS: 458 mn_mailboxes_check(self->mailboxes); 459 break; 460 461 default: 462 g_assert_not_reached(); 463 } 464 } 465 466 private void 467 icon_activate_mail_reader_h (MNMailIcon *icon, gpointer user_data) 468 { 469 mn_conf_execute_mail_reader(); 470 } 471 472 private void 473 icon_activate_open_latest_message_h (MNMailIcon *icon, gpointer user_data) 474 { 475 Self *self = user_data; 476 self_open_latest_message(self); 477 } 478 479 private void 480 icon_activate_properties_h (MNMailIcon *icon, gpointer user_data) 481 { 482 Self *self = user_data; 483 self_show_properties_dialog(self, gtk_get_current_event_time()); 484 } 485 486 private void 487 icon_activate_help_h (MNMailIcon *icon, gpointer user_data) 488 { 489 mn_show_help(NULL, NULL); 490 } 491 492 private void 493 icon_activate_about_h (MNMailIcon *icon, gpointer user_data) 494 { 495 Self *self = user_data; 496 self_show_about_dialog(self, gtk_get_current_event_time()); 497 } 498 499 private void 500 icon_destroy_h (GtkObject *object, gpointer user_data) 501 { 502 Self *self = user_data; 503 504 /* The Notification Area applet has been terminated. Recreate the icon. */ 505 mn_remove_weak_pointer(&self->icon); 506 self_init_icon(self); 507 } 508 509 private void 510 update_sensitivity (self) 511 { 512 gtk_widget_set_sensitive(self->icon->mail_reader_item, 513 mn_conf_has_command(MN_CONF_GNOME_MAIL_READER_NAMESPACE)); 514 gtk_widget_set_sensitive(self->icon->open_latest_message_item, 515 self->mailboxes->messages->len != 0 516 && mn_message_can_perform_action(g_ptr_array_index(self->mailboxes->messages, 0), 517 mn_message_get_action("open"))); 518 gtk_widget_set_sensitive(self->icon->consider_new_mail_as_read_item, 519 self->mailboxes->messages->len != 0); 520 gtk_widget_set_sensitive(self->icon->update_item, 521 mn_mailboxes_get_manually_checkable(self->mailboxes)); 522 } 523 524 private void 525 update_icon (self) 526 { 527 GList *l; 528 gboolean has_new = FALSE; 529 gboolean blink = FALSE; 530 gboolean always = mn_conf_get_bool(MN_CONF_ALWAYS_DISPLAY_ICON); 531 532 MN_LIST_FOREACH(l, self->mailboxes->list) 533 { 534 MNMailbox *mailbox = l->data; 535 536 if (g_hash_table_size(mailbox->messages) != 0) 537 has_new = TRUE; 538 if (mailbox->error) 539 blink = TRUE; 540 } 541 542 if (selfp->has_new && ! has_new && mn_conf_has_command(MN_CONF_COMMANDS_MAIL_READ_NAMESPACE)) 543 mn_conf_execute_command(MN_CONF_COMMANDS_MAIL_READ_COMMAND); 544 selfp->has_new = has_new; 545 546 if (blink && ! mn_conf_get_bool(MN_CONF_BLINK_ON_ERRORS)) 547 blink = FALSE; 548 549 if (has_new || blink || always) 550 { 551 int count; 552 553 if (mn_conf_get_bool(MN_CONF_DISPLAY_MESSAGE_COUNT)) 554 count = self->mailboxes->messages->len; 555 else 556 count = 0; 557 558 mn_mail_icon_set_from_stock(self->icon, has_new ? MN_STOCK_MAIL : MN_STOCK_NO_MAIL); 559 mn_mail_icon_set_blinking(self->icon, blink); 560 mn_mail_icon_set_count(self->icon, count); 561 gtk_widget_show(GTK_WIDGET(self->icon)); 562 } 563 else 564 { 565 gtk_widget_hide(GTK_WIDGET(self->icon)); 566 mn_mail_icon_set_blinking(self->icon, FALSE); 567 } 568 } 569 570 private void 571 update_tooltip (self) 572 { 573 GtkVBox *vbox = NULL; 574 575 if (self->mailboxes->list) 576 { 577 GList *la; 578 GSList *lb; 579 GSList *new_mailboxes = NULL; 580 GSList *error_mailboxes = NULL; 581 582 MN_LIST_FOREACH(la, self->mailboxes->list) 583 { 584 MNMailbox *mailbox = la->data; 585 586 if (g_hash_table_size(mailbox->messages) != 0) 587 new_mailboxes = g_slist_insert_sorted(new_mailboxes, mailbox, (GCompareFunc) self_new_mailboxes_compare_cb); 588 589 if (mailbox->error) 590 error_mailboxes = g_slist_insert_sorted(error_mailboxes, mailbox, mn_mailboxes_compare_by_name_func); 591 } 592 593 if (new_mailboxes) 594 { 595 GString *string = g_string_new(NULL); 596 597 MN_LIST_FOREACH(lb, new_mailboxes) 598 { 599 MNMailbox *mailbox = lb->data; 600 601 g_assert(g_hash_table_size(mailbox->messages) != 0); 602 603 if (*string->str) 604 g_string_append_c(string, '\n'); 605 606 g_string_append_printf(string, _("%s (%i)"), 607 mailbox->runtime_name, 608 (int) g_hash_table_size(mailbox->messages)); 609 } 610 g_slist_free(new_mailboxes); 611 612 /* translators: header capitalization */ 613 self_tooltip_text_section_new(&vbox, _("Mailboxes Having New Mail"), string->str); 614 g_string_free(string, TRUE); 615 } 616 617 if (error_mailboxes) 618 { 619 GString *string = g_string_new(NULL); 620 621 MN_LIST_FOREACH(lb, error_mailboxes) 622 { 623 MNMailbox *mailbox = lb->data; 624 625 if (*string->str) 626 g_string_append_c(string, '\n'); 627 g_string_append_printf(string, _("%s: %s"), mailbox->runtime_name, mailbox->error); 628 } 629 g_slist_free(error_mailboxes); 630 631 /* translators: header capitalization */ 632 self_tooltip_text_section_new(&vbox, _("Errors"), string->str); 633 g_string_free(string, TRUE); 634 } 635 } 636 637 if (self->mailboxes->messages->len != 0) 638 { 639 MNShellTooltipMailSummary mail_summary; 640 641 mail_summary = mn_conf_get_enum_value(MN_TYPE_SHELL_TOOLTIP_MAIL_SUMMARY, MN_CONF_TOOLTIP_MAIL_SUMMARY); 642 if (mail_summary != MN_SHELL_TOOLTIP_MAIL_SUMMARY_NONE) 643 { 644 GtkWidget *alignment; 645 GtkWidget *message_view; 646 int limit; 647 GSList *messages = NULL; 648 int num_messages; 649 int i; 650 651 /* translators: header capitalization */ 652 alignment = self_tooltip_section_new(&vbox, _("Mail Summary")); 653 654 switch (mail_summary) 655 { 656 case MN_SHELL_TOOLTIP_MAIL_SUMMARY_STANDARD: 657 message_view = mn_standard_message_view_new(); 658 break; 659 660 case MN_SHELL_TOOLTIP_MAIL_SUMMARY_COMPACT: 661 message_view = mn_compact_message_view_new(); 662 break; 663 664 default: 665 g_assert_not_reached(); 666 break; 667 } 668 669 gtk_widget_set_name(message_view, "mn-message-view"); 670 671 limit = mn_conf_get_int(MN_CONF_TOOLTIP_MAIL_SUMMARY_LIMIT); 672 673 num_messages = MIN(self->mailboxes->messages->len, limit); 674 for (i = num_messages - 1; i >= 0; i--) 675 messages = g_slist_prepend(messages, g_ptr_array_index(self->mailboxes->messages, i)); 676 mn_message_view_set_messages(MN_MESSAGE_VIEW(message_view), messages); 677 g_slist_free(messages); 678 679 if (self->mailboxes->messages->len <= limit) 680 gtk_container_add(GTK_CONTAINER(alignment), message_view); 681 else 682 { 683 int remaining; 684 char *markup; 685 GtkWidget *label; 686 GtkWidget *summary_vbox; 687 688 remaining = self->mailboxes->messages->len - limit; 689 690 label = gtk_label_new(NULL); 691 692 markup = g_strdup_printf(ngettext("<span style=\"italic\">%i message is not displayed</span>", 693 "<span style=\"italic\">%i messages are not displayed</span>", 694 remaining), 695 remaining); 696 gtk_label_set_markup(GTK_LABEL(label), markup); 697 g_free(markup); 698 699 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); 700 701 summary_vbox = gtk_vbox_new(FALSE, 12); 702 703 gtk_box_pack_start(GTK_BOX(summary_vbox), message_view, TRUE, TRUE, 0); 704 gtk_box_pack_start(GTK_BOX(summary_vbox), label, FALSE, FALSE, 0); 705 706 gtk_container_add(GTK_CONTAINER(alignment), summary_vbox); 707 } 708 } 709 } 710 711 if (vbox) 712 { 713 gtk_widget_show_all(GTK_WIDGET(vbox)); 714 mn_mail_icon_set_tip_widget(self->icon, GTK_WIDGET(vbox)); 715 } 716 else 717 mn_mail_icon_set_tip(self->icon, _("You have no new mail.")); 718 } 719 720 private int 721 new_mailboxes_compare_cb (MNMailbox *a, MNMailbox *b) 722 { 723 int cmp; 724 725 /* sort by timestamp (descending order) */ 726 cmp = b->timestamp - a->timestamp; 727 if (cmp != 0) 728 return cmp; 729 730 /* sort by number of messages (descending order) */ 731 cmp = (int) g_hash_table_size(b->messages) - g_hash_table_size(a->messages); 732 if (cmp != 0) 733 return cmp; 734 735 /* sort by name (ascending order) */ 736 return mn_mailboxes_compare_by_name_func(a, b); 737 } 738 739 private GtkWidget * 740 tooltip_section_new (GtkVBox **vbox (check null), 741 const char *title (check null)) 742 { 743 GtkWidget *section; 744 GtkWidget *label; 745 GtkWidget *alignment; 746 747 if (! *vbox) 748 *vbox = GTK_VBOX(gtk_vbox_new(FALSE, 18)); 749 750 section = mn_hig_section_new(title, &label, &alignment); 751 752 gtk_widget_set_name(label, "mn-tooltip-section-title"); 753 754 gtk_box_pack_start(GTK_BOX(*vbox), section, TRUE, TRUE, 0); 755 756 return alignment; 757 } 758 759 private void 760 tooltip_text_section_new (GtkVBox **vbox (check null), 761 const char *title (check null), 762 const char *text (check null)) 763 { 764 GtkWidget *alignment; 765 GtkWidget *label; 766 767 alignment = self_tooltip_section_new(vbox, title); 768 769 label = gtk_label_new(text); 770 gtk_widget_set_name(label, "mn-tooltip-section-body"); 771 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); 772 773 gtk_container_add(GTK_CONTAINER(alignment), label); 774 } 775 776 private void 777 open_latest_message (self) 778 { 779 MNMessage *message; 780 MNMessageAction *action; 781 782 g_return_if_fail(self->mailboxes->messages->len != 0); 783 784 message = g_ptr_array_index(self->mailboxes->messages, 0); 785 786 action = mn_message_get_action("open"); 787 g_return_if_fail(action != NULL); 788 g_return_if_fail(mn_message_can_perform_action(message, action)); 789 790 mn_message_perform_action(message, action, self_open_latest_message_done_cb, NULL); 791 } 792 793 private void 794 open_latest_message_done_cb (MNMessageAction *action, GError *err, gpointer data) 795 { 796 if (err && ! g_error_matches(err, MN_MESSAGE_ACTION_ERROR, MN_MESSAGE_ACTION_ERROR_CANCELLED)) 797 mn_show_error_dialog(NULL, _("Unable to open the latest message"), "%s", err->message); 798 } 799 800 public MNShell * 801 new (DBusGConnection *session_bus (check null), 802 DBus:G:Proxy *session_bus_proxy (check null)) 803 { 804 return GET_NEW_VARG(MN_SHELL_PROP_SESSION_BUS(session_bus), 805 MN_SHELL_PROP_SESSION_BUS_PROXY(session_bus_proxy), 806 NULL); 807 } 808 809 public void 810 consider_new_mail_as_read (self) 811 { 812 GList *l; 813 814 MN_LIST_FOREACH(l, self->mailboxes->list) 815 { 816 MNMailbox *mailbox = l->data; 817 GList *list; 818 819 list = g_hash_table_get_values(mailbox->messages); 820 mn_message_consider_as_read_list(list); 821 g_list_free(list); 822 } 823 } 824 825 public void 826 update (self) 827 { 828 mn_mailboxes_check(self->mailboxes); 829 } 830 831 public void 832 quit (self) 833 { 834 g_object_unref(self); 835 } 836 837 public void 838 show_properties_dialog (self, guint32 timestamp) 839 { 840 self_show_window(self, MN_TYPE_PROPERTIES_DIALOG, &selfp->properties_dialog, timestamp); 841 } 842 843 public void 844 show_about_dialog (self, guint32 timestamp) 845 { 846 self_show_window(self, MN_TYPE_ABOUT_DIALOG, &selfp->about_dialog, timestamp); 847 } 848 849 private void 850 show_window (self, 851 GType type (check != 0), 852 GtkWidget **ptr (check null), 853 guint32 timestamp) 854 { 855 if (*ptr) 856 { 857 if (timestamp) 858 gtk_window_present_with_time(GTK_WINDOW(*ptr), timestamp); 859 else 860 gtk_window_present(GTK_WINDOW(*ptr)); 861 return; 862 } 863 864 *ptr = g_object_new(type, NULL); 865 mn_add_weak_pointer(ptr); 866 867 gtk_widget_show(*ptr); 868 } 869 870 public void 871 add_mailbox_properties_dialog (self, MN:Mailbox:Properties:Dialog *dialog (check null type)) 872 { 873 selfp->mailbox_properties_dialogs = g_slist_append(selfp->mailbox_properties_dialogs, dialog); 874 g_object_weak_ref(G_OBJECT(dialog), self_mailbox_properties_dialog_weak_notify_cb, self); 875 } 876 877 private void 878 mailbox_properties_dialog_weak_notify_cb (gpointer data, 879 GObject *former_object) 880 { 881 Self *self = data; 882 883 selfp->mailbox_properties_dialogs = g_slist_remove(selfp->mailbox_properties_dialogs, former_object); 884 } 885 886 public MNMailboxPropertiesDialog * 887 get_mailbox_properties_dialog (self, MN:Mailbox *mailbox (check null type)) 888 { 889 GSList *l; 890 891 MN_LIST_FOREACH(l, selfp->mailbox_properties_dialogs) 892 { 893 MNMailboxPropertiesDialog *dialog = l->data; 894 MNMailbox *this_mailbox; 895 gboolean found; 896 897 this_mailbox = mn_mailbox_properties_dialog_get_mailbox(dialog); 898 found = this_mailbox == mailbox; 899 g_object_unref(this_mailbox); 900 901 if (found) 902 return dialog; 903 } 904 905 return NULL; 906 } 907 908 public char * 909 get_summary (self) 910 { 911 int indent; 912 xmlDoc *doc; 913 xmlNode *root; 914 int i; 915 xmlChar *summary; 916 917 indent = xmlIndentTreeOutput; 918 xmlIndentTreeOutput = 1; 919 920 doc = xmlNewDoc("1.0"); 921 root = xmlNewNode(NULL, "messages"); 922 xmlDocSetRootElement(doc, root); 923 924 MN_ARRAY_FOREACH(i, self->mailboxes->messages) 925 { 926 MNMessage *message = g_ptr_array_index(self->mailboxes->messages, i); 927 xmlNode *node; 928 929 node = mn_message_xml_node_new(message); 930 xmlAddChild(root, node); /* owns node */ 931 } 932 933 xmlDocDumpFormatMemory(doc, &summary, NULL, 1); 934 935 xmlFreeDoc(doc); 936 xmlIndentTreeOutput = indent; 937 938 return summary; 939 } 940 } 941 942 %h{ 943 extern MNShell *mn_shell; 944 %} 945 946 /* 947 * These enumerations really belong to mn-enums.gob, but gob does not 948 * allow a class-less input file. 949 */ 950 951 enum MN_ACTION 952 { 953 LAUNCH_MAIL_READER, 954 OPEN_LATEST_MESSAGE, 955 CONSIDER_NEW_MAIL_AS_READ, 956 UPDATE_MAIL_STATUS 957 } MN:Action; 958 959 enum MN_EXPIRATION_ENABLED 960 { 961 DEFAULT, 962 FALSE, 963 TRUE 964 } MN:Expiration:Enabled; 965 966 enum MN_POPUP_POSITION 967 { 968 ATTACHED, 969 FREE 970 } MN:Popup:Position;